To present different tools and functionalities present on Linux systems to implement firewall and routers. Specific usage of each tool will not be covered by this article and it is up to the reader to learn more about them by reading the manuals.
It is recommended that the reader follow the article together with each tool´s manual in hand for reference.
IP Tables behavior is based on pairs of rules and actions. Rules define in which packets (eg. packets from a certain network) a certain action (eg. to drop the packets) will be executed. Netfilter will process all rules sequentially for each package. When it finds one that matches a certain rule, it will act by calling the defined action for that rule. Actions taken can be either terminative or not. For example, an action which asks netfilter to ignore a certain packet will be called and no other rules will be applied. This is a terminative action. However, if the action specifies to only log the existence of the package, it will do its job and let netfilter process the following rules. This is a non terminative action.
The main tool is the command
iptables, which manual can be accessed via:
# man iptables
The next sections will explain how the explained behavior works inside iptables and how to configure it.
iptables comes from the fact that internally iptables works based on tables, each one specialized on a certain type of packets treatment. These are the tables existent on a Linux 2.6.8 (different kernel versions can have different tables):
| ||Low level alteration of packets.|
| ||Changes on packets headers (where NAT takes place).|
| ||Used to make specialized packet alterations.|
| ||Packet filtering.|
Thus, depending on what you wish to do with a certain packet, there is an adequate table for that.
In IP Tables, there are several chains glued to each table and each one associated a certain kind of traffic. Here they are:
| ||Incoming traffic to the machine (including local generated traffic destined to the machine itself) before routing takes place (can be either local or passing traffic).|
| ||Traffic destined to the machine itself.|
| ||Matches passing traffic (remote generated, remote destination).|
| ||Locally generated traffic (either local or remote destination).|
| ||Outgoing traffic (including locally generating traffic, destined to the machine itself).|
It is also possible to create personalized chains, topic that will not be covered on this article.
FORWARD chain has a special treatment inside Linux kernel. It comes with a lock that blocks traffic through this chain by default. To allow such traffic, it is needed to change the following kernel parameter:
# sysctl -w net.ipv4.ip_forward=1
This can be automated by adding the following line to
/etc/sysctl.conf) and should be set up at boot time:
Note: Debian versions previous (and including) Sarge have a file named
/etc/network/options that can also be used to set up this parameter. This is wrong and is not present any more in future releases.
The data flow inside each table / chain inside the Linux kernel is described by the graph below. In each box, it is labeled the acting chain and the names of the valid tables for each chain. Traffic flows through each one of the tables in series for each chain. For example, at
PREROUTING chain, there are the tables
nat. Traffic flowing through this chain will pass by each one of the three tables sequentially.
Incoming Traffic | | V +----------+ |PREROUTING| +----------+ | raw | <--------------+ | mangle | | | nat | | +----------+ | | | | | Routing | +- Decision -+ | | | | | | | V V | Local Remote | Destination Destination | | | | | | | V V | +--------+ +---------+ | | INPUT | | FORWARD | | +--------+ +---------+ | | mangle | | mangle | | | filter | | filter | | +--------+ +---------+ | | | | | | | V | | Local | | Machine | | | | | | | | V | | Routing | | Decision | | | | | | | | V | | +--------+ | | | OUTPUT | | | +--------+ | | | raw | | | | mangle | | | | nat | | | | filter | | | +--------+ | | | | | | +-------------+ | | | POSTROUTING | Local +----> +-------------+ --> Traffic | mangle | | nat | +-------------+ | | V Outgoing Traffic
Let´s take as an example an access from the machine to itself. Traffic will flow sequentially by each one of the following chains / tables:
- Packet generated by a local process / kernel
- Routing decision
- Routing decision
- Packet delivered to a local process / kernel
Thus, if one wish to allow such traffic, there must have no rule / action pair prohibiting the traffic in any of the above cases.
Given a certain chain / table, it is necessary to use rules to select in which packets a certain action will take place. Not all rules applies to all chains. For example, a rule specifying the outgoing interface of a packet does not apply to the chain
PREROUTING, since routing decision has not been taken at that point.
There are general rules (referred as
PARAMETERS on the manual
iptables(8)) and extensions (see
MATCH EXTENSIONS on the same manual). General rules are usually the same across different kernel versions. Match extensions depends on the running kernel and IP Tables version (remember that IP Tables is only a userland tool to interface with netfilter). It is possible to have the case where a certain rule exists in IP Tables but it's corresponding implementation is missing in the running kernel. In this case, trying to use such rule will fail.
General rules are:
| ||Specify an IP protocol (eg. TCP or UDP).|
| ||Specify a source address.|
| ||Specify a destination address.|
| ||Specify a local interface of incoming traffic.|
| ||Specify a local interface of outgoing traffic.|
Match extensions will be treated to follow.
A target specify the action to be taken when a certain packet match a certain rule. This target can be one of the default (should be available on all kernels) or one target extension (refer to
TARGET EXTENSIONS at
The default targets are:
| ||Allow the packet through.|
| ||Instruct netfilter to ignore the packet.|
| ||Pass the packet to userspace.|
| ||From the manual: "stop traversing this chain and resume at the next rule in the previous (calling) chain. If the end of a built-in chain is reached or a rule in a built-in chain with target RETURN is matched, the target specified by the chain policy determines the fate of the packet."|
In practice, most times you will be using either
iptables(8), there are many ways to add / remove rules. All add rule actions, are discrete operation. This suggests the creation of a shell script containing all
iptables(8) calls that compose the whole firewall. Remember to clean all pre-existing rules at the beginning of the script.
To add a single rule (at the end of the existing ones), follow this general form:
# iptables -t [TABLE] -A [CHAIN] [RULES] -j [TARGET]
| ||Specify the table.|
| ||Specify the chain.|
| ||Packet selection rules.|
| ||Action to take place.|
For example, to allow the network 192.168.0.0/24 connected through the network interface named eth0 to send traffic to the network 192.168.1.0/24, connected to the network interface eth1, one can write the following rule:
# iptables -t filter -A FORWARD -s 192.168.0.0/24 -d 192.168.1.0/24 -i eth0 -o eth1 -j ACCEPT
Follow the data flow on the above data flow graph to note that this rule would not be enough, since the packet flows through many other table / chains.
In the case that no rule matches a certain traffic, there is a way to set up a default policy to a certain table / chain. This default policy configure a target to be executed in the case no other terminative rule on the same target / table match the traffic. To define a default policy try:
# iptables -t [TABLE] -P [CHAIN] [TARGET]
# iptables -t raw -P OUTPUT ACCEPT
This rule will allow free traffic by default on the table raw, chain OUTPUT
Up to here, resources to control traffic are limited. The neTABELAxt topic will cover some extensions that give much more control / power over the firewall.
As said previously, there are match extensions that allow to better select packets. The general form is:
... -m [NAME] [OPTIONS]
| ||Name of the match extension in use.|
| ||Match extension options.|
Please reffer to the topic
MATCH EXTENSIONS of
iptables(8) to get more information on available match extensions and its options.
Here are presented some common match extensions, that will be used most of the times.
Allow to specify a certain source / destination TCP port:
... -m tcp --dport www
This will match TCP connections which destination port is www (number 80, please reffer to
Allow to specify a certain source / destination UDP port:
... -m udp --dport www
IP Tables is a so called stateful firewall. This means it is possible to match all packets related to a certain connection, having no need to make a rule to allow packets going and another to allow packets coming from the same connection. This is direct for TCP connections, but there are extensions to allow UDP traffic windows, thus defining an "UDP connection". Example:
# iptables -t filter -A FORWARD -d [WEB_SERVER] -i [EXTERNAL_INTERFACE] -o [INTERNAL_INTERFACE] -j ACCEPT # iptables -t filter -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
The first rule allows external traffic coming via
EXTERNAL_INTERFACE to reach
INTERNAL_INTERFACE. The second rule says that any forwarding traffic related to an existing connection, will be allowed (eg. traffic coming from
EXTERNAL_INTERFACE). The match extension
state tracks the connections, and matches the respective traffic. Note that in this case, all forwarding traffic related to a pre-existing connection will be allowed, not only the traffic from the first rule.
The connection tracking system from netfilter has many different modules, being able to track specific protocols, such as FTP, to allow related (data) connections to work with no additional rule or the need of a proxy server. In the case where no connection is defined (eg. UDP or ICMP) after the traffic goes, a window is opened to allow its return. Thus, everything works like if there was actually an "UDP connection".
It is recommended to let a rule similar to the above second one. It is better to have a generic rule "catch all" than writing an specific connection rule for each traffic. Since this rule will not allow traffic if it was not allowed before, it is not security problem to do this. It is also recommended to allow ICMP traffic to flow freely.
Please refer to iptables manual to find out more about connection tracking and more uses of this tool.
Target extensions allow some specialized actions and packet treatment to be done, such as NAT or load balancing. Please refer to iptables manual to know more about available target extensions. Here are some important ones:
This one allow to log (via kernel logging, you can see using the command
dmesg) any message you wish when a certain packet matches. A good rule is to include a logging rule right before any rejected traffic. This way, finding out problems with your firewall is easy, as you can see what and when it is dropping something. This target accepts an option
–log-prefix, that allow to prefix a text to each log message.
- MASQUERADE, SNAT, DNAT
Those are three possibilities of implementing NAT (RFC1631). This way, it is possible to integrate a private network (RFC1918) to the internet with no problems. Please refer to the manual on the use of each target.
Linux has a different approach for routing than other UNIX. The way things are implemented on Linux is more flexible and powerful than traditional ways. Legacy utilities such as
route are still valid, but incomplete. This is because they do not give access to the advanced routing layer present on Linux. The utility
ip (part of
iproute2) is the current tool for networking related stuff under Linux. This tool will be the focus of this section.
Linux advanced routing is implemented in two parts:
It is possible to have multiple routing tables under Linux and the rules indicate when to use each one of them.
To list existing routing rules, one can try:
# ip rule list 0: from all lookup local 32766: from all lookup main 32767: from all lookup default
Rules are processed in increasing priority (1st) column). In the above case, priority 0 rule instruct the kernel to look for a routing decision inside table named
local for traffic comming from any origin. The content of this table can be accessed via:
# ip route list table local local 192.168.1.1 dev eth3 proto kernel scope host src 192.168.1.1 local 188.8.131.52 dev eth0 proto kernel scope host src 184.108.40.206 broadcast 192.168.1.0 dev eth3 proto kernel scope link src 192.168.1.1 broadcast 192.168.2.255 dev eth1 proto kernel scope link src 192.168.2.1 local 220.127.116.11 dev eth0 proto kernel scope host src 18.104.22.168 broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 broadcast 22.214.171.124 dev eth0 proto kernel scope link src 126.96.36.199 local 188.8.131.52 dev eth0 proto kernel scope host src 184.108.40.206 local 220.127.116.11 dev eth0 proto kernel scope host src 18.104.22.168 local 22.214.171.124 dev eth0 proto kernel scope host src 126.96.36.199 broadcast 188.8.131.52 dev eth0 proto kernel scope link src 184.108.40.206 local 220.127.116.11 dev eth0 proto kernel scope host src 18.104.22.168 local 22.214.171.124 dev eth0 proto kernel scope host src 126.96.36.199 broadcast 188.8.131.52 dev eth2 proto kernel scope link src 184.108.40.206 local 220.127.116.11 dev eth0 proto kernel scope host src 18.104.22.168 local 192.168.2.1 dev eth1 proto kernel scope host src 192.168.2.1 broadcast 192.168.1.255 dev eth3 proto kernel scope link src 192.168.1.1 broadcast 192.168.2.0 dev eth1 proto kernel scope link src 192.168.2.1 broadcast 22.214.171.124 dev eth0 proto kernel scope link src 126.96.36.199 broadcast 188.8.131.52 dev eth0 proto kernel scope link src 184.108.40.206 broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 broadcast 220.127.116.11 dev eth2 proto kernel scope link src 18.104.22.168 local 22.214.171.124 dev eth2 proto kernel scope host src 126.96.36.199 local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
This table is long, but is managed by the kernel itself, thus, do not worry about it. This table contains information regarding local addresses (the ones associated with network interfaces), informing that they are resolved locally.
In the case the table does not solve a certain destination address (eg. traffic to another machine in this case), Linux kernel look for the next rule with lower priority (higher priority number) to find out a routing solution. In this case, the next rule is the one with priority 32766, which says to go to table
# ip route list table main 188.8.131.52/29 dev eth0 proto kernel scope link src 184.108.40.206 192.168.2.0/24 dev eth1 proto kernel scope link src 192.168.2.1 192.168.1.0/24 dev eth3 proto kernel scope link src 192.168.1.1 220.127.116.11/24 dev eth2 proto kernel scope link src 18.104.22.168 22.214.171.124/21 dev eth0 proto kernel scope link src 126.96.36.199 default via 188.8.131.52 dev eth2
This table contain exactly the same information displayed by the
# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 184.108.40.206 0.0.0.0 255.255.255.248 U 0 0 0 eth0 192.168.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth3 220.127.116.11 0.0.0.0 255.255.255.0 U 0 0 0 eth2 18.104.22.168 0.0.0.0 255.255.248.0 U 0 0 0 eth0 0.0.0.0 22.214.171.124 0.0.0.0 UG 0 0 0 eth2
So, this table contains the traditional routing information. The last table named
default comes empty (try it).
The rules and tables described here contain all the traditional routing information. It is possible however to insert new rules and new tables to fit demands of a more complex environment.
One can for example, add a rule specifying that traffic from a certain source must be routed using a specific routing table:
# ip rule add type unicast from 192.168.4.0/24 priority 55 table 55
This will instruct the kernel to when it finds traffic coming from 192.168.4.0/24 network, look for a routing solution inside table number 55, with priority 55. The table 55 must be created containing routing information regarding only to the source network in question. In this case:
# ip rule list 0: from all lookup local 55: from 192.168.4.0/24 lookup 55 32766: from all lookup main 32767: from all lookup default
Note that priority number 55 and table number 55 are absolutely arbitrary and have no need to be the same number. These numbers must only make sense to fit your routing needs.
Please look at
ip manual to obtain more information on how to add or remove rules and all possible parameters.
Routing tables are identified by numbers from 0 to 255. Tables
main have the numbers 255 and 254 associated with it. There is a configuration file that one can associate a name to each table number (as for
main) that will not be covered by this article.
The existence of a table containing routing rules inside does not imply that it is in use. A table will only be used if there is a rule pointing to it.
ip manual on how to add routing rules inside tables. It should not be a problem, since its operation is similar to classic routing utility
Linux kernel comes with an anti-spoof protection apart from firewall. This protection protects an internal network against external attacks. Depending on the routing situation you need (specially when connected to multiple ISPs) this setting must be disable in order for your routing rules to work.
This option is accessible vis sysctl
which changes configuration for all network interfaces. There is also the possibility to enable / disable by interface (substituting
.eth0 for example, to change only interface eth0).
/etc/sysctl.conf, should disable this feature at boot time. Please refer to distribution specific settings.
One possible routing rule matching filter is
fwmark. This option makes packets marked by the firewall to be matched and fall into desired table.
# ip rule list 0: from all lookup local 33: from all fwmark 0x1 lookup 33 32766: from all lookup main 32767: from all lookup default
In this case, packets marked as 0x1 by the firewall (0x1 is 1 noted in hexa-decimal) to be solved by the table numbered 33. The number 0x1 is absolutely arbitrary.
It is important to know that this mark is local and internal to the Linux kernel and in any way alters the packets.
To mark a packet with iptables, use the target
# iptables -t mangle -A PREROUTING -i eth0 -j MARK --set-mark 1
In this example, all traffic coming through interface eth0 will be marked as 1 (an arbitrary number). After this, it is possible to include a routing rule matching these packets, as explained on the previous topic. Thus, this iptables rule and the routing rule will make specific routing for packets coming by interface eth0.
Note by the above iptables fluxogram that the mark is being done before routing table. In this case, traffic is entering the machine, thus the
PREROUTING. In the case you wish to mark traffic generated locally, you should use the
It is also possible to mark all packets related to a connection. The target
CONNMARK allows marking of a connection (referred as connection mark, internal to the firewall), that is not the same marking seen by routing (referred as netfilter mark, accessible by routing rules), do not confuse them. Thus, one can make the following to mark packets of a connection to the routing rules:
# iptables -t mangle -A OUTPUT -d SERVER -j CONNMARK --set-mark 1 # iptables -t mangle -A OUTPUT -m connmark --mark 1 -j MARK --set-mark 1
The first rule mark all desired connections. The second rule uses the match extension
-m connmark to identify those connection marks and create netfilter marks by using the target
MARK (to be saved by routing rules).