@pavelgl
iptables is still fairly new to me so I don't know all of the parameters.
In the code below this first line essentially sets the variable rn with command substitution to the output of iptables with a table filter, lists all the rules in the chain, shows line numbers, filters out the CoD-PS4-UDP rule and finally prints the first column?
rn=$(iptables -t nat -L --line-numbers | grep CoD-PS4-UDP | awk '{ print $1 }')
iptables -t nat -R zone_wan_postrouting "$rn" -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --comment "CoD-PS4-UDP" -j SNAT --to-source "$ipv4":3080
With some reverse engineering of the commands I can see that my rule is listed under the zone_wan_postrouting chain and with all the piped commands, this filters it down to the number 5.
rn=$(iptables -t nat -L --line-numbers | grep CoD-PS4-UDP | awk '{ print $1 }')
echo ${rn}
5
Below is the combined both bits of your code with some modifications:
Modified code
# This creates the initial firewall rule
iptables -t nat -A zone_wan_postrouting -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --comment "CoD-PS4-UDP" -j SNAT --to-source "${WAN_IPv4}":3080
# IP address is listed in this file
WAN_IPv4="$(cat /tmp/WAN_IPv4)"
# Get the current IP address from the iptables rule
current_IPv4=$(iptables -t nat -L | grep CoD-PS4-UDP | sed 's/.* to\://' | sed 's/\:.*//')
if [ "${WAN_IPv4}" = "${current_IPv4}" ]; then
exit 0
else
rule_number=$(iptables -t nat -L zone_wan_postrouting --line-numbers | grep CoD-PS4-UDP | awk '{ print $1 }')
iptables -t nat -R zone_wan_postrouting "${rule_number}" -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --comment "CoD-PS4-UDP" -j SNAT --to-source "${WAN_IPv4}":3080
fi
exit 0
Is the whole idea of the iptables' replace parameter to simply update the existing rules by a specific number in the chain?
iptables --help
...
--replace -R chain rulenum Replace rule rulenum (1 = first) in chain
...
What's the difference between deleting the rule, re-appending it and restarting the firewall service?
delete chain rule-specification
-D, --delete chain rulenum
I was thinking of trying to match the majority of the rule specification except skipping the source WAN IP address but that gives me an error.
iptables -t nat -D zone_wan_postrouting -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --comment "CoD-PS4-UDP" -j SNAT
iptables v1.8.3 (legacy): SNAT: option "--to-source" must be specified
Is there a wildcard I can use or is it a case of finding the current IP address from the current rule as variable and calling that from the command above?
# Get the current IP address from the iptables rule
current_IPv4=$(iptables -t nat -L | grep CoD-PS4-UDP | sed 's/.* to\://' | sed 's/\:.*//')
iptables -t nat -D zone_wan_postrouting -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --c
omment "CoD-PS4-UDP" -j SNAT ${current_IPv4}:3080
In other words, if I was apply the same parameters that I appended to iptables and changed -A for -D I would end up with the command:
Commands and output
iptables -t nat -D zone_wan_postrouting -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --comment "CoD-PS4-UDP" -j SNAT --to-source "${WAN_IPv4}":3080
The only problem I've found is you can easily end up with duplicate rules in iptables as will explain below:
Creating first rule
root@OpenWrt-AP1:~# iptables -t nat -A zone_wan_postrouting -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --comment "CoD-PS4-UDP" -j SNAT --to-source "${WAN_ IPv4}":3080
I then list the rule including showing the line number
root@OpenWrt-AP1:~# iptables -t nat -L zone_wan_postrouting --line-numbers | grep CoD-PS4-UDP
7 SNAT udp -- William-PS4.lan anywhere udp spt:3074 /* CoD-PS4-UDP */ to:109.149.182.250:3080
Creating second rule
iptables -t nat -A zone_wan_postrouting -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --comment "CoD-PS4-UDP" -j SNAT --to-source "${WAN_ IPv4}":3080
I then list the rule(s) again
root@OpenWrt-AP1:~# iptables -t nat -L zone_wan_postrouting --line-numbers | grep CoD-PS4-UDP
7 SNAT udp -- William-PS4.lan anywhere udp spt:3074 /* CoD-PS4-UDP */ to:109.149.182.250:3080
8 SNAT udp -- William-PS4.lan anywhere udp spt:3074 /* CoD-PS4-UDP */ to:109.149.182.250:3080
As you can see above there are two duplicate rules. These duplicates wouldn't be a problem if the delete parameter deleted both rules together in a single command but it does't. I suppose one way around this is to run a while loop until the iptables returns zero.
However, the chances of duplicates with the script is very slim as they only way this could occur is if the delete parameter failed to run but carries on to the append parameter, and each time the script is ran it keeps appending the same rule.
Could I use the delete and append options in iptables within a for loop?
rule_1=$(iptables -t nat -L zone_wan_postrouting --line-numbers | grep CoD-PS4-UDP | awk '{ print $1 }')
rule_2=$(iptables -t nat -L zone_wan_postrouting --line-numbers | grep CoD-XBOX-UDP | awk '{ print $1 }')
for rule in ${rule_1} ${rule_2}
do
iptables -t nat -R zone_wan_postrouting "${rule}" -s 192.168.5.11/32 -p udp -m udp --sport 3074 -m comment --comment "CoD-PS4-UDP" -j SNAT --to-source "${WAN_IPv4}":3080
done
What's the difference between the replace parameter in comparison to delete and append or do they relatively do the same function in this case?
I take it because iptables files are treated like scripts, does that mean I could use a cronjob to periodically refresh the iptables commands? What shebang does iptables conform to?
Sorry for the long reply. I've been working on constructing this reply for a while and I wanted to make sure I asked the right questions. At the end of the day It helps me but helps others who may discover this thread/forums.