version 1.1.1.1, 2012/02/21 23:16:02
|
version 1.1.1.3, 2013/07/22 00:32:35
|
Line 1
|
Line 1
|
/* $Id$ */ |
/* $Id$ */ |
/* MiniUPnP project |
/* MiniUPnP project |
* (c) 2007-2010 Thomas Bernard | * (c) 2007-2012 Thomas Bernard |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* This software is subject to the conditions detailed |
* This software is subject to the conditions detailed |
* in the LICENCE file provided within the distribution */ |
* in the LICENCE file provided within the distribution */ |
Line 8
|
Line 8
|
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <syslog.h> |
#include <syslog.h> |
|
#include <errno.h> |
#include <time.h> |
#include <time.h> |
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <arpa/inet.h> |
#include <arpa/inet.h> |
|
|
|
#include "macros.h" |
#include "config.h" |
#include "config.h" |
#include "natpmp.h" |
#include "natpmp.h" |
#include "upnpglobalvars.h" |
#include "upnpglobalvars.h" |
#include "getifaddr.h" |
#include "getifaddr.h" |
#include "upnpredirect.h" |
#include "upnpredirect.h" |
#include "commonrdr.h" |
#include "commonrdr.h" |
|
#include "upnputils.h" |
|
|
#ifdef ENABLE_NATPMP |
#ifdef ENABLE_NATPMP |
|
|
int OpenAndConfNATPMPSocket(in_addr_t addr) |
int OpenAndConfNATPMPSocket(in_addr_t addr) |
{ |
{ |
int snatpmp; |
int snatpmp; |
|
int i = 1; |
snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/); |
snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/); |
if(snatpmp<0) |
if(snatpmp<0) |
{ |
{ |
syslog(LOG_ERR, "socket(natpmp): %m"); | syslog(LOG_ERR, "%s: socket(natpmp): %m", |
| "OpenAndConfNATPMPSocket"); |
return -1; |
return -1; |
} |
} |
else | if(setsockopt(snatpmp, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) |
{ |
{ |
|
syslog(LOG_WARNING, "%s: setsockopt(natpmp, SO_REUSEADDR): %m", |
|
"OpenAndConfNATPMPSocket"); |
|
} |
|
if(!set_non_blocking(snatpmp)) |
|
{ |
|
syslog(LOG_WARNING, "%s: set_non_blocking(): %m", |
|
"OpenAndConfNATPMPSocket"); |
|
} |
|
{ |
struct sockaddr_in natpmp_addr; |
struct sockaddr_in natpmp_addr; |
memset(&natpmp_addr, 0, sizeof(natpmp_addr)); |
memset(&natpmp_addr, 0, sizeof(natpmp_addr)); |
natpmp_addr.sin_family = AF_INET; |
natpmp_addr.sin_family = AF_INET; |
natpmp_addr.sin_port = htons(NATPMP_PORT); |
natpmp_addr.sin_port = htons(NATPMP_PORT); |
//natpmp_addr.sin_addr.s_addr = INADDR_ANY; | /*natpmp_addr.sin_addr.s_addr = INADDR_ANY; */ |
natpmp_addr.sin_addr.s_addr = addr; |
natpmp_addr.sin_addr.s_addr = addr; |
//natpmp_addr.sin_addr.s_addr = inet_addr("192.168.0.1"); |
|
if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0) |
if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0) |
{ |
{ |
syslog(LOG_ERR, "bind(natpmp): %m"); |
syslog(LOG_ERR, "bind(natpmp): %m"); |
Line 53 int OpenAndConfNATPMPSocket(in_addr_t addr)
|
Line 67 int OpenAndConfNATPMPSocket(in_addr_t addr)
|
int OpenAndConfNATPMPSockets(int * sockets) |
int OpenAndConfNATPMPSockets(int * sockets) |
{ |
{ |
int i, j; |
int i, j; |
for(i=0; i<n_lan_addr; i++) | struct lan_addr_s * lan_addr; |
| for(i = 0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++) |
{ |
{ |
sockets[i] = OpenAndConfNATPMPSocket(lan_addr[i].addr.s_addr); | sockets[i] = OpenAndConfNATPMPSocket(lan_addr->addr.s_addr); |
if(sockets[i] < 0) |
if(sockets[i] < 0) |
{ |
{ |
for(j=0; j<i; j++) |
for(j=0; j<i; j++) |
Line 73 static void FillPublicAddressResponse(unsigned char *
|
Line 88 static void FillPublicAddressResponse(unsigned char *
|
{ |
{ |
#ifndef MULTIPLE_EXTERNAL_IP |
#ifndef MULTIPLE_EXTERNAL_IP |
char tmp[16]; |
char tmp[16]; |
|
UNUSED(senderaddr); |
|
|
if(use_ext_ip_addr) { |
if(use_ext_ip_addr) { |
inet_pton(AF_INET, use_ext_ip_addr, resp+8); |
inet_pton(AF_INET, use_ext_ip_addr, resp+8); |
Line 85 static void FillPublicAddressResponse(unsigned char *
|
Line 101 static void FillPublicAddressResponse(unsigned char *
|
resp[3] = 3; /* Network Failure (e.g. NAT box itself |
resp[3] = 3; /* Network Failure (e.g. NAT box itself |
* has not obtained a DHCP lease) */ |
* has not obtained a DHCP lease) */ |
} else { |
} else { |
inet_pton(AF_INET, tmp, resp+8); | inet_pton(AF_INET, tmp, resp+8); /* ok */ |
} |
} |
} |
} |
#else |
#else |
int i; | struct lan_addr_s * lan_addr; |
|
|
for(i = 0; i<n_lan_addr; i++) { | for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { |
if( (senderaddr & lan_addr[i].mask.s_addr) | if( (senderaddr & lan_addr->mask.s_addr) |
== (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr)) { | == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) { |
memcpy(resp+8, &lan_addr[i].ext_ip_addr, | memcpy(resp+8, &lan_addr->ext_ip_addr, |
sizeof(lan_addr[i].ext_ip_addr)); | sizeof(lan_addr->ext_ip_addr)); |
break; |
break; |
} |
} |
} |
} |
Line 117 void ProcessIncomingNATPMPPacket(int s)
|
Line 133 void ProcessIncomingNATPMPPacket(int s)
|
n = recvfrom(s, req, sizeof(req), 0, |
n = recvfrom(s, req, sizeof(req), 0, |
(struct sockaddr *)&senderaddr, &senderaddrlen); |
(struct sockaddr *)&senderaddr, &senderaddrlen); |
if(n<0) { |
if(n<0) { |
syslog(LOG_ERR, "recvfrom(natpmp): %m"); | /* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time) |
| * other errors : log to LOG_ERR */ |
| if(errno != EAGAIN && |
| errno != EWOULDBLOCK && |
| errno != EINTR) { |
| syslog(LOG_ERR, "recvfrom(natpmp): %m"); |
| } |
return; |
return; |
} |
} |
if(!inet_ntop(AF_INET, &senderaddr.sin_addr, |
if(!inet_ntop(AF_INET, &senderaddr.sin_addr, |
Line 138 void ProcessIncomingNATPMPPacket(int s)
|
Line 160 void ProcessIncomingNATPMPPacket(int s)
|
memset(resp, 0, sizeof(resp)); |
memset(resp, 0, sizeof(resp)); |
resplen = 8; |
resplen = 8; |
resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */ |
resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */ |
/* setting response TIME STAMP */ | /* setting response TIME STAMP : |
| * time elapsed since its port mapping table was initialized on |
| * startup or reset for any other reason */ |
*((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time); |
*((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time); |
if(req[0] > 0) { |
if(req[0] > 0) { |
/* invalid version */ |
/* invalid version */ |
Line 161 void ProcessIncomingNATPMPPacket(int s)
|
Line 185 void ProcessIncomingNATPMPPacket(int s)
|
int proto; |
int proto; |
char iaddr_old[16]; |
char iaddr_old[16]; |
unsigned short iport_old; |
unsigned short iport_old; |
|
unsigned int timestamp; |
|
|
iport = ntohs(*((uint16_t *)(req+4))); |
iport = ntohs(*((uint16_t *)(req+4))); |
eport = ntohs(*((uint16_t *)(req+6))); |
eport = ntohs(*((uint16_t *)(req+6))); |
lifetime = ntohl(*((uint32_t *)(req+8))); |
lifetime = ntohl(*((uint32_t *)(req+8))); |
Line 185 void ProcessIncomingNATPMPPacket(int s)
|
Line 211 void ProcessIncomingNATPMPPacket(int s)
|
while(get_redirect_rule_by_index(index, 0, |
while(get_redirect_rule_by_index(index, 0, |
&eport2, iaddr2, sizeof(iaddr2), |
&eport2, iaddr2, sizeof(iaddr2), |
&iport2, &proto2, |
&iport2, &proto2, |
desc, sizeof(desc), 0, 0) >= 0) { | desc, sizeof(desc), |
| 0, 0, ×tamp, 0, 0) >= 0) { |
syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'", |
syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'", |
index, proto2, eport2, iaddr2, iport2, desc); |
index, proto2, eport2, iaddr2, iport2, desc); |
if(0 == strcmp(iaddr2, senderaddrstr) |
if(0 == strcmp(iaddr2, senderaddrstr) |
&& 0 == memcmp(desc, "NAT-PMP ", 8)) { | && 0 == memcmp(desc, "NAT-PMP", 7)) { |
r = _upnp_delete_redir(eport2, proto2); |
r = _upnp_delete_redir(eport2, proto2); |
/* TODO : check return value */ |
/* TODO : check return value */ |
if(r<0) { |
if(r<0) { |
syslog(LOG_ERR, "failed to remove port mapping"); |
syslog(LOG_ERR, "failed to remove port mapping"); |
index++; |
index++; |
} else { |
} else { |
syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed", proto2==IPPROTO_TCP?"TCP":"UDP", eport2); | syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed", |
| proto2==IPPROTO_TCP?"TCP":"UDP", eport2); |
} |
} |
} else { |
} else { |
index++; |
index++; |
Line 204 void ProcessIncomingNATPMPPacket(int s)
|
Line 232 void ProcessIncomingNATPMPPacket(int s)
|
} |
} |
} else { |
} else { |
/* To improve the interworking between nat-pmp and |
/* To improve the interworking between nat-pmp and |
* UPnP, we should check that we remove only NAT-PMP | * UPnP, we should check that we remove only NAT-PMP |
* mappings */ |
* mappings */ |
r = _upnp_delete_redir(eport, proto); |
r = _upnp_delete_redir(eport, proto); |
/*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/ |
/*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/ |
if(r<0) { |
if(r<0) { |
syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s", eport, (proto==IPPROTO_TCP)?"TCP":"UDP"); | syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s", |
| eport, (proto==IPPROTO_TCP)?"TCP":"UDP"); |
resp[3] = 2; /* Not Authorized/Refused */ |
resp[3] = 2; /* Not Authorized/Refused */ |
} |
} |
} |
} |
Line 220 void ProcessIncomingNATPMPPacket(int s)
|
Line 249 void ProcessIncomingNATPMPPacket(int s)
|
} else do { |
} else do { |
r = get_redirect_rule(ext_if_name, eport, proto, |
r = get_redirect_rule(ext_if_name, eport, proto, |
iaddr_old, sizeof(iaddr_old), |
iaddr_old, sizeof(iaddr_old), |
&iport_old, 0, 0, 0, 0); | &iport_old, 0, 0, 0, 0, |
| ×tamp, 0, 0); |
if(r==0) { |
if(r==0) { |
if(strcmp(senderaddrstr, iaddr_old)==0 |
if(strcmp(senderaddrstr, iaddr_old)==0 |
&& iport==iport_old) { |
&& iport==iport_old) { |
/* redirection allready existing */ |
/* redirection allready existing */ |
syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing", eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old); | syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing", |
| eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old); |
/* remove and then add again */ |
/* remove and then add again */ |
if(_upnp_delete_redir(eport, proto) < 0) { |
if(_upnp_delete_redir(eport, proto) < 0) { |
syslog(LOG_ERR, "failed to remove port mapping"); |
syslog(LOG_ERR, "failed to remove port mapping"); |
Line 238 void ProcessIncomingNATPMPPacket(int s)
|
Line 269 void ProcessIncomingNATPMPPacket(int s)
|
} |
} |
{ /* do the redirection */ |
{ /* do the redirection */ |
char desc[64]; |
char desc[64]; |
unsigned timestamp = (unsigned)(time(NULL) - startup_time) | #if 0 |
| timestamp = (unsigned)(time(NULL) - startup_time) |
+ lifetime; |
+ lifetime; |
snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp); |
snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp); |
|
#else |
|
timestamp = time(NULL) + lifetime; |
|
snprintf(desc, sizeof(desc), "NAT-PMP %hu %s", |
|
eport, (proto==IPPROTO_TCP)?"tcp":"udp"); |
|
#endif |
/* TODO : check return code */ |
/* TODO : check return code */ |
if(upnp_redirect_internal(eport, senderaddrstr, | if(upnp_redirect_internal(NULL, eport, senderaddrstr, |
iport, proto, desc) < 0) { | iport, proto, desc, |
| timestamp) < 0) { |
syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'", |
syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'", |
eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc); |
eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc); |
resp[3] = 3; /* Failure */ |
resp[3] = 3; /* Failure */ |
|
#if 0 |
} else if( !nextnatpmptoclean_eport |
} else if( !nextnatpmptoclean_eport |
|| timestamp < nextnatpmptoclean_timestamp) { |
|| timestamp < nextnatpmptoclean_timestamp) { |
nextnatpmptoclean_timestamp = timestamp; |
nextnatpmptoclean_timestamp = timestamp; |
nextnatpmptoclean_eport = eport; |
nextnatpmptoclean_eport = eport; |
nextnatpmptoclean_proto = proto; |
nextnatpmptoclean_proto = proto; |
|
#endif |
} |
} |
break; |
break; |
} |
} |
} while(r==0); |
} while(r==0); |
*((uint16_t *)(resp+8)) = htons(iport); /* private port */ |
*((uint16_t *)(resp+8)) = htons(iport); /* private port */ |
*((uint16_t *)(resp+10)) = htons(eport); /* public port */ |
*((uint16_t *)(resp+10)) = htons(eport); /* public port */ |
*((uint32_t *)(resp+12)) = htonl(lifetime); | *((uint32_t *)(resp+12)) = htonl(lifetime); /* Port Mapping lifetime */ |
} |
} |
resplen = 16; |
resplen = 16; |
break; |
break; |
Line 275 void ProcessIncomingNATPMPPacket(int s)
|
Line 315 void ProcessIncomingNATPMPPacket(int s)
|
} |
} |
} |
} |
|
|
|
#if 0 |
/* iterate through the redirection list to find those who came |
/* iterate through the redirection list to find those who came |
* from NAT-PMP and select the first to expire */ |
* from NAT-PMP and select the first to expire */ |
int ScanNATPMPforExpiration() |
int ScanNATPMPforExpiration() |
Line 289 int ScanNATPMPforExpiration()
|
Line 330 int ScanNATPMPforExpiration()
|
for(i = 0; ; i++) { |
for(i = 0; ; i++) { |
r = get_redirect_rule_by_index(i, 0, &eport, 0, 0, |
r = get_redirect_rule_by_index(i, 0, &eport, 0, 0, |
&iport, &proto, desc, sizeof(desc), |
&iport, &proto, desc, sizeof(desc), |
0, 0); | ×tamp, 0, 0); |
if(r<0) |
if(r<0) |
break; |
break; |
if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { |
if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { |
Line 315 int CleanExpiredNATPMP()
|
Line 356 int CleanExpiredNATPMP()
|
if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport, |
if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport, |
nextnatpmptoclean_proto, |
nextnatpmptoclean_proto, |
0, 0, |
0, 0, |
&iport, desc, sizeof(desc), 0, 0) < 0) | &iport, desc, sizeof(desc), ×tamp, 0, 0) < 0) |
return ScanNATPMPforExpiration(); |
return ScanNATPMPforExpiration(); |
/* check desc - this is important since we keep expiration time as part |
/* check desc - this is important since we keep expiration time as part |
* of the desc. |
* of the desc. |
* If the rule is renewed, timestamp and nextnatpmptoclean_timestamp | * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp |
* can be different. In that case, the rule must not be removed ! */ |
* can be different. In that case, the rule must not be removed ! */ |
if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { |
if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { |
if(timestamp > nextnatpmptoclean_timestamp) |
if(timestamp > nextnatpmptoclean_timestamp) |
Line 333 int CleanExpiredNATPMP()
|
Line 374 int CleanExpiredNATPMP()
|
nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP"); |
nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP"); |
return ScanNATPMPforExpiration(); |
return ScanNATPMPforExpiration(); |
} |
} |
|
#endif |
|
|
/* SendNATPMPPublicAddressChangeNotification() |
/* SendNATPMPPublicAddressChangeNotification() |
* should be called when the public IP address changed */ |
* should be called when the public IP address changed */ |
Line 342 void SendNATPMPPublicAddressChangeNotification(int * s
|
Line 384 void SendNATPMPPublicAddressChangeNotification(int * s
|
unsigned char notif[12]; |
unsigned char notif[12]; |
int j, n; |
int j, n; |
|
|
notif[0] = 0; | notif[0] = 0; /* vers */ |
notif[1] = 128; | notif[1] = 128; /* OP code */ |
notif[2] = 0; | notif[2] = 0; /* result code */ |
notif[3] = 0; | notif[3] = 0; /* result code */ |
| /* seconds since "start of epoch" : |
| * time elapsed since the port mapping table was initialized on |
| * startup or reset for any other reason */ |
*((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time); |
*((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time); |
#ifndef MULTIPLE_EXTERNAL_IP |
#ifndef MULTIPLE_EXTERNAL_IP |
FillPublicAddressResponse(notif, 0); |
FillPublicAddressResponse(notif, 0); |
Line 358 void SendNATPMPPublicAddressChangeNotification(int * s
|
Line 403 void SendNATPMPPublicAddressChangeNotification(int * s
|
#endif |
#endif |
memset(&sockname, 0, sizeof(struct sockaddr_in)); |
memset(&sockname, 0, sizeof(struct sockaddr_in)); |
sockname.sin_family = AF_INET; |
sockname.sin_family = AF_INET; |
sockname.sin_port = htons(NATPMP_PORT); |
|
sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR); |
sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR); |
|
|
for(j=0; j<n_sockets; j++) |
for(j=0; j<n_sockets; j++) |
Line 366 void SendNATPMPPublicAddressChangeNotification(int * s
|
Line 410 void SendNATPMPPublicAddressChangeNotification(int * s
|
if(sockets[j] < 0) |
if(sockets[j] < 0) |
continue; |
continue; |
#ifdef MULTIPLE_EXTERNAL_IP |
#ifdef MULTIPLE_EXTERNAL_IP |
FillPublicAddressResponse(notif, lan_addr[j].addr.s_addr); | { |
| struct lan_addr_s * lan_addr = lan_addrs.lh_first; |
| int i; |
| for(i=0; i<j; i++) |
| lan_addr = lan_addr->list.le_next; |
| FillPublicAddressResponse(notif, lan_addr->addr.s_addr); |
| } |
#endif |
#endif |
|
/* Port to use in 2006 version of the NAT-PMP specification */ |
|
sockname.sin_port = htons(NATPMP_PORT); |
n = sendto(sockets[j], notif, 12, 0, |
n = sendto(sockets[j], notif, 12, 0, |
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in)); |
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in)); |
if(n < 0) |
if(n < 0) |
{ | { |
| syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m", |
| "SendNATPMPPublicAddressChangeNotification", sockets[j]); |
| return; |
| } |
| /* Port to use in 2008 version of the NAT-PMP specification */ |
| sockname.sin_port = htons(NATPMP_NOTIF_PORT); |
| n = sendto(sockets[j], notif, 12, 0, |
| (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)); |
| if(n < 0) |
| { |
syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m", |
syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m", |
"SendNATPMPPublicAddressChangeNotification", sockets[j]); |
"SendNATPMPPublicAddressChangeNotification", sockets[j]); |
return; |
return; |