Problem installing custom /etc/firewall.user file

I have a custom /etc/firewall.user file, which I scp to the router.

iptables -N misc-lan-rules
iptables -A misc-lan-rules -p tcp --dport 22 -j RETURN -m comment --comment okSSH
iptables -I zone_lan_input -t filter -j misc-lan-rules

When I reboot the router, the file is partly loaded. I can see that my new chain has been defined, but it has not been inserted into to the zone_lan_input chain. Output from iptables -S follows:

-N misc-lan-rules
-A misc-lan-rules -p tcp -m tcp --dport 22 -m comment --comment okSSH -j RETURN
Notice that the chain is not referenced, as it should be from the third line in the original

When I manually log into the router and do a /etc/init.d/firewall restart, the custom firewall rules seem to be propery installed. Output from iptables -S follows:

-N misc-lan-rules
-A misc-lan-rules -p tcp -m tcp --dport 22 -m comment --comment okSSH -j RETURN
-A zone_lan_input -j misc-lan-rules

I don't understand why /etc/init.d/firewall restart is needed, even after a restart. This is especially important as the restart command does not seem to work when sent to the router from an automated configuration process.

"Chicken-and-egg" problem? Does your target exist when your rules are loaded? Have you tried capturing verbose output of the script to a temporary file, for example on /tmp/?

Is it wrong to assume that the rest of the Openwrt firewall definitions, for example zone_lan_input, are already loaded when the user definitions added?

That in any case does not explain why it reliably fails after the first restart but then (once I have done the /etc/init.d/firewall restart) reliably works in subsequent restarts.

Good idea on script output capture! I will add

Can't you add rules to the chains that are meant to be used for custom rules instead, for example input_lan_rule?

I have logged the firewall.user script output following a reboot and there is no error from any of the commands, including

iptables -I zone_lan_input -t filter -j misc-lan-rules

So the chain zone_lan_forward does exist when the command gets run, and the problem is still unresolved

Likely I should consider it. Is there a source/link which describes where custom rules are intended to be added? I must say it is unclear to me!

Try this:

uci set firewall.@include[0].reload="1"
uci commit firewall
service firewall restart

I use the comments in iptables as guidance. Try this:

# iptables -n -L |grep "user chain"
input_rule  all  --              /* !fw3: user chain for input */
forwarding_rule  all  --              /* !fw3: user chain for forwarding */
output_rule  all  --              /* !fw3: user chain for output */
1 Like

Why do you think this will help? The include file is already loading when given an explicit restart command

The firewall has two reload flavors:

  • A restart mode which will destroy the entire ruleset, rebuild it from scratch and process all includes and user scripts
  • A reload mode which will clear and rebuild internal chains while leaving the rest of the rule set mostly intact. However in order to free up internal chains and being able to delete them, all rules pointing to such internal chains are deleted as well. For firewall-internal rules this is no issue, as they'll be recreated immediately. But rules stemming from external sources will be lost after such a reload.

In your case, the zone_lan_input chain is an internal one, so the iptables -I zone_lan_input -t filter -j misc-lan-rules rule referencing it will get deleted during firewall reloads which may happen frequently due to various events.

Setting option reload 1 will cause your include script to be executed with every reload as well and not just on restarts which will take care of readding your iptables -I zone_lan_input ... rule.

If you do that though, you must rework your script to avoid creating duplicate rules with each firewall reload as only those rules "touching" internal chains (e.g. -I zone_lan_input ... or ... -j zone_lan_input) are deleted while others (e.g. -A misc-lan-rules ... ones) remain intact.

One can do that by using wrappers such as ipt() { iptables -D "$@" 2>/dev/null; iptables -A "$@"; } which will first emit a rule deletion request before each corresponding rule add.

On the other hand it is far easier to simply use the user hook chains, in your case input_lan_rule which do not suffer from the "deletion" problem.

You can see an overview over the chain structure here:
Any chain not marked "internal" is safe to use as either container or jump target.


This is a super helpful discussion. Thanks!