miniupnpd: work in progress on PCP pinhole support

This commit is contained in:
Markus Stenberg 2014-05-10 15:33:34 +02:00 committed by Thomas Bernard
parent 7c7407099e
commit be6db5995d
2 changed files with 108 additions and 91 deletions

View File

@ -93,6 +93,7 @@ struct pcp_server_info {
/* default server settings, highest version supported is the default */ /* default server settings, highest version supported is the default */
static struct pcp_server_info this_server_info = {2}; static struct pcp_server_info this_server_info = {2};
/* structure holding information from PCP msg*/ /* structure holding information from PCP msg*/
/* all variables are in host byte order except IP addresses */ /* all variables are in host byte order except IP addresses */
typedef struct pcp_info { typedef struct pcp_info {
@ -135,7 +136,7 @@ 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]; char mapped_str[INET6_ADDRSTRLEN];
int pfailure_present; int pfailure_present;
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. */
@ -150,8 +151,8 @@ int get_dscp_value(pcp_info_t *pcp_msg_info) {
for (ind = 0; ind < num_dscp_values; ind++) { for (ind = 0; ind < num_dscp_values; ind++) {
if ((dscp_values_list[ind].app_name) && if ((dscp_values_list[ind].app_name) &&
(!strncmp(dscp_values_list[ind].app_name, (!strcmp(dscp_values_list[ind].app_name,
pcp_msg_info->app_name, pcp_msg_info->app_name_len)) && pcp_msg_info->app_name)) &&
(pcp_msg_info->delay_tolerance == dscp_values_list[ind].delay) && (pcp_msg_info->delay_tolerance == dscp_values_list[ind].delay) &&
(pcp_msg_info->loss_tolerance == dscp_values_list[ind].loss) && (pcp_msg_info->loss_tolerance == dscp_values_list[ind].loss) &&
(pcp_msg_info->jitter_tolerance == dscp_values_list[ind].jitter) (pcp_msg_info->jitter_tolerance == dscp_values_list[ind].jitter)
@ -649,6 +650,15 @@ static int CheckExternalAddress(pcp_info_t* pcp_msg_info)
} }
static const char* inet_n46top(struct in6_addr* in, char* buf, size_t buf_len)
{
if (IN6_IS_ADDR_V4MAPPED(in)) {
return inet_ntop(AF_INET, ((uint32_t*)(in->s6_addr))+3, buf, buf_len);
} else {
return inet_ntop(AF_INET6, in, buf, buf_len);
}
}
#ifdef PCP_PEER #ifdef PCP_PEER
static void FillSA(struct sockaddr *sa, const struct in6_addr *in6, static void FillSA(struct sockaddr *sa, const struct in6_addr *in6,
uint16_t port) uint16_t port)
@ -675,16 +685,7 @@ static const char* inet_satop(struct sockaddr* sa, char* buf, size_t buf_len)
} }
} }
static const char* inet_n46top(struct in6_addr* in, char* buf, size_t buf_len) static void CreatePCPPeer(pcp_info_t *pcp_msg_info)
{
if (IN6_IS_ADDR_V4MAPPED(in)) {
return inet_ntop(AF_INET, ((uint32_t*)(in->s6_addr))+3, buf, buf_len);
} else {
return inet_ntop(AF_INET6, in, buf, buf_len);
}
}
static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
{ {
struct sockaddr_storage intip; struct sockaddr_storage intip;
struct sockaddr_storage peerip; struct sockaddr_storage peerip;
@ -695,8 +696,7 @@ 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]; char peerip_s[INET6_ADDRSTRLEN], extip_s[INET6_ADDRSTRLEN];
time_t timestamp = time(NULL) + pcp_msg_info->lifetime;
int r = -1; int r = -1;
FillSA((struct sockaddr*)&intip, pcp_msg_info->mapped_ip, FillSA((struct sockaddr*)&intip, pcp_msg_info->mapped_ip,
@ -710,17 +710,24 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
inet_satop((struct sockaddr*)&extip, extip_s, sizeof(extip_s)); inet_satop((struct sockaddr*)&extip, extip_s, sizeof(extip_s));
if (pcp_msg_info->is_fw) { if (pcp_msg_info->is_fw) {
#if 0
/* Someday, something like this is available.. and we're ready! */
#ifdef ENABLE_UPNPPINHOLE #ifdef ENABLE_UPNPPINHOLE
eport = pcp_msg_info->int_port; eport = pcp_msg_info->int_port;
r = upnp_add_inboundpinhole(peerip_s, r = upnp_add_outbound_pinhole(peerip_s,
pcp_msg_info->peer_port, pcp_msg_info->peer_port,
pcp_msg_info->mapped_str, pcp_msg_info->mapped_str,
eport, eport,
proto, proto,
pcp_msg_info->desc, pcp_msg_info->desc,
0, NULL); pcp_msg_info->lifetime, NULL);
#endif /* ENABLE_UPNPPINHOLE */ #endif /* ENABLE_UPNPPINHOLE */
#else
pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPCODE;
return;
#endif /* 0 */
} else { } else {
time_t timestamp = time(NULL) + pcp_msg_info->lifetime;
/* 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 */
@ -736,7 +743,7 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
eport = ret_eport; eport = ret_eport;
} else { } else {
pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL;
return 0; return;
} }
} }
/* Create Peer Mapping */ /* Create Peer Mapping */
@ -757,7 +764,7 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
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; return;
} }
} }
@ -773,7 +780,7 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
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; return;
} }
} }
#endif #endif
@ -801,8 +808,6 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
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;
} else { } else {
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'",
@ -814,8 +819,6 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info)
pcp_msg_info->peer_port, pcp_msg_info->peer_port,
pcp_msg_info->desc); pcp_msg_info->desc);
} }
return 1;
} }
static void DeletePCPPeer(pcp_info_t *pcp_msg_info) static void DeletePCPPeer(pcp_info_t *pcp_msg_info)
@ -824,7 +827,7 @@ static void DeletePCPPeer(pcp_info_t *pcp_msg_info)
uint16_t rport = pcp_msg_info->peer_port; /* private port */ uint16_t rport = pcp_msg_info->peer_port; /* private port */
uint8_t proto = pcp_msg_info->protocol; uint8_t proto = pcp_msg_info->protocol;
char rhost[INET6_ADDRSTRLEN]; char rhost[INET6_ADDRSTRLEN];
int r=-1; int r = -1;
/* remove requested mappings for this client */ /* remove requested mappings for this client */
int index = 0; int index = 0;
@ -833,7 +836,14 @@ 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;
#if 0
int uid; int uid;
#endif /* 0 */
if (pcp_msg_info->is_fw) {
pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPCODE;
return;
}
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));
@ -845,24 +855,30 @@ static void DeletePCPPeer(pcp_info_t *pcp_msg_info)
desc, sizeof(desc), desc, sizeof(desc),
rhost2, sizeof(rhost2), &rport2, rhost2, sizeof(rhost2), &rport2,
&timestamp, 0, 0) >= 0) &timestamp, 0, 0) >= 0)
|| #if 0
(pcp_msg_info->is_fw && /* Some day if outbound pinholes are supported.. */
(uid=upnp_get_pinhole_uid_by_index(index))>=0 && ||
upnp_get_pinhole_info((unsigned short)uid, (pcp_msg_info->is_fw &&
rhost2, sizeof(rhost2), &rport2, (uid=upnp_get_pinhole_uid_by_index(index))>=0 &&
iaddr2, sizeof(iaddr2), &iport2, upnp_get_pinhole_info((unsigned short)uid,
&proto2, desc, sizeof(desc), rhost2, sizeof(rhost2), &rport2,
&timestamp, NULL) > 0) ; iaddr2, sizeof(iaddr2), &iport2,
index++) { &proto2, desc, sizeof(desc),
if((0 == strncmp(iaddr2, pcp_msg_info->mapped_str, sizeof(iaddr2))) &timestamp, NULL) > 0)
&& (0 == strncmp(rhost2, rhost, sizeof(rhost2))) #endif /* 0 */
;
index++)
if((0 == strcmp(iaddr2, pcp_msg_info->mapped_str))
&& (0 == strcmp(rhost2, rhost))
&& (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) if (!pcp_msg_info->is_fw)
r = _upnp_delete_redir(eport2, proto2); r = _upnp_delete_redir(eport2, proto2);
#if 0
else else
r = upnp_delete_inboundpinhole(uid); r = upnp_delete_outboundpinhole(uid);
#endif /* 0 */
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");
} else { } else {
@ -871,7 +887,6 @@ static void DeletePCPPeer(pcp_info_t *pcp_msg_info)
} }
return; return;
} }
}
if (r==-1) { if (r==-1) {
syslog(LOG_ERR, "PCP PEER: Failed to find 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");
@ -910,7 +925,7 @@ static void DeletePCPPeer(pcp_info_t *pcp_msg_info)
/* TODO : support more scenarios than just NAT44 */ /* TODO : support more scenarios than just NAT44 */
static void CreatePCPMap(pcp_info_t *pcp_msg_info) static void CreatePCPMap(pcp_info_t *pcp_msg_info)
{ {
char iaddr_old[INET_ADDRSTRLEN]; char iaddr_old[INET6_ADDRSTRLEN];
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;
@ -924,7 +939,7 @@ static void CreatePCPMap(pcp_info_t *pcp_msg_info)
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, pcp_msg_info->lifetime,
NULL); NULL);
#endif /* ENABLE_UPNPPINHOLE */ #endif /* ENABLE_UPNPPINHOLE */
} else { } else {
@ -979,8 +994,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->mapped_str, iaddr_old, if((strcmp(pcp_msg_info->mapped_str, 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 */
if (pcp_msg_info->pfailure_present) { if (pcp_msg_info->pfailure_present) {
@ -1041,38 +1055,53 @@ static void DeletePCPMap(pcp_info_t *pcp_msg_info)
int r=-1; int r=-1;
/* remove the mapping */ /* remove the mapping */
/* remove all the mappings for this client */ /* remove all the mappings for this client */
int index = 0; int index;
unsigned short eport2, iport2; unsigned short eport2, iport2;
char iaddr2[16]; char iaddr2[16];
int proto2; int proto2;
char desc[64]; char desc[64];
unsigned int timestamp; unsigned int timestamp;
#ifdef ENABLE_UPNPPINHOLE
int uid = -1;
#endif /* ENABLE_UPNPPINHOLE */
/* iterate through all rules and delete the requested ones */ /* iterate through all rules and delete the requested ones */
while(get_redirect_rule_by_index(index, 0, for (index = 0 ;
&eport2, iaddr2, sizeof(iaddr2), (!pcp_msg_info->is_fw &&
&iport2, &proto2, get_redirect_rule_by_index(index, 0,
desc, sizeof(desc), &eport2, iaddr2, sizeof(iaddr2),
0, 0, &timestamp, 0, 0) >= 0) { &iport2, &proto2,
desc, sizeof(desc),
if(0 == strncmp(iaddr2, pcp_msg_info->mapped_str, sizeof(iaddr2)) 0, 0, &timestamp, 0, 0) >= 0)
&& (proto2==proto) #ifdef ENABLE_UPNPPINHOLE
&& 0 == strcmp(desc, pcp_msg_info->desc) ||
&& ((iport2==iport) || (iport==0))) { (pcp_msg_info->is_fw &&
(uid=upnp_get_pinhole_uid_by_index(index))>=0 &&
r = _upnp_delete_redir(eport2, proto2); upnp_get_pinhole_info((unsigned short)uid,
if(r<0) { NULL, 0, NULL,
syslog(LOG_ERR, "PCP: failed to remove port mapping"); iaddr2, sizeof(iaddr2), &iport2,
index++; &proto2, desc, sizeof(desc),
&timestamp, NULL) > 0)
#endif /* ENABLE_UPNPPINHOLE */
;
index++)
if(0 == strcmp(iaddr2, pcp_msg_info->mapped_str)
&& (proto2==proto)
&& 0 == strcmp(desc, pcp_msg_info->desc)
&& ((iport2==iport) || (iport==0))) {
if (!pcp_msg_info->is_fw) {
r = _upnp_delete_redir(eport2, proto2);
} else { } else {
syslog(LOG_INFO, "PCP: %s port %hu mapping removed", #ifdef ENABLE_UPNPPINHOLE
proto2==IPPROTO_TCP?"TCP":"UDP", eport2); r = upnp_delete_inboundpinhole(uid);
#endif /* ENABLE_UPNPPINHOLE */
} }
} else { break;
index++;
} }
} if (r >= 0) {
if (r==-1) { syslog(LOG_INFO, "PCP: %s port %hu mapping removed",
proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
} else {
syslog(LOG_ERR, "Failed to remove PCP mapping internal port %hu, protocol %s", syslog(LOG_ERR, "Failed to remove 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;
@ -1111,17 +1140,8 @@ static int ValidatePCPMsg(pcp_info_t *pcp_msg_info)
} }
/* Produce mapped_str for future use. */ /* Produce mapped_str for future use. */
if (IN6_IS_ADDR_V4MAPPED(pcp_msg_info->mapped_ip)) { if (!inet_n46top(pcp_msg_info->mapped_ip, pcp_msg_info->mapped_str,
if (!inet_ntop(AF_INET, sizeof(pcp_msg_info->mapped_str))) {
&((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"); syslog(LOG_ERR, "inet_ntop(pcpserver): %m");
return 0; return 0;
} }
@ -1373,6 +1393,7 @@ static int processPCPRequest(void * req, int req_size, pcp_info_t *pcp_msg_info)
} }
sadscp = (pcp_sadscp_req_t*)req; sadscp = (pcp_sadscp_req_t*)req;
req += sizeof(pcp_sadscp_req_t);
if (sadscp->app_name_length > remainingSize) { if (sadscp->app_name_length > remainingSize) {
pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION;
@ -1387,7 +1408,6 @@ static int processPCPRequest(void * req, int req_size, pcp_info_t *pcp_msg_info)
get_dscp_value(pcp_msg_info); get_dscp_value(pcp_msg_info);
processedSize += sizeof(pcp_sadscp_req_t);
break; break;
#endif #endif

View File

@ -128,15 +128,13 @@ upnp_add_inboundpinhole(const char * raddr,
} }
else else
#endif #endif
{
#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 */
#endif #endif
}
} }
#if 0 #if 0
@ -234,8 +232,8 @@ upnp_get_pinhole_info(unsigned short uid,
/*u_int64_t bytes_tmp;*/ /*u_int64_t bytes_tmp;*/
r = get_pinhole_info(uid, raddr, raddrlen, rport, r = get_pinhole_info(uid, raddr, raddrlen, rport,
iaddr, iaddrlen, iport, proto, iaddr, iaddrlen, iport,
desc, desclen, proto, desc, desclen,
leasetime ? &timestamp : NULL, leasetime ? &timestamp : NULL,
packets ? &packets_tmp : NULL, packets ? &packets_tmp : NULL,
NULL/*&bytes_tmp*/); NULL/*&bytes_tmp*/);
@ -266,8 +264,7 @@ int
upnp_get_pinhole_uid_by_index(int index) upnp_get_pinhole_uid_by_index(int index)
{ {
#if defined (USE_NETFILTER) #if defined (USE_NETFILTER)
return get_pinhole_uid_by_index(index);
return -1;
#else #else
UNUSED(index); UNUSED(index);
return -42; return -42;