miniupnpd: When enabled perform STUN to learn external IP address and NAT type

Also enable port forwarding when direct (non-NAT) connection or unrestricted NAT 1:1 (without any filtering) is detected.
This commit is contained in:
Pali Rohár 2018-05-19 13:32:42 +02:00
parent 8e10a1aeab
commit 8c97654d70
6 changed files with 107 additions and 1 deletions

View File

@ -64,6 +64,7 @@
#include "minissdp.h"
#include "upnpredirect.h"
#include "upnppinhole.h"
#include "upnpstun.h"
#include "miniupnpdtypes.h"
#include "daemonize.h"
#include "upnpevents.h"
@ -1028,6 +1029,44 @@ parselan_error:
return -1;
}
static char ext_addr_str[INET_ADDRSTRLEN];
int update_ext_ip_addr_from_stun(int init)
{
struct in_addr if_addr, ext_addr;
int restrictive_nat;
char if_addr_str[INET_ADDRSTRLEN];
syslog(LOG_INFO, "STUN: Performing with host=%s and port=%u ...", ext_stun_host, (unsigned)ext_stun_port);
if (getifaddr(ext_if_name, if_addr_str, INET_ADDRSTRLEN, &if_addr, NULL) < 0) {
syslog(LOG_ERR, "STUN: Cannot get IP address for ext interface %s", ext_if_name);
return 1;
}
if (perform_stun(ext_if_name, if_addr_str, ext_stun_host, ext_stun_port, &ext_addr, &restrictive_nat) != 0) {
syslog(LOG_ERR, "STUN: Performing STUN failed: %s", strerror(errno));
return 1;
}
if (!inet_ntop(AF_INET, &ext_addr, ext_addr_str, sizeof(ext_addr_str))) {
syslog(LOG_ERR, "STUN: Function inet_ntop for IP address returned by STUN failed: %s", strerror(errno));
return 1;
}
syslog(LOG_INFO, "STUN: ... done");
if ((init || disable_port_forwarding) && !restrictive_nat) {
if (addr_is_reserved(&if_addr))
syslog(LOG_INFO, "STUN: ext interface %s with IP address %s is now behind unrestricted NAT 1:1 with public IP address %s: Port forwarding is now enabled", ext_if_name, if_addr_str, ext_addr_str);
else
syslog(LOG_INFO, "STUN: ext interface %s has now public IP address %s: Port forwarding is now enabled", ext_if_name, if_addr_str);
} else if ((init || !disable_port_forwarding) && restrictive_nat) {
syslog(LOG_INFO, "STUN: ext interface %s with IP address %s is now behind restrictive NAT with public IP address %s: Port forwarding is now impossible", ext_if_name, if_addr_str, ext_addr_str);
}
disable_port_forwarding = restrictive_nat;
return 0;
}
/* fill uuidvalue_wan and uuidvalue_wcd based on uuidvalue_igd */
void complete_uuidvalues(void)
{
@ -1141,6 +1180,16 @@ init(int argc, char * * argv, struct runtime_vars * v)
case UPNPEXT_IP:
use_ext_ip_addr = ary_options[i].value;
break;
case UPNPEXT_PERFORM_STUN:
if(strcmp(ary_options[i].value, "yes") == 0)
SETFLAG(PERFORMSTUN);
break;
case UPNPEXT_STUN_HOST:
ext_stun_host = ary_options[i].value;
break;
case UPNPEXT_STUN_PORT:
ext_stun_port = atoi(ary_options[i].value);
break;
case UPNPLISTENING_IP:
lan_addr = (struct lan_addr_s *) malloc(sizeof(struct lan_addr_s));
if (lan_addr == NULL)
@ -1337,6 +1386,10 @@ init(int argc, char * * argv, struct runtime_vars * v)
return 1;
}
#endif /* ENABLE_PCP */
if (GETFLAG(PERFORMSTUN) && !ext_stun_host) {
fprintf(stderr, "You must specify ext_stun_host= when ext_perform_stun=yes\n");
return 1;
}
}
#endif /* DISABLE_CONFIG_FILE */
@ -1613,6 +1666,11 @@ init(int argc, char * * argv, struct runtime_vars * v)
goto print_usage;
}
if (use_ext_ip_addr && GETFLAG(PERFORMSTUN)) {
fprintf(stderr, "Error: options ext_ip= and ext_perform_stun=yes cannot be specified together\n");
return 1;
}
if (use_ext_ip_addr) {
if (inet_pton(AF_INET, use_ext_ip_addr, &addr) != 1) {
fprintf(stderr, "Error: option ext_ip contains invalid address %s\n", use_ext_ip_addr);
@ -1941,7 +1999,16 @@ main(int argc, char * * argv)
GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "",
ext_if_name, upnp_bootid);
if (!use_ext_ip_addr)
if(GETFLAG(PERFORMSTUN))
{
int ret = update_ext_ip_addr_from_stun(1);
if (ret != 0) {
syslog(LOG_ERR, "Performing STUN failed. EXITING");
return 1;
}
use_ext_ip_addr = ext_addr_str;
}
else if (!use_ext_ip_addr)
{
char if_addr[INET_ADDRSTRLEN];
struct in_addr addr;
@ -1951,6 +2018,7 @@ main(int argc, char * * argv)
}
if (addr_is_reserved(&addr)) {
syslog(LOG_INFO, "Reserved / private IP address %s on ext interface %s: Port forwarding is impossible", if_addr, ext_if_name);
syslog(LOG_INFO, "You are probably behind NAT, enable option ext_perform_stun=yes to detect public IP address");
disable_port_forwarding = 1;
}
}
@ -2146,6 +2214,8 @@ main(int argc, char * * argv)
if(should_send_public_address_change_notif)
{
syslog(LOG_INFO, "should send external iface address change notification(s)");
if(GETFLAG(PERFORMSTUN))
update_ext_ip_addr_from_stun(0);
if (!use_ext_ip_addr)
{
char if_addr[INET_ADDRSTRLEN];
@ -2156,6 +2226,7 @@ main(int argc, char * * argv)
syslog(LOG_INFO, "Public IP address %s on ext interface %s: Port forwarding is enabled", if_addr, ext_if_name);
} else if (!disable_port_forwarding && reserved) {
syslog(LOG_INFO, "Reserved / private IP address %s on ext interface %s: Port forwarding is impossible", if_addr, ext_if_name);
syslog(LOG_INFO, "You are probably behind NAT, enable option ext_perform_stun=yes to detect public IP address");
}
disable_port_forwarding = reserved;
}

View File

@ -4,6 +4,25 @@
# If the WAN interface has several IP addresses, you
# can specify the one to use below
#ext_ip=
# WAN interface must have public IP address. Otherwise it is behind NAT
# and port forwarding is impossible. In some cases WAN interface can be
# behind unrestricted NAT 1:1 when all incoming traffic is NAT-ed and
# routed to WAN interfaces without any filtering. In this cases miniupnpd
# needs to know public IP address and it can be learnt by asking external
# server via STUN protocol. Following option enable retrieving external
# public IP address from STUN server and detection of NAT type. You need
# to specify also external STUN server in stun_host option below.
# This option is disabled by default.
#ext_perform_stun=yes
# Specify STUN server, either hostname or IP address
# Some public STUN servers:
# stun.stunprotocol.org
# stun.sipgate.net
# stun.xten.com
# stun.l.google.com (on non standard port 19302)
#ext_stun_host=stun.stunprotocol.org
# Specify STUN UDP port, by default it is standard port 3478.
#ext_stun_port=3478
# LAN network interfaces IPs / networks
# There can be multiple listening IPs for SSDP traffic, in that case

View File

@ -30,6 +30,9 @@ static const struct {
} optionids[] = {
{ UPNPEXT_IFNAME, "ext_ifname" },
{ UPNPEXT_IP, "ext_ip" },
{ UPNPEXT_PERFORM_STUN, "ext_perform_stun" },
{ UPNPEXT_STUN_HOST, "ext_stun_host" },
{ UPNPEXT_STUN_PORT, "ext_stun_port" },
{ UPNPLISTENING_IP, "listening_ip" },
#ifdef ENABLE_IPV6
{ UPNPIPV6_LISTENING_IP, "ipv6_listening_ip" },

View File

@ -17,6 +17,9 @@ enum upnpconfigoptions {
UPNP_INVALID = 0,
UPNPEXT_IFNAME = 1, /* ext_ifname */
UPNPEXT_IP, /* ext_ip */
UPNPEXT_PERFORM_STUN, /* ext_perform_stun */
UPNPEXT_STUN_HOST, /* ext_stun_host */
UPNPEXT_STUN_PORT, /* ext_stun_port */
UPNPLISTENING_IP, /* listening_ip */
#ifdef ENABLE_IPV6
UPNPIPV6_LISTENING_IP, /* listening address for IPv6 */

View File

@ -16,6 +16,10 @@
/* network interface for internet */
const char * ext_if_name = 0;
/* stun host/port configuration */
const char * ext_stun_host = 0;
uint16_t ext_stun_port = 0;
/* file to store leases */
#ifdef ENABLE_LEASEFILE
const char* lease_file = 0;

View File

@ -17,6 +17,10 @@
/* name of the network interface used to access internet */
extern const char * ext_if_name;
/* stun host/port configuration */
extern const char * ext_stun_host;
extern uint16_t ext_stun_port;
/* file to store all leases */
#ifdef ENABLE_LEASEFILE
extern const char * lease_file;
@ -73,6 +77,8 @@ extern int runtime_flags;
#define FORCEIGDDESCV1MASK 0x0800
#endif
#define PERFORMSTUN 0x1000
#define SETFLAG(mask) runtime_flags |= mask
#define GETFLAG(mask) (runtime_flags & mask)
#define CLEARFLAG(mask) runtime_flags &= ~mask