Packet Filtering with Linux 2.4′s iptables

The Linux 2.4 kernel is just around the corner and, in theory, is supposed to be coming to a computer near you around the time you read this article. So in the interest of shamelessly tapping into the 2.4 hype and excitement, this month's column is about the extensions to packet filtering you will have at your fingertips when you finally get your hands on the Linux 2.4 kernel. (See pg. 30 for the complete story on Linux 2.4. -Ed.)

best defense figure
Figure One: The iptables Packet-filtering System.

The Linux 2.4 kernel is just around the corner and, in theory, is supposed to be coming to a computer near you around the time you read this article. So in the interest of shamelessly tapping into the 2.4 hype and excitement, this month’s column is about the extensions to packet filtering you will have at your fingertips when you finally get your hands on the Linux 2.4 kernel. (See pg. 30 for the complete story on Linux 2.4. -Ed.)

In my October 1999 column, I wrote about the netfilter architecture that was introduced in the 2.3 kernels to separate out packet filtering, redirection, port forwarding, and masquerading from the core of the networking code.

netfilter is an organized infrastructure inside the kernel for writing extensions to these kinds of services. The idea with netfilter is to create a modular architecture that can be easily extended. New features can be added without an annoying reboot. You simply add a new kernel module.

A variety of modules have been built on top of the netfilter frame-work — masquerading and Net-work Address Translation (NAT), state-tracking, and packet filtering. And there are netfiltercompatibility modules for both Linux 2.2 (ipchains) and 2.0 (ipfwadm). These methods of packet filtering will be supported for some time, but if you’re setting up a new configuration, you should use the new packet-filtering code: iptables.

iptables is the third (and most powerful) revision of the Linux packet-filtering system. When it is used in conjunction with various extension modules, it provides state-of-the-art packet-filtering capabilities.

Figure One shows how packets pass through the iptables filtering system. When a network packet enters the box,the computer consults the routing code to decide whether the packet stops there or is simply trying to pass through. The list of rules (called a chain of rules) named FORWARD is consulted if the packet is passing through. Otherwise the INPUT rules are consulted.

This arrangement is different from previous kernels. In kernels 2.0 and 2.2, the ipfwadm and ipchains filtering occurred at three points: input, forward and output. The input point was as soon as the packet came off the wire (i.e. before routing), and the output point was just before the packet left. This meant that packets hit the input filtering point whether they were destined for the box itself or merely passing through. Similarly, all outgoing packets, whether merely passing through or locally generated, passed the output filtering point.

With iptables, each rule has two parts: a part that tells the rule how to match the packet, and a part that says what to do with the packet if it matches. The most important new feature in iptables is that both parts of the rule can be extended.

Rule extensions are implemented as kernel modules, which are automatically loaded the first time iptables refers to them. The extensions/modules themselves are implemented as shared libraries which iptables expects to find in the /usr/local/lib/iptables directory.

In order to extend rules, each module provides additional command line options that can only be used after the module is loaded. Extensions/ modules are loaded or referred to in one of two ways. The first is by typing -j<module> <options> to jump to a “target.” A target tells iptables what to do with a packet that has been matched.

Alternatively, modules are loaded or referred to in order to “teach” rules what type of packets they should “match” by typing
-m<module> <options>. Typing
-hor -helpafter the <module> will output a help message about that extension/module.

Packet Logging:
The LOG Target

The old packet-filtering code had a
-l option to have the kernel log the details of a packet. The new code has a LOG module, called with the -jLOG option. You can specify a level of logging (debug, info, notice,warning, error, crit, alert, and panic). The meaning of these terms is defined in the syslog. conf(5) man page. You can also supply a prefix for each log line. This can be extremely useful if you have several logging rules for different things. For example:

# iptables -A INPUT -s -j LOG -log-prefix Test:

This says to append a rule to the INPUT chain (-A INPUT) that matches packets coming from (-s and jumps to the LOG target (-j LOG) with a log line that begins with Test:. This logging is quite verbose and complete. An example might look like:

Test:IN=ppp0 OUT=
LEN=100 TOS=0×00 PREC=0×00
TYPE=8 CODE=0 ID=56 SEQ=58

Packet Limiting: The limit Match

Unfortunately, if you are logging public packets, it’s fairly easy for someone to flood your logs. Hence, you’ll want to limit the rate at which the packets are logged.

The limit match extension matches packets up until a certain rate and is usually used with the LOG target above. When that rate is reached, packets stop matching. A user sets the rate (in packets per second, minute, hour, or day) and a burst, which indicates how many packets will be matched before the rate limitation cuts in:

# iptables -A INPUT -s -m limit -rate 1/hour -j LOG

This rule will match a maximum of once per hour. The default burst of 5 means that the first five packets will always match. Using -limit-burst1 would cut this number to one.

Suspicious Packets: The unclean Match

The Internet is promiscuous. It has a knack for delivering malformed packets when it shouldn’t. The unclean extension is an attempt to recognize a number of packets that are unusual or completely malformed. This extension logs the problem with the packet, and the logging is internally limited to avoid flooding your logs:

# iptables -A FORWARD -i ppp0 -m unclean -j DROP

This rule, which only applies to packets being forwarded to other machines, indicates that packets coming in the ppp0 interface (-I ppp0) that match the unclean module should be dropped (-j DROP).

The unclean module tests many packet properties, including:

* Packets that are too short to have a full ICMP/UDP/TCP header

* TCP and UDP packets with zero (illegal) source and destination ports

* Illegal combinations of TCP flags

* Zero-length (illegal) or over-length TCP and IP options, or options after the END-OF-OPTIONS option

* Fragments of illegal length or offset (e.g., Ping of Death).

Matching Ethernet Addresses:
The MAC Match

Sometimes it’s useful to check the Ethernet card your packet came from. This is the purpose of the mac module. Every Ethernet card is supposed to have a unique 48-bit MAC (Media Access Control) address, sometimes called a hardware address. My laptop’s card, for example, has the address 00:60:08:91:CC:B7. While some cards have the ability to change this address, many are immutably stamped into the card.

So by matching a card’s address, you can verify that a packet came from a certain machine on your local network. You can’t tell whether it originated the packet or was passing it through for someone else, but you can tell who gave it to you. This can be used for extra assurance that the packet came from a certain router.

mac will match only in the INPUT and FORWARD chains. It is meaningless in the OUTPUT chain.

# iptables -A FORWARD  -m mac -mac-source 00:60:08:91:CC:B7 -j LOG

This appends a rule to the FORWARD chain, which logs packets that came from my laptop’s Ethernet card and that are passing through this box.

Keeping Track of Packets:
The state Match

The most sophisticated match extension is the state match. This requires the ip_conntrack module, which tracks connections. The ip_conntrack module is also used by the NAT module to do Network Address Translation, but packet filtering can also use it with the state extension.

ip_conntrack sorts out packets on the basis of which packets it has seen before. Whenever a packet enters the box from the network or is generated by the box itself, ip_conntrack looks at its table of connections and sorts the packet into one of the following four categories:

NEW: This packet is trying to establish a new connection that hasn’t been seen before. This also counts retransmission of the first packet. A connection is regarded as NEW until a reply packet is seen.

ESTABLISHED: This packet is part of an established connection. It could be a first reply or any other traffic.

RELATED: This packet is related to an existing connection, but not part of it. It could be an ICMP error reporting a problem with the connection, or something special like an FTP data channel being established.

INVALID: This packet could not be classified. Sometimes this can be caused by an out-of-memory or other error, but usually it means something is wrong with the packet, such as an ICMP error that reports problems with an unknown connection.

Generally, you want to allow all established and related packets from the outside, not allow any INVALID packets, and examine NEW packets. These commands can look like the following:

# iptables -A FORWARD -m state -state ESTABLISHED, RELATED -j ACCEPT

# iptables -A FORWARD -m state -state INVALID -j DROP

# iptables -A FORWARD -i ppp0 -j DROP

The first rule says to let through (-j ACCEPT) any packets that are passing through (-AFORWARD) associated with an existing connection (-state ESTABLISHED,RELATED).The second says to drop any INVALID packets, and the final rule, which only packets trying to set up
a NEW connection will reach, says to drop any packets coming in ppp0
(-i ppp0).

These rules mean that no one from your modem (ppp0) can start a new connection — the classic “don’t speak unless spoken to” firewall.

Everything Together

If you take the above rules as a basis, you can enhance them to form a fairly secure packet filter. Without any filtering on the INPUT chain, you are restricting only what packets pass through your firewall, not what occurs on the box itself.

First, you need to set up the modules. The ip_conntrack_ftp module can track FTP
connections. FTP sometimes uses a second connection to handle data traffic, and these are marked RELATED by this module. The ipt_modules should be loaded automatically. I inserted them here for completeness:

 # insmod ip_conntrack.o
# insmod ip_conntrack_ftp.o
# insmod iptables.o
# insmod ipt_state.o
# insmod ipt_unclean.o
# insmod ipt_limit.o
# insmod ipt_LOG.o

The first thing you want to do is drop any suspicious packets:

 # iptables -A FORWARD -m unclean -j DROP

Now you create two new chains called INVALID-DROP and NEW-DROP. These chains are simply a group of rules that log the packet (with limiting — i.e., not logging above a certain rate — to stop a log-flooding attack) and then drop it, but each prints a slightly different message in the logs:

 # iptables -N INVALID-DROP
# iptables -A INVALID-DROP -m limit -rate 5/hour -rate-burst 3 -j LOG -log-prefix “FW:Invalid:”

# iptables -A INVALID-DROP -j DROP
# iptables -N NEW-DROP
# iptables -A NEW-DROP -m limit -rate 5/hour -rate-burst 3 -j LOG -log-prefix “FW:New:”
# iptables -A NEW-DROP

Now, you just need to sew these into the state example given above:

 # iptables -A FORWARD -m state -state ESTABLISHED, RELATED -j ACCEPT

# iptables -A FORWARD -m state -state INVALID -j INVALID-DROP

# iptables -A FORWARD -i ppp0 -j NEW-DROP

And voila: your firewall is configured. The default policy for the FORWARD chain is to DROP all the packets unless overridden when the module is inserted. Further refinements are possible and recommended: You should give additional consideration to locking down the box itself, which is unprotected by these rules.

More Reading

Netfilter Homepages:




Paul “Rusty” Russell of WatchGuard maintains the Linux packet filter code. He can be reached at paul.russell@ rustcorp.com.au.

Comments are closed.