MiniUPnPd problem (solved & helper script)

The Chief

Regular Contributor
I've got a problem in 386.5_2 with miniupnpd refuses to map ports. Instead I got this:

Code:
Apr 23 01:50:25 RT-AC86U miniupnpd[2782]: Failed to add NAT-PMP 42613 tcp->192.168.16.16:22000 'NAT-PMP 42613 tcp'
Apr 23 01:50:26 RT-AC86U miniupnpd[2782]: Failed to add NAT-PMP 42613 tcp->192.168.16.16:22000 'NAT-PMP 42613 tcp'

I've investigated some miniupnpd and rc code and problem was in my ISP configuration (rare case). My WAN interface has private ip (172.30.0.xxx), but ISP forwards all ports from public external ip (213.234.xxx.xxx) to this private ip. Problem was /etc/upnp/config auto-generated by ASUS rc daemon lacks 'ext_ip=213.234.xxx.xxx' statement, so it can't map external ip (detected by clients) as eth0 (ext_ifname=eth0 for WAN) has private ip on it.

Well, more code to investigate. RC adds ext_ip to miniupnpd config only when wan(0|1)_realip_state nvram variable is "2" and wan(0|1)_realip_ip is set. But these nvram vars are not set even after successful inadyn ddns update! Inadyn updates ddns_ipaddr nvram var, but not realip vars mentioned earlier, nor rc regenerates miniupnpd config, nor restarts it. What to do?

Here's a helper script. It's not DualWAN-friendly, so beware.

CheckExtIP.sh
Bash:
#!/bin/sh
# IPv4 detection taken from WGM code by Martineau
Is_IPv4() {
    grep -oE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'                    # IPv4 format
}
Is_Private_IPv4() {
    grep -oE "(^127\.)|(^(0)?10\.)|(^172\.(0)?1[6-9]\.)|(^172\.(0)?2[0-9]\.)|(^172\.(0)?3[0-1]\.)|(^169\.254\.)|(^192\.168\.)"
}

var_wan0_realip_ip=$(nvram get wan0_realip_ip)
# if wan0_realip_ip nvram var is not set or not real ip
if [ ! "$(echo "$var_wan0_realip_ip" | Is_IPv4)" ] || [ "$(echo "$var_wan0_realip_ip" | Is_Private_IPv4)" ] ; then
  var_ddns_ipaddr=$(nvram get ddns_ipaddr)
  # if ddns_ipaddr nvram var is set and is a real ip
  if [ "$(echo "$var_ddns_ipaddr" | Is_IPv4)" ] && [ ! "$(echo "$var_ddns_ipaddr" | Is_Private_IPv4)" ] ; then
    logger -st "($(basename $0))" "Populating wan0_realip_ip with $var_ddns_ipaddr ip..."
    # set nvram vars needed for rc control script to populate miniupnpd config with proper ext_ip value
    nvram set wan0_realip_ip=$var_ddns_ipaddr
    nvram set wan0_realip_state="2"
    logger -st "($(basename $0))" "Restarting miniupnpd..."
    service restart_upnp
  fi
fi

We have no event script to be called on 'successful ddns update' event, so just add it to cron from init-start, for ex:

Bash:
cru a CheckExtIP "*/1 * * * * /jffs/scripts/CheckExtIP.sh" # CheckExtIP helper
 
Last edited:

The Chief

Regular Contributor
Slightly optimized:

Bash:
#!/bin/sh
# IPv4 detection taken from WGM code by Martineau
Is_IPv4() {
    grep -oE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'                    # IPv4 format
}
Is_Private_IPv4() {
    grep -oE "(^127\.)|(^(0)?10\.)|(^172\.(0)?1[6-9]\.)|(^172\.(0)?2[0-9]\.)|(^172\.(0)?3[0-1]\.)|(^169\.254\.)|(^192\.168\.)"
}

# get nvram vars
var_wan0_realip_ip=$(nvram get wan0_realip_ip)
var_ddns_ipaddr=$(nvram get ddns_ipaddr)
# if nvram vars wan0_realip_ip and ddns_ipaddr are equal then we're fine, else...
if [ "$var_wan0_realip_ip" != "$var_ddns_ipaddr" ] ; then
  # if ddns_ipaddr nvram var is set and is a public ip
  if [ "$(echo "$var_ddns_ipaddr" | Is_IPv4)" ] && [ ! "$(echo "$var_ddns_ipaddr" | Is_Private_IPv4)" ] ; then
    logger -st "($(basename $0))" "Populating wan0_realip_ip with $var_ddns_ipaddr ip..."
    # set nvram vars needed for rc control script to populate miniupnpd config with proper ext_ip value
    nvram set wan0_realip_ip=$var_ddns_ipaddr
    nvram set wan0_realip_state="2"
    logger -st "($(basename $0))" "Restarting miniupnpd..."
    service restart_upnp
  fi
fi
 

The Chief

Regular Contributor
DualWAN-friendly:

Bash:
#!/bin/sh
# IPv4 detection taken from WGM code by Martineau
Is_IPv4() {
    grep -oE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'                    # IPv4 format
}

Is_Private_IPv4() {
    grep -oE "(^127\.)|(^(0)?10\.)|(^172\.(0)?1[6-9]\.)|(^172\.(0)?2[0-9]\.)|(^172\.(0)?3[0-1]\.)|(^169\.254\.)|(^192\.168\.)"
}

var_wan0_primary=$(nvram get wan0_primary)
var_wan1_primary=$(nvram get wan1_primary)
# SingleWAN and DualWAN-friendly
# if neither wan0 or wan1 is active, or both are active (load balance WAN), then do nothing (exit)
[ $(($var_wan0_primary ^ $var_wan1_primary)) -eq 0 ] && exit
# from this point we assume one of WAN's is active

# get nvram vars
# get realip from active WAN
[ $var_wan0_primary -eq 1 ] && var_wan_realip_ip=$(nvram get wan0_realip_ip) || var_wan_realip_ip=$(nvram get wan1_realip_ip)
# get DDNS-provided external ip
var_ddns_ipaddr=$(nvram get ddns_ipaddr)

# if DDNS and active WAN ip's are equal then we're fine, else...
if [ "$var_wan_realip_ip" != "$var_ddns_ipaddr" ] ; then
  # if ddns_ipaddr nvram var is set and is a public ip
  if [ "$(echo "$var_ddns_ipaddr" | Is_IPv4)" ] && [ ! "$(echo "$var_ddns_ipaddr" | Is_Private_IPv4)" ] ; then
    logger -st "($(basename $0))" "Populating wan_realip_ip with $var_ddns_ipaddr ip..."
    # set nvram vars needed for rc control script to populate miniupnpd config with proper ext_ip value
    [ $var_wan0_primary -eq 1 ] && { nvram set wan0_realip_ip=$var_ddns_ipaddr; nvram set wan1_realip_ip=""; } || { nvram set wan0_realip_ip=""; nvram set wan1_realip_ip=$var_ddns_ipaddr; }
    [ $var_wan0_primary -eq 1 ] && { nvram set wan0_realip_state="2"; nvram set wan1_realip_state=""; } || { nvram set wan0_realip_state=""; nvram set wan1_realip_state="2"; }
    logger -st "($(basename $0))" "Restarting miniupnpd..."
    # command rc to recreate miniupnpd config and to restart it
    service restart_upnp
  fi
fi
 

The Chief

Regular Contributor
Hmmm… I've got a few cases when realip is properly set, but still no ext_ip= statement in miniupnpd config. Then we need to force update process.

Bash:
#!/bin/sh
# IPv4 detection taken from WGM code by Martineau
Is_IPv4() {
    grep -oE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'                    # IPv4 format
}

Is_Private_IPv4() {
    grep -oE "(^127\.)|(^(0)?10\.)|(^172\.(0)?1[6-9]\.)|(^172\.(0)?2[0-9]\.)|(^172\.(0)?3[0-1]\.)|(^169\.254\.)|(^192\.168\.)"
}
                    
# in rare cases there is no ext_ip= statement in miniupnpd config regardless of realip presence, so we need to force update
grep -qF -- "ext_ip=" "/etc/upnp/config" || force_upd=true
#
var_wan0_primary=$(nvram get wan0_primary)
var_wan1_primary=$(nvram get wan1_primary)
# SingleWAN and DualWAN-friendly
# if neither wan0 or wan1 is active, or both are active (load balance WAN), then do nothing (exit)
[ $(($var_wan0_primary ^ $var_wan1_primary)) -eq 0 ] && exit
# from this point we assume one of WAN's is active

# get nvram vars
# get realip from active WAN
[ $var_wan0_primary -eq 1 ] && var_wan_realip_ip=$(nvram get wan0_realip_ip) || var_wan_realip_ip=$(nvram get wan1_realip_ip)
# get DDNS-provided external ip
var_ddns_ipaddr=$(nvram get ddns_ipaddr)

# if DDNS and active WAN ip's are equal and no need in force updating then we're fine, else...
if [ "$var_wan_realip_ip" != "$var_ddns_ipaddr" ] || [ $force_upd ] ; then
  # if ddns_ipaddr nvram var is set and is a public ip
  if [ "$(echo "$var_ddns_ipaddr" | Is_IPv4)" ] && [ ! "$(echo "$var_ddns_ipaddr" | Is_Private_IPv4)" ] ; then
    logger -st "($(basename $0))" "Populating wan_realip_ip with $var_ddns_ipaddr ip..."
    # set nvram vars needed for rc control script to populate miniupnpd config with proper ext_ip value
    [ $var_wan0_primary -eq 1 ] && { nvram set wan0_realip_ip=$var_ddns_ipaddr; nvram set wan1_realip_ip=""; } || { nvram set wan0_realip_ip=""; nvram set wan1_realip_ip=$var_ddns_ipaddr; }
    [ $var_wan0_primary -eq 1 ] && { nvram set wan0_realip_state="2"; nvram set wan1_realip_state=""; } || { nvram set wan0_realip_state=""; nvram set wan1_realip_state="2"; }
    logger -st "($(basename $0))" "Restarting miniupnpd..."
    # command rc to recreate miniupnpd config and to restart it
    service restart_upnp
  fi
fi
 

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