A quick run down on iptables rules

What is it?

iptables is a cli frontend to netfilter, the Linux kernel firewall/nat implementation for OSI layer 3 and 4.

Okay, give me more details

  • When you run “iptables”, the actual file being executed is “xtables-multi”. That is, because the file name “iptables” is symlinked to “xtables-multi”.
  • What is xtables-multi? A binary that contains code for a couple of utilities, like ip6tables, ip(6)tables-restore, ip(6)tables-save
Basic rules
  1. The rules in a chain are applied top to bottom.
  2. The *nat table is only called for connections with ctstate NEW
  3. There are terminating and non-terminating targets in iptables. If a packet hits a terminating target. the rule examination for that packet ends there.
  4. NAT relies on conntrack. Untracked packets can not be natted.
  5. The order of calling for the different chains and tables is defined here (png link)
  6. iptables -L lies. Use iptables-save instead.
Rule structure

iptables rules actually only contain anything after the binary name and the table name. This is how they are referred to in this document.
That is, because it corresponds to how the kernel actually stores them and how “iptables-save” prints them.

An iptables rule is structured like this:

<OPERATION> <CHAIN NAME> [builtin matches [builtin matches parameters]] [<-m> <match module> [<match module parameters> ]] [<-j> <TARGET> [<TARGET PARAMETERS>]]
  • <> denote a mandatory parameter
  • [] denote optional parameters
  • You can structure differently, but I strongly suggest you structure them as described above to make it readable and understandable.
  • Anything between <CHAIN NAME> and the “-j” is a selector which restricts which packets are handled by the target. All of its parts have to evaluate to TRUE for the packet to be handled by the target.
  • You can invert certain matches by prepending an exclamation mark (!) to the match. The corresponding section in the man page for iptables-extensions explicitely mention what you can invert.
Rule examples
Rules that would fullfill the structure requirements could look like this:
  1. (in filter table) -A INPUT -i lo -j ACCEPT
    Other common rules that fullfill this criteria:
  2. (in filter table) -A INPUT -m conntrack –ctstate RELATED,ESTABLISHED -j ACCEPT
  3. (in nat table) -A PREROUTING -i eth0 -d -p tcp –dport 443 -j DNAT –to-destination
  4. (in filter table) -A FORWARD -i eth0 -m policy –pol none –dir in -j DROP
Explanation of rule …
  1. In the INPUT chain, ACCEPT anything where the input interface is “lo”.
  2. In the INPUT chain, ACCEPT anything where the match module “conntrack” with the parameters “–ctstate” and “RELATED,ESTABLISHED” evaluate to “true”
  3. In the PREROUTING chain, DNAT anything to the IP, where the input interface is “eth0”, the destination is “”, the protocol is “tcp” and the destination port is “443”.
  4. In the FORWARD chain, DROP anything where the input interface is “eth0” and the “policy” match module with the parameters “–pol”, “none” “–dir” and “in”.
Interpretation of rule …
  1. Accept anything from the loopback adapter
  2. Accept any packet that is a reply to a connection, for which conntrack has already seen a reply (basically a connection which conntrack considers established, e.g. a TCP SYN+ACK, when the previous packet was a TCP SYN) or any packet that relates to an already established connection. The “established” in the latter does not refer to the mentioning of the ESTABLISHED parameter before!
  3. DNAT any packet to that is received on eth0, is destined to and is a tcp packet to port 443.
  4. First a little bit of background on the “policy” match module. It matches if the parameter to it are true for this packet. If you’re using “-m policy –pol ipsec –dir in”, that means that it will match if there is a matching IPsec policy for the INBOUND direction. That would be true if a corresponding IPsec policy exists. Usually IPsec policies have the “required” parameter set. That means that unprotected packets that match the corresponding policy are dropped.
    You can use the “-pol” parameter to specify if you want this match to be true if there is a policy (–pol ipsec) or if there is no policy (–pol none). Hence this rule drops all packets that arrive on the eth0 interface for which there is no (–pol none) IPsec policy for the INBOUND direction.
What types of packets can I filter using iptables?

“iptables” itself is for IPv4 traffic. There is the equivalent “ip6tables”, which is for IPv6 traffic. So you can filter IPv4 and IPv6 packets.

How do I load my rules the best way?

Write them in “iptables-save” format and then load them using “iptables-restore”.
Why? See here (Mirror)

Where’s the documentation?

There are man pages for “iptables” and “iptables-extensions”. The order of chains and tables is explained [here](http://
.de/images/nf-packet-flow.svg) (png link) and upstream kernel description of sysctl ip options is here

Can I use aliases for interface filters?

Nope. Aliases aren’t real interfaces and won’t appear as input devices.

What about bridging?

The behaviour of bridging depends on the exact settings. Sometimes, the input and output interfaces that you expect aren’t what the kernel actually thinks it is. Check the “net.bridge.bridge-nf-pass-vlan-input-dev” sysctl setting.

Invalid TCP flag filtering?

Conntrack does that for you already. It does not filter explicitely, but such packets are considered “invalid”. “-m conntrack –ctstate INVALID” matches on such packets.

Do I need to filter packets from wrong sources, like packets where the source IP is a private IP, but I get them on the interface towards the Internet?

That depends on the family and if you use policy based routing or not. For IPv4, the kernel uses the main routing table to figure out if a packet is a martian (comes from an unexpected direction) or not. If you use policy based routing, that obviously does not work anymore. In that case, you need to disable the rp_filter (reverse path filter) or set it to 2 for the interfaces that take part in the policy based routing. rp_filter = 2 sets it to accept packets with any destination the host has a route to.

The best approach would be to apply the rpfilter using custom iptables rules that match as your requirements dictate.
If you do not use policy based routing, the kernel drops martians (packets coming from an unexpected direction) by default already.

For IPv6, you unconditionally need to, because there is no builtin rp_filter for it. There is a match module that implements the rp_filter for ipv6: rpfilter (it also implements it for IPv4).

You can use the “rpfilter” match module in iptables to figure out if a packet comes from an unexpected direction. See the man page for iptables-extensions for details.

Where do I find good rule sets to start with?

Take a look here

How do I log packets?

You can use the LOG target for that. You can send packets to userspace using the NFLOG target and an application listening on the NFLOG group, for example tcpdump, dumpcap or wireshark. You can combine that with the remote dumping feature of wireshark to remotely capture packets using nflog from a remote host.

How do I debug my rule set?

You can either use strategically placed rules with the LOG target to get a log line for every packet matching the selector or use the TRACE target in the *raw table to get a line in the kernel log for every chain policy or rule that matched the packet.

I don’t get any packets printed in dmesg/kern.log\/journalctl when the TRACE\/LOG target is hit. What’s wrong?

Load the “nf_log_ipv4” and “nf_log_ipv6” kernel modules. They provide the logging capability for the respective IP protocol versions. After that, you should see the packets in the logs.

If that does not help yet, set the “net.netfilter.nf_log.2” sysctl setting to “nf_log_ipv4” and “net.netfilter.nf_log.10” to “nf_log_ipv6”.

What IS conntrack?

Conntrack is first and foremost a subsystem of the netfilter system of the kernel which tracks all sorts of packets and interprets them as connections, to which packets are correlated against. It gives you the essential component to build stateful firewall rules.
It is also the name of a match module, which uses the kernel subsystem to figure out the state of the connection the packet belongs to, and a front end tool for viewing and manipulating the conntrack table.

Can I do if-else statements?

Yes, use custom chains for that and call them like you would call a target. You return from custom chains by using the RETURN target or by reaching the end of the custom chain.

Why should I restrict the MASQUERADE and SNAT targets to an interface?

Because otherwise, they will MASQUERADE and SNAT traffic over the loopback adapter with the source IP the main routing table uses for the best fitting route (probably the default route in that case). That breaks any service on localhost that checks the source IP, like email spam filters or other software.