Netfilter In OpenWrt

The purpose of this section is to briefly describe the netfilter/iptables subsystem and then delve into OpenWrt specifics.

netfilter rules require a fine level of granularity to tune packet filtering. This can cause undesirable scenarios when many rules are matching on similar packets. Be careful using the iptable application!

Netfilter is the packet filtering framework inside the Linux kernel. It allows for packet filtering, network address [and port] translation (NA[P]T) and other packet manipulations. It is far more than a simple firewall and very powerful!

Netfilter has code hooks in the kernel networking stacks (NF_HOOK_ which are a set of #define to one of the nf_hook netfilter calls) and a set of netfilter kernel modules (mostly starting with nf_, ipt_ or xt_)

iptables is the user interface to the kernel netfilter subsystem. The iptables application uses the netfilter libiptc library to communicate between with the netfilter kernel modules. libiptc, like the networking stacks, uses a BSD socket interface to communicate between user-space and kernel-space.

There are many netfilter/iptables references so we will not repeat here, mainly because this would be almost purely a cut-and-paste effort with marginal levels of accuracy. Some good references are:

This section contains a high-level view of netfilter provided by OpenWrt.

The netfilter capabilites are exist in the kernel, either into the monolith or as loadable kernel modules. By default, OpenWrt builds the kernel with a useful set of netfilter capabilities for a robust router.

  • NAT
  • LOG
  • MATCH: MAC, STATE (connection state), TIME, MULTIPORT (one or more IP ports), LIMIT…
  • MARK

iptables and ip6tables user interfaces exist.

:!: ebtables is no longer available in official versions due to performance implications ( Please employ OpenWrt Buildroot if you need ebtables support. The kernel Kconfig settings are BRIDGE_NF_EBTABLES.

:!: arptables is not built, probably same reason as ebtables. This is an ipv4 function. The kernel Kconfig settings are IP_NF_ARPTABLES.

FIXME ebtables has returned. Were fore-mentioned performance issues fixed?

By default, OpenWrt uses three netfilter tables: filter, nat, mangle. These are sufficient to provide the desired netfilter functionality.

Two other netfilter tables are: raw, security.

The raw table can be added to the kernel via make menuconfig Kernel modules → Netfilter Extensions → kmod-ipt-raw . This will enable the netfilter IP_NF_RAW config:

config IP_NF_RAW
	tristate  'raw table support (required for NOTRACK/TRACE)'
	  This option adds a `raw' table to iptables. This table is the very
	  first in the netfilter framework and hooks in at the PREROUTING
	  and OUTPUT chains.
	  If you want to compile it as a module, say M here and read
	  <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.

The security table does not seem to be in the OpenWrt menuconfig. There is a reference in the kernel ipv4/netfilter/Kconfig to it but it is unclear how to enable kernel support for it.

	tristate "Security table"
	depends on SECURITY
	  This option adds a `security' table to iptables, for use
	  with Mandatory Access Control (MAC) policy.
	  If unsure, say N.

As was previously mentioned, there are a large number of netfilter references and examples. However, I find it helpful (to myself) to track each step of a specific fw3 rule from definition to packet processing (“Soup_to_nuts”).

This example fw3 application configuration rule allows SSH access from any station on the WAN-side of the router to any station on the LAN-side.

config rule
	option src 'wan'
	option dest 'lan'
	option proto 'tcp'
	option dest_port '22'
	option target 'ACCEPT'
	option name 'ACCEPT-SSH-WAN-LAN'

fw3 UCI parses the rule to the following iptables rule (with some others for context, implicitly created). The rules are listed as they appear in the fw3 print listing.

iptables -t filter -N zone_lan_dest_ACCEPT
iptables -t filter -N zone_lan_dest_REJECT
iptables -t filter -N zone_wan_forward
# TCP/22 from WAN jumps to zone_lan_dest_ACCEPT chain
iptables -t filter -A zone_wan_forward -p tcp -m tcp --dport 22 -m comment --comment "!fw3: ACCEPT-SSH-WAN-LAN" -j zone_lan_dest_ACCEPT
# All TCP from WAN jumps to zone_lan_dest_REJECT
iptables -t filter -A zone_wan_forward -p tcp -m comment --comment "!fw3: REJECT-ALL-WAN-LAN" -j zone_lan_dest_REJECT
# zone_lan_dest_ACCEPT jumps to final ACCEPT target
iptables -t filter -A zone_lan_dest_ACCEPT -o br-lan -m comment --comment "!fw3" -j ACCEPT
# All traffic jumps to final reject target
iptables -t filter -A zone_lan_dest_REJECT -o br-lan -m comment --comment "!fw3" -j reject
# FORWARD hook, jump to chain zone_wan_forward
iptables -t filter -A FORWARD -i eth1 -m comment --comment "!fw3" -j zone_wan_forward

The zone_lan_dest_ACCEPT, zone_lan_dest_REJECT and zone_wan_forward chains are created, primarily for convenience so they can be added and removed easily.

The first rule added to the zone_wan_forward chain performs a packet match for tcp/22 (SSH) and, if it passes, jumps to the zone_lan_dest_ACCEPT chain. Farther down the rules, this chain matches on the output interface br-lan (the lan-bridge) and jumps to the final ACCEPT target. One nuance to consider: the netfilter output interface does not provide any routing routing information; IFF the network stack decides to route the packet to the LAN then this rule will be invoked.

:!: Generally each match and target is a unique kernel module. TCP/22 uses the xt_tcpudp module, comment uses the xt_comment module (always returns success). The mangle TCPMSS target uses xt_TCPMSS; the reject target uses ipt_REJECT. The ACCEPT and DROP targets are essentially no-ops: ACCEPT allows the network stack to continue processing and DROP immediately discards the packet.

If the 'TCP/22' rule does not match, netfilter continues to the next rule which matches all TCP traffic (other than SSH) and jumps to zone_lan_dest_REJECT. The next rule chain jumps to the final reject target which sends back an ICMP packet to the originator.

Finally, the zone_wan_forward chain is appended to the FORWARD hook matching input from the eth1, the WAN interface. This hook is statically called in the kernel ipv4 network stack (see the NF_HOOK call in ./net/ipv4/ip_forward.c:ip_forward.)

So that covers a single conceptual rule: TCP/SSH traffic allowed from the WAN to the LAN. NAT is a little more tricky ;-)

Beyond the standard netfilter capabilities provided in the OpenWrt release, these are useful (but not necessary.)


ipset is a netfilter mechanism to quickly manage lists of similar entities.

One powerful use is blocking spam. Typically one adds a rule to reject/drop traffic from each source. In the standard firewall, the following rules block a single source from sending a large amount of email spam to the mail server (SMTP is port 25).

Currently, the most maintainable mechanism in OpenWrt is to add rules to a new chain in the WAN zone in /etc/firewall.user

iptables -N spam_block
iptables -A forwarding_rule -j spam_block
iptables -t filter -A spam_block -s -p tcp -m tcp --dport 25 -j DROP
iptables -t filter -A spam_block -s -p tcp -m tcp --dport 25 -j DROP

There are thousands of spam sources so the number of rules in the (custom) spam_block chain can be quite large.

In order to use ipset, it must be added to the kernel and application package.

In the OpenWrt image build directory, set it in the menu Kernel Modules → Netfilter Extensions → kmod-ipt-ipset

Once the kernel is running, add the package using opkg install ipset.

:!: the ipset package install will fail if the kernel has not been built to support it. DO NOT force install!!!!

FIXME There is probably a better way to add custom firewall capabilities.

Using ipset

This example shows how to use ipset to block a large number of spammers!

FIXME not tested yet…

config ipset
	option external         spam_block
	option match            'dest_ip dest_port'
	option family           ipv4
	option storage          hash

:!: Adding each rule to ipset will make /etc/config/firewall unmanageable. Put the iptable rules in /etc/firewall.user. And there is no point to using ipset for a small number of rules.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2020/04/04 16:00
  • by steve-newcomb