What's new

Issue 386.7_2: iptables rules created by VPN Director get lost

  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

grogi

Occasional Visitor
There seems to be an issue with the iptables rules to force DNS traffic through the tunnel created by VPN Director. They are being lost when some of the services restart, I lack the internal knowledge to pinpoint which exactly. I originally though it was dnsmasq restart (trigger by exp. changing the lease time of DHCP leases), but this alone doesn't trigger the issue.

To reproduce:
- activate a OpenVPN connection, exp. OpenVPN Client 1
- create a rule for that connection in VPNDirector, click apply the rules
- check the iptables-save -t nat table
- change the DNS that is broadcasted to the client devices (LAN -> DHCP Server page)
- router restarts services (a rather long cycle that goes over 3%, 7%...). Syslog says: rc_service: httpd 13637:notify_rc restart_net_and_phy
- check the iptables-save -t nat table again. DNS Intercept rules are missing.

Those rules will not be until user manually clicks Apply in the VPN Director tab in the router UI. They don't get recreated on router restart either.

Example:
Code:
#:> iptables-save -t nat
# Generated by iptables-save v1.4.15 on Thu Sep 15 20:44:42 2022
  ....
-A PREROUTING -p tcp -m tcp --dport 53 -j DNSVPN2           
-A PREROUTING -p udp -m udp --dport 53 -j DNSVPN2
-A PREROUTING -d 192.168.178.4/32 -j GAME_VSERVER
-A PREROUTING -d 192.168.178.4/32 -j VSERVER
-A PREROUTING -i br+ -p udp -m udp --dport 53 -j DNSFILTER
-A PREROUTING -i br+ -p tcp -m tcp --dport 53 -j DNSFILTER
-A POSTROUTING -o tun12 -j MASQUERADE
-A DNSFILTER -j DNAT --to-destination 192.168.178.16
-A DNSVPN2 -s 192.168.178.20/32 -j DNAT --to-destination 10.200.0.1
COMMIT

After I changed the DNS advertised by the LAN/DHCP server screen, router got a bit busy restarting some services. When it came back, the

Code:
#:> iptables-save -t nat
# Generated by iptables-save v1.4.15 on Thu Sep 15 20:50:47 2022
   ...
-A PREROUTING -d 192.168.178.4/32 -j GAME_VSERVER
-A PREROUTING -d 192.168.178.4/32 -j VSERVER
-A PREROUTING -i br+ -p udp -m udp --dport 53 -j DNSFILTER
-A PREROUTING -i br+ -p tcp -m tcp --dport 53 -j DNSFILTER
-A POSTROUTING -o tun12 -j MASQUERADE
-A DNSFILTER -j DNAT --to-destination 192.168.178.16
COMMIT

Once I go to VPN Director page and click apply, the rules come back. Selective routing reported by ip rule is not affected.

We are interested in the two PREROUTING rules disappearing (there will be more if more clients are configured), as well as the complete DNSVPNx chains.
Code:
-A PREROUTING -p tcp -m tcp --dport 53 -j DNSVPN2           
-A PREROUTING -p udp -m udp --dport 53 -j DNSVPN2

-A DNSVPN2 -s 192.168.178.20/32 -j DNAT --to-destination 10.200.0.1
 
Last edited:
Why would you be changing the DNS server(s) under LAN - DHCP server on a running system in the first place?! Many parts of the system are intended to be configured once, then left alone. If such changes are needed, you're expected to make them then reboot so everything is properly reconfigure w/ those changes in mind.

Too many users believe (mistakenly) that they can just make all kinds of arbitrary changes on a running system and expect everything else to immediately adjust accordingly. That just isn't necessarily the case.

IIRC, the OpenVPN clients are NOT dependent on the firewall service directly. The firewall rules are managed independently (via scripting) when the OpenVPN client is initiated. That probably explains this specific behavior. And why the correct procedure is to restart the OpenVPN client.
 
Why would you be changing the DNS server(s) under LAN - DHCP server on a running system in the first place?! Many parts of the system are intended to be configured once, then left alone.
That is just a way to reproduce the issue, not representative of anything I would recommend.

The issue is present after router restart too, btw.

If such changes are needed, you're expected to make them then reboot so everything is properly reconfigure w/ those changes in mind.
The router software is designed to reboot as many services as necessarily. In fact it would reboot completely if you change some settings, exp. disable/enable CTF.
 
Last edited:
The router software is designed to reboot as many services as necessarily. In fact it would reboot completely if you change some settings, exp. disable/enable CTF.
Asus and RMerlin try to minimise the number of services that are restarted whenever there's a change to the settings. People complain enough already about these things. But with the best will in the world they're bound to miss some combinations, and in the case of DNS and VPN there seem to be endless permutations. So I'm not at all surprised that when you throw DNSFilter into the mix it's not able to cope.

I suppose RMerlin might be able to add all the VPN clients to the list of services that need to be restarted after changes to DNSFilter. But no doubt people would complain about that to. At the end of the day the amount of effort to "fix" it might not be worth the effort.
 
Asus and RMerlin try to minimise the number of services that are restarted whenever there's a change to the settings.

Back in early 2000s, when first TomatoWrt for WRT54-G was released, it was its design principle. What a massive improvement it was from the likes of HyperWrt, which would restart for every tiny thing. AJAX based UI and asynchronous configuration changes - it was a game changer.

Asus based the original AsusWrt on the TomatoWrt. So it is in MerlinWrt DNA... ;)

I suppose RMerlin might be able to add all the VPN clients to the list of services that need to be restarted after changes to DNSFilter. But no doubt people would complain about that to. At the end of the day the amount of effort to "fix" it might not be worth the effort.

It's not the VPN Client that needs to restart - I've managed to see that it already does. However, (in my case I have the client2) there should be a /etc/openvpn/client2/dns.sh file. It's not there. Once I click apply in VPN Director, this file is created with all the rules that should be executed. Seems that the thing that is missing is reapplying all VPN Director rules after VPN Client reconnects.

I can see that the UI sends the request to action_mode: apply; action_script: restart_vpnrouting0, but I cannot track where it ends up.

I tried to reinvent the wheel and rewrite that functionality in a custom script executed after dnsmasq restarts or the tunnel restart (reading out the /jffs/openvpn/vpndirector_rulelist, parsing it, removing old rules if the are present etc.), but cannot figure out where to get the remote DNS entry from.

But with the best will in the world they're bound to miss some combinations, and in the case of DNS and VPN there seem to be endless permutations.

Absolutely agree. The amount of functionality thrown into those boxes is unreal, ASUS are dangerously close to bloatware at this stage imho... :( Almost impossible to maintain, as @RMerlin explains.

There are graph algorithms that work on directed acyclic graph designed to solve scenarios like that - each software components declares which other parts it depends on and the code resolves what parts need to be re-executed. For it to work though your software needs to be build around it from the get go. It's not something that can be easily added now, even by Asus, if they wanted to.

So I'm not at all surprised that when you throw DNSFilter into the mix it's not able to cope.

It's not DNSFilter, who's the culprit. Switching it off does not improve things. DNSFilter rules are applied consistently though, after every configuration change I could do.

-- edit

Clarified when the issue happens.
 
Last edited:
Meanwhile, following openvpn-event script does the job...
  • obeys the Accept DNS Configuration for each of the VPN Clients (will redirect DNS through given tunnel only if Strict or Exlusive policy is selected)
  • obeys the Redirect Internet traffic through tunnel - redirects DNS Traffic to the tunnel only if All is selected, or VPN Director with appropriate rule.
  • VPN Director rules are respected.

Bash:
#!/bin/sh

VPNCLIENTNO=$(echo $dev | cut -c5-5)
VPNCLIENTPATH=/etc/openvpn/client$VPNCLIENTNO


# Mode of the DNS for this VPN Client
#   0 - Ignore
#   1 - Relaxed
#   2 - Strict
#   3 - Exlusive
# We are going to capture the DNS traffic only when DNS_MODE is Strict or Exclusive
VPN_DNS_MODE=$(nvram get "vpn_client${VPNCLIENTNO}_adns")

# Mode of the VPN_Directory for this VPN Client
#   0 - Do not push traffic through the tunnel
#   1 - Push all traffic through the tunnel
#   2 - Use VPN_Director
# We are going to capture the DNS traffic if the client is in mode 1 or 2.
VPN_DIRECTOR_MODE=$(nvram get "vpn_client${VPNCLIENTNO}_rgw")

function addChain() {
        MAPPINGHOSTSRC=$1
        CHAIN_NAME="DNSVPN$VPNCLIENTNO"

        CHAIN_EXISTS=$(iptables-save -t nat | grep ':'$CHAIN_NAME)
        if [ -z "$CHAIN_EXISTS" ]; then
                logger "Chain $CHAIN_NAME doesn't exists. Creating..."

                iptables -t nat -N $CHAIN_NAME
                iptables -t nat -I PREROUTING -p udp -m udp --dport 53 -j $CHAIN_NAME
                iptables -t nat -I PREROUTING -p tcp -m tcp --dport 53 -j $CHAIN_NAME
        fi

        RULE_EXISTS=$(iptables-save -t nat | grep -Fe "-A $CHAIN_NAME -s $MAPPINGHOSTSRC")
        if [ -z "$RULE_EXISTS" ]; then
                iptables -t nat -A $CHAIN_NAME -s $MAPPINGHOSTSRC -j DNAT --to-destination $VPNDNS
        fi
}

if [ "$script_type" == "route-up" ]; then
        if [ "${VPN_DNS_MODE}" == "0" -o "${VPN_DNS_MODE}" == "1" ]; then
                logger "OpenVPN Client $VPNCLIENTNO. DNS policy either Ignore or Relaxed. Not going to force DNS traffic through the tunnel."
                exit 0
        fi

        # Sometimes the configuration returns two DNS servrs. Picking just the first one
        VPNDNS=$(cat $VPNCLIENTPATH/client.resolv | cut -d = -f2 | head -n 1)

        if [ -z "$VPNDNS" ]; then
                logger "OpenVPN Client $VPNCLIENTNO. No DNS associated with this client, check Accept DNS Configuration in the config screen"
                exit 0
        fi

        if [ "$VPN_DIRECTOR_MODE" == "0" ]; then
                logger "OpenVPN Client $VPNCLIENTNO doesn't push clients to use the tunnel. Skipping..."
                exit 0
        fi

        if [ "$VPN_DIRECTOR_MODE" == "1" ]; then
                logger "OpenVPN Client $VPNCLIENTNO - recreating DNSFilter to $VPNDNS for all requests."
                addChain "0.0.0.0/0"
                exit 0
        fi

        logger "OpenVPN Client $VPNCLIENTNO. Using VPN Director - recreating DNSFilter to $VPNDNS."

        MAPPINGS=$(cat /jffs/openvpn/vpndirector_rulelist | grep -oE '<1>[^>]*>[^>]+>>OVPN[1-5]' | awk -F '>' '{print $3" "$5}')

        IFS=$'\n'
        for MAPPING in $MAPPINGS; do
                MAPPINGHOSTSRC=$(echo $MAPPING | cut -d " " -f1)
                MAPPINGCLIENTVPNNO=$(echo $MAPPING | cut -d " " -f2 | cut -c5-5)

                if [ "$VPNCLIENTNO" == "$MAPPINGCLIENTVPNNO" ]; then
                        addChain "$MAPPINGHOSTSRC"
                fi
        done
fi
 
Last edited:
Meanwhile, following openvpn-event script does the job...
  • obeys the Accept DNS Configuration for each of the VPN Clients (will redirect DNS through given tunnel only if Strict or Exlusive policy is selected)
  • obeys the Redirect Internet traffic through tunnel - redirects DNS Traffic to the tunnel only if All is selected, or VPN Director with appropriate rule.
  • VPN Director rules are respected.

Bash:
#!/bin/sh

VPNCLIENTNO=$(echo $dev | cut -c5-5)
VPNCLIENTPATH=/etc/openvpn/client$VPNCLIENTNO


# Mode of the DNS for this VPN Client
#   0 - Ignore
#   1 - Relaxed
#   2 - Strict
#   3 - Exlusive
# We are going to capture the DNS traffic only when DNS_MODE is Strict or Exclusive
VPN_DNS_MODE=$(nvram get "vpn_client${VPNCLIENTNO}_adns")

# Mode of the VPN_Directory for this VPN Client
#   0 - Do not push traffic through the tunnel
#   1 - Push all traffic through the tunnel
#   2 - Use VPN_Director
# We are going to capture the DNS traffic if the client is in mode 1 or 2.
VPN_DIRECTOR_MODE=$(nvram get "vpn_client${VPNCLIENTNO}_rgw")

function addChain() {
        MAPPINGHOSTSRC=$1
        CHAIN_NAME="DNSVPN$VPNCLIENTNO"

        CHAIN_EXISTS=$(iptables-save -t nat | grep ':'$CHAIN_NAME)
        if [ -z "$CHAIN_EXISTS" ]; then
                logger "Chain $CHAIN_NAME doesn't exists. Creating..."

                iptables -t nat -N $CHAIN_NAME
                iptables -t nat -I PREROUTING -p udp -m udp --dport 53 -j $CHAIN_NAME
                iptables -t nat -I PREROUTING -p tcp -m tcp --dport 53 -j $CHAIN_NAME
        fi

        RULE_EXISTS=$(iptables-save -t nat | grep -Fe "-A $CHAIN_NAME -s $MAPPINGHOSTSRC")
        if [ -z "$RULE_EXISTS" ]; then
                iptables -t nat -A $CHAIN_NAME -s $MAPPINGHOSTSRC -j DNAT --to-destination $VPNDNS
        fi
}

if [ "$script_type" == "route-up" ]; then
        if [ "${VPN_DNS_MODE}" == "0" -o "${VPN_DNS_MODE}" == "1" ]; then
                logger "OpenVPN Client $VPNCLIENTNO. DNS policy either Ignore or Relaxed. Not going to force DNS traffic through the tunnel."
                exit 0
        fi

        # Sometimes the configuration returns two DNS servrs. Picking just the first one
        VPNDNS=$(cat $VPNCLIENTPATH/client.resolv | cut -d = -f2 | head -n 1)

        if [ -z "$VPNDNS" ]; then
                logger "OpenVPN Client $VPNCLIENTNO. No DNS associated with this client, check Accept DNS Configuration in the config screen"
                exit 0
        fi

        if [ "$VPN_DIRECTOR_MODE" == "0" ]; then
                logger "OpenVPN Client $VPNCLIENTNO doesn't push clients to use the tunnel. Skipping..."
                exit 0
        fi

        if [ "$VPN_DIRECTOR_MODE" == "1" ]; then
                logger "OpenVPN Client $VPNCLIENTNO - recreating DNSFilter to $VPNDNS for all requests."
                addChain "0.0.0.0/0"
                exit 0
        fi

        logger "OpenVPN Client $VPNCLIENTNO. Using VPN Director - recreating DNSFilter to $VPNDNS."

        MAPPINGS=$(cat /jffs/openvpn/vpndirector_rulelist | grep -oE '<1>[^>]*>[^>]+>>OVPN[1-5]' | awk -F '>' '{print $1" "$3}')

        IFS=$'\n'
        for MAPPING in $MAPPINGS; do
                MAPPINGHOSTSRC=$(echo $MAPPING | cut -d " " -f1)
                MAPPINGCLIENTVPNNO=$(echo $MAPPING | cut -d " " -f2 | cut -c5-5)

                if [ "$VPNCLIENTNO" == "$MAPPINGCLIENTVPNNO" ]; then
                        addChain "$MAPPINGHOSTSRC"
                fi
        done
fi

Ok, now you've lost me completely.

I thought the problem was when something else in the system caused the firewall to be reinitialized, but without reapplying the firewall changes associated w/ the OpenVPN client (specifically those related to DNS). And one solution was to restart the OpenVPN client, which you apparently reject. But your script above assumes the OpenVPN client *is* being restarted, since the route-up event will only be triggered in that particular situation, which makes the script redundant.
 
Ok, now you've lost me completely.

:)

I thought the problem was when something else in the system caused the firewall to be reinitialized, but without reapplying the firewall changes associated w/ the OpenVPN client (specifically those related to DNS). And one solution was to restart the OpenVPN client, which you apparently reject. But your script above assumes the OpenVPN client *is* being restarted, since the route-up event will only be triggered in that particular situation, which makes the script redundant.

I didn't know exactly what was causing the issue - after digging a bit and playing around I know. It is the restart_net_and_phy service restart. I will update the original post to include that.

The problem is that those rules DON'T get reinstated with the OpenVPN client restart, when it is plugged into the above sequence. Policy based routing does (rules you see when you execute ip rule), DNS intercept rules don't. They only get populated when you manually click Apply in UI on the VPN Director page. Here the script above come in and recreates them rules after OpenVPN client restarted.
 
Last edited:
:)



I didn't know exactly what was causing the issue - after digging a bit and playing around I know. It is the OpenVPN client restart. I will update the original post to include that.

The problem is that those rules DON'T get reinstated with the OpenVPN client restart. Policy based routing is (rules you see when you execute ip rule), DNS intercept rules don't. They only get populated when you manually click Apply in UI on the VPN Director page. Here the script come in and recreates them rules after OpenVPN client restart.

Well that makes more sense. And if this is the case, then that sounds like a bug to me. I can't imagine why just that part of the firewall changes wouldn't be reapplied on a restart. After all, a restart implies a full stop before the restart takes place, so there'd be no reason for the OpenVPN client to NOT reapply those rules. IOW, there shouldn't be any effective difference between a restart vs. a full stop followed by a start. They should respond identically. And if they don't, then as I say, that sounds like a bug.
 

Similar threads

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top