miniupnpd: Some initial effort at actually adding pinhole support to PCP code.

This commit is contained in:
Markus Stenberg 2014-05-10 14:30:58 +02:00 committed by Thomas Bernard
parent c000a00508
commit e907d7bba6
3 changed files with 251 additions and 198 deletions

View File

@ -75,6 +75,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include "upnputils.h" #include "upnputils.h"
#include "portinuse.h" #include "portinuse.h"
#include "pcp_msg_struct.h" #include "pcp_msg_struct.h"
#ifdef ENABLE_UPNPPINHOLE
#include "upnppinhole.h"
#endif /* ENABLE_UPNPPINHOLE */
#ifdef PCP_PEER #ifdef PCP_PEER
/* TODO make this platform independent */ /* TODO make this platform independent */
@ -131,8 +135,8 @@ typedef struct pcp_info {
uint8_t is_peer_op; uint8_t is_peer_op;
const struct in6_addr *thirdp_ip; const struct in6_addr *thirdp_ip;
const struct in6_addr *mapped_ip; const struct in6_addr *mapped_ip;
char mapped_str[INET_ADDRSTRLEN];
int pfailure_present; int pfailure_present;
char senderaddrstr[48]; /* can be either IPv4 or IPv6 */
struct in6_addr sender_ip; struct in6_addr sender_ip;
int is_fw; /* is this firewall operation? if not, nat. */ int is_fw; /* is this firewall operation? if not, nat. */
char desc[64]; char desc[64];
@ -691,6 +695,10 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
uint16_t eport = pcp_msg_info->ext_port; /* public port */ uint16_t eport = pcp_msg_info->ext_port; /* public port */
char peerip_s[INET_ADDRSTRLEN], extip_s[INET_ADDRSTRLEN];
time_t timestamp = time(NULL) + pcp_msg_info->lifetime;
int r = -1;
FillSA((struct sockaddr*)&intip, pcp_msg_info->mapped_ip, FillSA((struct sockaddr*)&intip, pcp_msg_info->mapped_ip,
pcp_msg_info->int_port); pcp_msg_info->int_port);
FillSA((struct sockaddr*)&peerip, pcp_msg_info->peer_ip, FillSA((struct sockaddr*)&peerip, pcp_msg_info->peer_ip,
@ -698,6 +706,22 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
FillSA((struct sockaddr*)&extip, pcp_msg_info->ext_ip, FillSA((struct sockaddr*)&extip, pcp_msg_info->ext_ip,
eport); eport);
inet_satop((struct sockaddr*)&peerip, peerip_s, sizeof(peerip_s));
inet_satop((struct sockaddr*)&extip, extip_s, sizeof(extip_s));
if (pcp_msg_info->is_fw) {
#ifdef ENABLE_UPNPPINHOLE
eport = pcp_msg_info->int_port;
r = upnp_add_inboundpinhole(peerip_s,
pcp_msg_info->peer_port,
pcp_msg_info->mapped_str,
eport,
proto,
pcp_msg_info->desc,
0, NULL);
#endif /* ENABLE_UPNPPINHOLE */
} else {
/* check if connection with given peer exists, if it was */ /* check if connection with given peer exists, if it was */
/* already established use this external port */ /* already established use this external port */
if (get_nat_ext_addr( (struct sockaddr*)&intip, (struct sockaddr*)&peerip, if (get_nat_ext_addr( (struct sockaddr*)&intip, (struct sockaddr*)&peerip,
@ -716,63 +740,60 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
} }
} }
/* Create Peer Mapping */ /* Create Peer Mapping */
{
char peerip_s[INET_ADDRSTRLEN], extip_s[INET_ADDRSTRLEN];
time_t timestamp = time(NULL) + pcp_msg_info->lifetime;
if (eport == 0) { if (eport == 0) {
eport = pcp_msg_info->int_port; eport = pcp_msg_info->int_port;
} }
inet_satop((struct sockaddr*)&peerip, peerip_s, sizeof(peerip_s));
inet_satop((struct sockaddr*)&extip, extip_s, sizeof(extip_s));
#ifdef PCP_FLOWP #ifdef PCP_FLOWP
if (pcp_msg_info->flowp_present && pcp_msg_info->dscp_up) { if (pcp_msg_info->flowp_present && pcp_msg_info->dscp_up) {
if (add_peer_dscp_rule2(ext_if_name, peerip_s, if (add_peer_dscp_rule2(ext_if_name, peerip_s,
pcp_msg_info->peer_port, pcp_msg_info->dscp_up, pcp_msg_info->peer_port, pcp_msg_info->dscp_up,
pcp_msg_info->senderaddrstr, pcp_msg_info->int_port, pcp_msg_info->mapped_str, pcp_msg_info->int_port,
proto, pcp_msg_info->desc, timestamp) < 0 ) { proto, pcp_msg_info->desc, timestamp) < 0 ) {
syslog(LOG_ERR, "PCP: failed to add flowp upstream mapping %s:%hu->%s:%hu '%s'", syslog(LOG_ERR, "PCP: failed to add flowp upstream mapping %s:%hu->%s:%hu '%s'",
pcp_msg_info->senderaddrstr, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->int_port,
peerip_s, peerip_s,
pcp_msg_info->peer_port, pcp_msg_info->peer_port,
pcp_msg_info->desc); pcp_msg_info->desc);
pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES;
return 0;
} }
} }
if (pcp_msg_info->flowp_present && pcp_msg_info->dscp_down) { if (pcp_msg_info->flowp_present && pcp_msg_info->dscp_down) {
if (add_peer_dscp_rule2(ext_if_name, pcp_msg_info->senderaddrstr, if (add_peer_dscp_rule2(ext_if_name, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->dscp_down, pcp_msg_info->int_port, pcp_msg_info->dscp_down,
peerip_s, pcp_msg_info->peer_port, proto, pcp_msg_info->desc, timestamp) peerip_s, pcp_msg_info->peer_port, proto, pcp_msg_info->desc, timestamp)
< 0 ) { < 0 ) {
syslog(LOG_ERR, "PCP: failed to add flowp downstream mapping %s:%hu->%s:%hu '%s'", syslog(LOG_ERR, "PCP: failed to add flowp downstream mapping %s:%hu->%s:%hu '%s'",
pcp_msg_info->senderaddrstr, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->int_port,
peerip_s, peerip_s,
pcp_msg_info->peer_port, pcp_msg_info->peer_port,
pcp_msg_info->desc); pcp_msg_info->desc);
pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES;
return 0;
} }
} }
#endif #endif
/* TODO: add upnp function for PI */
if (add_peer_redirect_rule2(ext_if_name, r = add_peer_redirect_rule2(ext_if_name,
peerip_s, peerip_s,
pcp_msg_info->peer_port, pcp_msg_info->peer_port,
extip_s, extip_s,
eport, eport,
pcp_msg_info->senderaddrstr, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->int_port,
pcp_msg_info->protocol, pcp_msg_info->protocol,
pcp_msg_info->desc, pcp_msg_info->desc,
timestamp) < 0 ) { timestamp);
}
/* TODO: add upnp function for PI */
if (r < 0 ) {
syslog(LOG_ERR, "PCP PEER: failed to add peer mapping %s %s:%hu(%hu)->%s:%hu '%s'", syslog(LOG_ERR, "PCP PEER: failed to add peer mapping %s %s:%hu(%hu)->%s:%hu '%s'",
(pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP",
pcp_msg_info->senderaddrstr, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->int_port,
eport, eport,
peerip_s, peerip_s,
@ -786,14 +807,13 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
pcp_msg_info->ext_port = eport; pcp_msg_info->ext_port = eport;
syslog(LOG_INFO, "PCP PEER: added mapping %s %s:%hu(%hu)->%s:%hu '%s'", syslog(LOG_INFO, "PCP PEER: added mapping %s %s:%hu(%hu)->%s:%hu '%s'",
(pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP",
pcp_msg_info->senderaddrstr, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->int_port,
eport, eport,
peerip_s, peerip_s,
pcp_msg_info->peer_port, pcp_msg_info->peer_port,
pcp_msg_info->desc); pcp_msg_info->desc);
} }
}
return 1; return 1;
} }
@ -813,33 +833,47 @@ static void DeletePCPPeer(pcp_info_t *pcp_msg_info)
int proto2; int proto2;
char desc[64]; char desc[64];
unsigned int timestamp; unsigned int timestamp;
int uid;
inet_n46top((struct in6_addr*)pcp_msg_info->peer_ip, rhost, sizeof(rhost)); inet_n46top((struct in6_addr*)pcp_msg_info->peer_ip, rhost, sizeof(rhost));
while(get_peer_rule_by_index(index, 0, for (index = 0 ;
(!pcp_msg_info->is_fw &&
get_peer_rule_by_index(index, 0,
&eport2, iaddr2, sizeof(iaddr2), &eport2, iaddr2, sizeof(iaddr2),
&iport2, &proto2, &iport2, &proto2,
desc, sizeof(desc), desc, sizeof(desc),
rhost2, sizeof(rhost2), &rport2, &timestamp, 0, 0) >= 0) { rhost2, sizeof(rhost2), &rport2,
if((0 == strncmp(iaddr2, pcp_msg_info->senderaddrstr, sizeof(iaddr2))) &timestamp, 0, 0) >= 0)
||
(pcp_msg_info->is_fw &&
(uid=upnp_get_pinhole_uid_by_index(index))>=0 &&
upnp_get_pinhole_info((unsigned short)uid,
rhost2, sizeof(rhost2), &rport2,
iaddr2, sizeof(iaddr2), &iport2,
&proto2, desc, sizeof(desc),
&timestamp, NULL) > 0) ;
index++) {
if((0 == strncmp(iaddr2, pcp_msg_info->mapped_str, sizeof(iaddr2)))
&& (0 == strncmp(rhost2, rhost, sizeof(rhost2))) && (0 == strncmp(rhost2, rhost, sizeof(rhost2)))
&& (proto2==proto) && (proto2==proto)
&& 0 == strcmp(desc, pcp_msg_info->desc) && 0 == strcmp(desc, pcp_msg_info->desc)
&& (iport2==iport) && (rport2==rport)) { && (iport2==iport) && (rport2==rport)) {
if (!pcp_msg_info->is_fw)
r = _upnp_delete_redir(eport2, proto2); r = _upnp_delete_redir(eport2, proto2);
else
r = upnp_delete_inboundpinhole(uid);
if(r<0) { if(r<0) {
syslog(LOG_ERR, "PCP PEER: failed to remove peer mapping"); syslog(LOG_ERR, "PCP PEER: failed to remove peer mapping");
index++;
} else { } else {
syslog(LOG_INFO, "PCP PEER: %s port %hu peer mapping removed", syslog(LOG_INFO, "PCP PEER: %s port %hu peer mapping removed",
proto2==IPPROTO_TCP?"TCP":"UDP", eport2); proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
} }
} else { return;
index++;
} }
} }
if (r==-1) { if (r==-1) {
syslog(LOG_ERR, "PCP PEER: Failed to remove PCP mapping internal port %hu, protocol %s", syslog(LOG_ERR, "PCP PEER: Failed to find PCP mapping internal port %hu, protocol %s",
iport, (pcp_msg_info->protocol == IPPROTO_TCP)?"TCP":"UDP"); iport, (pcp_msg_info->protocol == IPPROTO_TCP)?"TCP":"UDP");
pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES;
} }
@ -880,9 +914,20 @@ static void CreatePCPMap(pcp_info_t *pcp_msg_info)
uint16_t iport_old; uint16_t iport_old;
uint16_t eport_first = 0; uint16_t eport_first = 0;
int any_eport_allowed = 0; int any_eport_allowed = 0;
unsigned int timestamp; unsigned int timestamp = time(NULL) + pcp_msg_info->lifetime;
int r=0; int r=-1;
if (pcp_msg_info->is_fw) {
#ifdef ENABLE_UPNPPINHOLE
r = upnp_add_inboundpinhole(NULL, 0,
pcp_msg_info->mapped_str,
pcp_msg_info->int_port,
pcp_msg_info->protocol,
pcp_msg_info->desc,
timestamp,
NULL);
#endif /* ENABLE_UPNPPINHOLE */
} else {
if (pcp_msg_info->ext_port == 0) { if (pcp_msg_info->ext_port == 0) {
pcp_msg_info->ext_port = pcp_msg_info->int_port; pcp_msg_info->ext_port = pcp_msg_info->int_port;
} }
@ -915,7 +960,7 @@ static void CreatePCPMap(pcp_info_t *pcp_msg_info)
any_eport_allowed = 1; any_eport_allowed = 1;
#ifdef CHECK_PORTINUSE #ifdef CHECK_PORTINUSE
if (port_in_use(ext_if_name, pcp_msg_info->ext_port, pcp_msg_info->protocol, if (port_in_use(ext_if_name, pcp_msg_info->ext_port, pcp_msg_info->protocol,
pcp_msg_info->senderaddrstr, pcp_msg_info->int_port) > 0) { pcp_msg_info->mapped_str, pcp_msg_info->int_port) > 0) {
syslog(LOG_INFO, "port %hu protocol %s already in use", syslog(LOG_INFO, "port %hu protocol %s already in use",
pcp_msg_info->ext_port, pcp_msg_info->ext_port,
(pcp_msg_info->protocol==IPPROTO_TCP)?"tcp":"udp"); (pcp_msg_info->protocol==IPPROTO_TCP)?"tcp":"udp");
@ -934,7 +979,7 @@ static void CreatePCPMap(pcp_info_t *pcp_msg_info)
&timestamp, 0, 0); &timestamp, 0, 0);
if(r==0) { if(r==0) {
if((strncmp(pcp_msg_info->senderaddrstr, iaddr_old, if((strncmp(pcp_msg_info->mapped_str, iaddr_old,
sizeof(iaddr_old))!=0) sizeof(iaddr_old))!=0)
|| (pcp_msg_info->int_port != iport_old)) { || (pcp_msg_info->int_port != iport_old)) {
/* redirection already existing */ /* redirection already existing */
@ -962,19 +1007,19 @@ static void CreatePCPMap(pcp_info_t *pcp_msg_info)
} }
} while (r==0); } while (r==0);
timestamp = time(NULL) + pcp_msg_info->lifetime; r = upnp_redirect_internal(NULL,
if(upnp_redirect_internal(NULL,
pcp_msg_info->ext_port, pcp_msg_info->ext_port,
pcp_msg_info->senderaddrstr, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->int_port,
pcp_msg_info->protocol, pcp_msg_info->protocol,
pcp_msg_info->desc, pcp_msg_info->desc,
timestamp) < 0) { timestamp);
}
if(r < 0) {
syslog(LOG_ERR, "PCP MAP: Failed to add mapping %hu->%s:%hu '%s'", syslog(LOG_ERR, "PCP MAP: Failed to add mapping %hu->%s:%hu '%s'",
pcp_msg_info->ext_port, pcp_msg_info->ext_port,
pcp_msg_info->senderaddrstr, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->int_port,
pcp_msg_info->desc); pcp_msg_info->desc);
@ -983,7 +1028,7 @@ static void CreatePCPMap(pcp_info_t *pcp_msg_info)
} else { } else {
syslog(LOG_INFO, "PCP MAP: added mapping %hu->%s:%hu '%s'", syslog(LOG_INFO, "PCP MAP: added mapping %hu->%s:%hu '%s'",
pcp_msg_info->ext_port, pcp_msg_info->ext_port,
pcp_msg_info->senderaddrstr, pcp_msg_info->mapped_str,
pcp_msg_info->int_port, pcp_msg_info->int_port,
pcp_msg_info->desc); pcp_msg_info->desc);
} }
@ -1010,7 +1055,7 @@ static void DeletePCPMap(pcp_info_t *pcp_msg_info)
desc, sizeof(desc), desc, sizeof(desc),
0, 0, &timestamp, 0, 0) >= 0) { 0, 0, &timestamp, 0, 0) >= 0) {
if(0 == strncmp(iaddr2, pcp_msg_info->senderaddrstr, sizeof(iaddr2)) if(0 == strncmp(iaddr2, pcp_msg_info->mapped_str, sizeof(iaddr2))
&& (proto2==proto) && (proto2==proto)
&& 0 == strcmp(desc, pcp_msg_info->desc) && 0 == strcmp(desc, pcp_msg_info->desc)
&& ((iport2==iport) || (iport==0))) { && ((iport2==iport) || (iport==0))) {
@ -1065,6 +1110,22 @@ static int ValidatePCPMsg(pcp_info_t *pcp_msg_info)
} }
} }
/* Produce mapped_str for future use. */
if (IN6_IS_ADDR_V4MAPPED(pcp_msg_info->mapped_ip)) {
if (!inet_ntop(AF_INET,
&((uint32_t *)pcp_msg_info->mapped_ip)[3],
pcp_msg_info->mapped_str,
sizeof(pcp_msg_info->mapped_str))) {
syslog(LOG_ERR, "inet_ntop(pcpserver): %m");
return 0;
}
} else if(!inet_ntop(AF_INET6, pcp_msg_info->mapped_ip,
pcp_msg_info->mapped_str,
sizeof(pcp_msg_info->mapped_str))) {
syslog(LOG_ERR, "inet_ntop(pcpserver): %m");
return 0;
}
/* protocol zero means 'all protocols' : internal port MUST be zero */ /* protocol zero means 'all protocols' : internal port MUST be zero */
if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port != 0) { if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port != 0) {
pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST;
@ -1441,25 +1502,15 @@ int ProcessIncomingPCPPacket(int s, unsigned char *buff, int len,
memset(&pcp_msg_info, 0, sizeof(pcp_info_t)); memset(&pcp_msg_info, 0, sizeof(pcp_info_t));
if(senderaddr->sa_family == AF_INET) { if(senderaddr->sa_family == AF_INET) {
const struct sockaddr_in * senderaddr_v4; const struct sockaddr_in * senderaddr_v4 =
senderaddr_v4 = (const struct sockaddr_in *)senderaddr; (const struct sockaddr_in *)senderaddr;
if(!inet_ntop(AF_INET, &senderaddr_v4->sin_addr,
pcp_msg_info.senderaddrstr,
sizeof(pcp_msg_info.senderaddrstr))) {
syslog(LOG_ERR, "inet_ntop(pcpserver): %m");
}
pcp_msg_info.sender_ip.s6_addr[11] = 0xff; pcp_msg_info.sender_ip.s6_addr[11] = 0xff;
pcp_msg_info.sender_ip.s6_addr[10] = 0xff; pcp_msg_info.sender_ip.s6_addr[10] = 0xff;
memcpy(pcp_msg_info.sender_ip.s6_addr+12, memcpy(pcp_msg_info.sender_ip.s6_addr+12,
&senderaddr_v4->sin_addr, 4); &senderaddr_v4->sin_addr, 4);
} else if(senderaddr->sa_family == AF_INET6) { } else if(senderaddr->sa_family == AF_INET6) {
const struct sockaddr_in6 * senderaddr_v6; const struct sockaddr_in6 * senderaddr_v6 =
senderaddr_v6 = (const struct sockaddr_in6 *)senderaddr; (const struct sockaddr_in6 *)senderaddr;
if(!inet_ntop(AF_INET6, &senderaddr_v6->sin6_addr,
pcp_msg_info.senderaddrstr,
sizeof(pcp_msg_info.senderaddrstr))) {
syslog(LOG_ERR, "inet_ntop(pcpserver): %m");
}
pcp_msg_info.sender_ip = senderaddr_v6->sin6_addr; pcp_msg_info.sender_ip = senderaddr_v6->sin6_addr;
} else { } else {
syslog(LOG_WARNING, "unknown PCP packet sender address family %d", syslog(LOG_WARNING, "unknown PCP packet sender address family %d",

View File

@ -100,6 +100,7 @@ upnp_add_inboundpinhole(const char * raddr,
const char * iaddr, const char * iaddr,
unsigned short iport, unsigned short iport,
int proto, int proto,
char * desc,
unsigned int leasetime, unsigned int leasetime,
int * uid) int * uid)
{ {
@ -130,7 +131,7 @@ upnp_add_inboundpinhole(const char * raddr,
{ {
#if defined(USE_PF) || defined(USE_NETFILTER) #if defined(USE_PF) || defined(USE_NETFILTER)
*uid = add_pinhole (0/*ext_if_name*/, raddr, rport, *uid = add_pinhole (0/*ext_if_name*/, raddr, rport,
iaddr, iport, proto, ""/*desc*/, timestamp); iaddr, iport, proto, desc, timestamp);
return 1; return 1;
#else #else
return -42; /* not implemented */ return -42; /* not implemented */

View File

@ -28,7 +28,8 @@ upnp_check_outbound_pinhole(int proto, int * timeout);
int int
upnp_add_inboundpinhole(const char * raddr, unsigned short rport, upnp_add_inboundpinhole(const char * raddr, unsigned short rport,
const char * iaddr, unsigned short iport, const char * iaddr, unsigned short iport,
int proto, unsigned int leasetime, int * uid); int proto, char * desc,
unsigned int leasetime, int * uid);
/* /*
* return values : * return values :