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.
I use the comments in iptables as guidance. Try this:
# iptables -n -L |grep "user chain"
input_rule all -- 0.0.0.0/0 0.0.0.0/0 /* !fw3: user chain for input */
forwarding_rule all -- 0.0.0.0/0 0.0.0.0/0 /* !fw3: user chain for forwarding */
output_rule all -- 0.0.0.0/0 0.0.0.0/0 /* !fw3: user chain for output */
...
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.