version 1.1.1.1, 2012/02/21 23:16:02
|
version 1.1.1.2, 2012/05/29 12:55:57
|
Line 1
|
Line 1
|
|
/* $Id$ */ |
/* |
/* |
* MiniUPnP project |
* MiniUPnP project |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* (c) 2009 Jardel Weyrich |
* (c) 2009 Jardel Weyrich |
|
* (c) 2011 Thomas Bernard |
* 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 |
*/ |
*/ |
|
|
|
#include "../config.h" |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/file.h> |
#include <sys/file.h> |
Line 61 struct file;
|
Line 65 struct file;
|
#include <unistd.h> |
#include <unistd.h> |
#include <netinet/ip_fw.h> |
#include <netinet/ip_fw.h> |
#include "ipfwaux.h" |
#include "ipfwaux.h" |
|
#include "ipfwrdr.h" |
|
|
#include "../config.h" |
|
#include "../upnpglobalvars.h" |
#include "../upnpglobalvars.h" |
|
|
|
/* init and shutdown functions */ |
|
|
int init_redirect(void) { |
int init_redirect(void) { |
ipfw_exec(IP_FW_INIT, NULL, 0); | return ipfw_exec(IP_FW_INIT, NULL, 0); |
return 0; | |
} |
} |
|
|
void shutdown_redirect(void) { |
void shutdown_redirect(void) { |
ipfw_exec(IP_FW_TERM, NULL, 0); |
ipfw_exec(IP_FW_TERM, NULL, 0); |
} |
} |
|
|
|
/* ipfw cannot store descriptions and timestamp for port mappings so we keep |
|
* our own list in memory */ |
|
struct mapping_desc_time { |
|
struct mapping_desc_time * next; |
|
unsigned int timestamp; |
|
unsigned short eport; |
|
short proto; |
|
char desc[]; |
|
}; |
|
|
|
static struct mapping_desc_time * mappings_list = NULL; |
|
|
|
/* add an element to the port mappings descriptions & timestamp list */ |
|
static void |
|
add_desc_time(unsigned short eport, int proto, |
|
const char * desc, unsigned int timestamp) |
|
{ |
|
struct mapping_desc_time * tmp; |
|
size_t l; |
|
if(!desc) |
|
desc = "miniupnpd"; |
|
l = strlen(desc) + 1; |
|
tmp = malloc(sizeof(struct mapping_desc_time) + l); |
|
if(tmp) { |
|
/* fill the element and insert it as head of the list */ |
|
tmp->next = mappings_list; |
|
tmp->timestamp = timestamp; |
|
tmp->eport = eport; |
|
tmp->proto = (short)proto; |
|
memcpy(tmp->desc, desc, l); |
|
mappings_list = tmp; |
|
} |
|
} |
|
|
|
/* remove an element to the port mappings descriptions & timestamp list */ |
|
static void |
|
del_desc_time(unsigned short eport, int proto) |
|
{ |
|
struct mapping_desc_time * e; |
|
struct mapping_desc_time * * p; |
|
p = &mappings_list; |
|
e = *p; |
|
while(e) { |
|
if(e->eport == eport && e->proto == (short)proto) { |
|
*p = e->next; |
|
free(e); |
|
return; |
|
} else { |
|
p = &e->next; |
|
e = *p; |
|
} |
|
} |
|
} |
|
|
|
/* go through the list and find the description and timestamp */ |
|
static void |
|
get_desc_time(unsigned short eport, int proto, |
|
char * desc, int desclen, |
|
unsigned int * timestamp) |
|
{ |
|
struct mapping_desc_time * e; |
|
|
|
for(e = mappings_list; e; e = e->next) { |
|
if(e->eport == eport && e->proto == (short)proto) { |
|
if(desc) |
|
strlcpy(desc, e->desc, desclen); |
|
if(timestamp) |
|
*timestamp = e->timestamp; |
|
return; |
|
} |
|
} |
|
} |
|
|
|
/* --- */ |
int add_redirect_rule2( |
int add_redirect_rule2( |
const char * ifname, |
const char * ifname, |
|
const char * rhost, |
unsigned short eport, |
unsigned short eport, |
const char * iaddr, |
const char * iaddr, |
unsigned short iport, |
unsigned short iport, |
int proto, |
int proto, |
const char * desc) | const char * desc, |
| unsigned int timestamp) |
{ |
{ |
struct ip_fw rule; |
struct ip_fw rule; |
|
int r; |
|
|
if (ipfw_validate_protocol(proto) < 0) |
if (ipfw_validate_protocol(proto) < 0) |
return -1; |
return -1; |
Line 93 int add_redirect_rule2(
|
Line 174 int add_redirect_rule2(
|
memset(&rule, 0, sizeof(struct ip_fw)); |
memset(&rule, 0, sizeof(struct ip_fw)); |
rule.version = IP_FW_CURRENT_API_VERSION; |
rule.version = IP_FW_CURRENT_API_VERSION; |
//rule.fw_number = 1000; // rule number |
//rule.fw_number = 1000; // rule number |
rule.context = (void *)desc; // TODO keep this? | //rule.context = (void *)desc; // The description is kept in a separate list |
rule.fw_prot = proto; // protocol |
rule.fw_prot = proto; // protocol |
rule.fw_flg |= IP_FW_F_IIFACE; // interfaces to check |
rule.fw_flg |= IP_FW_F_IIFACE; // interfaces to check |
rule.fw_flg |= IP_FW_F_IIFNAME; // interfaces to check by name |
rule.fw_flg |= IP_FW_F_IIFNAME; // interfaces to check by name |
Line 101 int add_redirect_rule2(
|
Line 182 int add_redirect_rule2(
|
rule.fw_flg |= IP_FW_F_FWD; // forward action |
rule.fw_flg |= IP_FW_F_FWD; // forward action |
#ifdef USE_IFNAME_IN_RULES |
#ifdef USE_IFNAME_IN_RULES |
if (ifname != NULL) { |
if (ifname != NULL) { |
strcpy(rule.fw_in_if.fu_via_if.name, ifname); // src interface | strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); // src interface |
rule.fw_in_if.fu_via_if.unit = -1; |
rule.fw_in_if.fu_via_if.unit = -1; |
} |
} |
#endif |
#endif |
Line 111 int add_redirect_rule2(
|
Line 192 int add_redirect_rule2(
|
} |
} |
memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); |
memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); |
memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); |
memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); |
rule.fw_dmsk.s_addr = INADDR_BROADCAST; | rule.fw_dmsk.s_addr = INADDR_BROADCAST; //TODO check this |
IP_FW_SETNDSTP(&rule, 1); // number of external ports |
IP_FW_SETNDSTP(&rule, 1); // number of external ports |
rule.fw_uar.fw_pts[0] = eport; // external port |
rule.fw_uar.fw_pts[0] = eport; // external port |
rule.fw_fwd_ip.sin_port = iport; // internal port |
rule.fw_fwd_ip.sin_port = iport; // internal port |
|
if (rhost && rhost[0] != '\0') { |
|
inet_aton(rhost, &rule.fw_src); |
|
rule.fw_smsk.s_addr = htonl(INADDR_NONE); |
|
} |
|
|
return ipfw_exec(IP_FW_ADD, &rule, sizeof(rule)); | r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule)); |
| if(r >= 0) |
| add_desc_time(eport, proto, desc, timestamp); |
| return r; |
} |
} |
|
|
/* get_redirect_rule() |
/* get_redirect_rule() |
Line 131 int get_redirect_rule(
|
Line 219 int get_redirect_rule(
|
unsigned short * iport, |
unsigned short * iport, |
char * desc, |
char * desc, |
int desclen, |
int desclen, |
|
char * rhost, |
|
int rhostlen, |
|
unsigned int * timestamp, |
u_int64_t * packets, |
u_int64_t * packets, |
u_int64_t * bytes) |
u_int64_t * bytes) |
{ |
{ |
Line 141 int get_redirect_rule(
|
Line 232 int get_redirect_rule(
|
return -1; |
return -1; |
if (ipfw_validate_ifname(ifname) < 0) |
if (ipfw_validate_ifname(ifname) < 0) |
return -1; |
return -1; |
| if (timestamp) |
| *timestamp = 0; |
| |
do { |
do { |
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); |
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); |
if (count_rules < 0) |
if (count_rules < 0) |
Line 157 int get_redirect_rule(
|
Line 250 int get_redirect_rule(
|
*bytes = ptr->fw_bcnt; |
*bytes = ptr->fw_bcnt; |
if (iport != NULL) |
if (iport != NULL) |
*iport = ptr->fw_fwd_ip.sin_port; |
*iport = ptr->fw_fwd_ip.sin_port; |
if (desc != NULL && desclen > 0) |
|
strlcpy(desc, "", desclen); // TODO should we copy ptr->context? |
|
if (iaddr != NULL && iaddrlen > 0) { |
if (iaddr != NULL && iaddrlen > 0) { |
if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) { | /* looks like fw_out_if.fu_via_ip is zero */ |
| //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) { |
| if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) { |
syslog(LOG_ERR, "inet_ntop(): %m"); |
syslog(LOG_ERR, "inet_ntop(): %m"); |
goto error; |
goto error; |
} |
} |
} |
} |
|
if (rhost != NULL && rhostlen > 0) { |
|
if (ptr->fw_src.s_addr == 0) |
|
rhost[0] = '\0'; |
|
else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) { |
|
syslog(LOG_ERR, "inet_ntop(): %m"); |
|
goto error; |
|
} |
|
} |
// And what if we found more than 1 matching rule? |
// And what if we found more than 1 matching rule? |
ipfw_free_ruleset(&rules); |
ipfw_free_ruleset(&rules); |
|
get_desc_time(eport, proto, desc, desclen, timestamp); |
return 0; |
return 0; |
} |
} |
} |
} |
Line 203 int delete_redirect_rule(
|
Line 305 int delete_redirect_rule(
|
goto error; |
goto error; |
// And what if we found more than 1 matching rule? |
// And what if we found more than 1 matching rule? |
ipfw_free_ruleset(&rules); |
ipfw_free_ruleset(&rules); |
|
del_desc_time(eport, proto); |
return 0; |
return 0; |
} |
} |
} |
} |
Line 215 error:
|
Line 318 error:
|
|
|
int add_filter_rule2( |
int add_filter_rule2( |
const char * ifname, |
const char * ifname, |
|
const char * rhost, |
const char * iaddr, |
const char * iaddr, |
unsigned short eport, |
unsigned short eport, |
unsigned short iport, |
unsigned short iport, |
int proto, |
int proto, |
const char * desc) |
const char * desc) |
{ |
{ |
return -1; | //return -1; |
| return 0; /* nothing to do, always success */ |
} |
} |
|
|
int delete_filter_rule( |
int delete_filter_rule( |
Line 229 int delete_filter_rule(
|
Line 334 int delete_filter_rule(
|
unsigned short eport, |
unsigned short eport, |
int proto) |
int proto) |
{ |
{ |
return -1; | //return -1; |
| return 0; /* nothing to do, always success */ |
} |
} |
|
|
int get_redirect_rule_by_index( |
int get_redirect_rule_by_index( |
Line 242 int get_redirect_rule_by_index(
|
Line 348 int get_redirect_rule_by_index(
|
int * proto, |
int * proto, |
char * desc, |
char * desc, |
int desclen, |
int desclen, |
|
char * rhost, |
|
int rhostlen, |
|
unsigned int * timestamp, |
u_int64_t * packets, |
u_int64_t * packets, |
u_int64_t * bytes) |
u_int64_t * bytes) |
{ |
{ |
Line 251 int get_redirect_rule_by_index(
|
Line 360 int get_redirect_rule_by_index(
|
if (index < 0) // TODO shouldn't we also validate the maximum? |
if (index < 0) // TODO shouldn't we also validate the maximum? |
return -1; |
return -1; |
|
|
|
if(timestamp) |
|
*timestamp = 0; |
|
|
ipfw_fetch_ruleset(&rules, &total_rules, index + 1); |
ipfw_fetch_ruleset(&rules, &total_rules, index + 1); |
|
|
if (total_rules == index + 1) { | if (total_rules > index) { |
const struct ip_fw const * ptr = &rules[index]; |
const struct ip_fw const * ptr = &rules[index]; |
|
if (ptr->fw_prot == 0) // invalid rule |
|
goto error; |
if (proto != NULL) |
if (proto != NULL) |
*proto = ptr->fw_prot; |
*proto = ptr->fw_prot; |
if (eport != NULL) |
if (eport != NULL) |
Line 269 int get_redirect_rule_by_index(
|
Line 383 int get_redirect_rule_by_index(
|
*bytes = ptr->fw_bcnt; |
*bytes = ptr->fw_bcnt; |
if (iport != NULL) |
if (iport != NULL) |
*iport = ptr->fw_fwd_ip.sin_port; |
*iport = ptr->fw_fwd_ip.sin_port; |
if (desc != NULL && desclen > 0) |
|
strlcpy(desc, "", desclen); // TODO should we copy ptr->context? |
|
if (iaddr != NULL && iaddrlen > 0) { |
if (iaddr != NULL && iaddrlen > 0) { |
if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) { | /* looks like fw_out_if.fu_via_ip is zero */ |
| //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) { |
| if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) { |
syslog(LOG_ERR, "inet_ntop(): %m"); |
syslog(LOG_ERR, "inet_ntop(): %m"); |
goto error; |
goto error; |
} |
} |
} |
} |
|
if (rhost != NULL && rhostlen > 0) { |
|
if (ptr->fw_src.s_addr == 0) |
|
rhost[0] = '\0'; |
|
else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) { |
|
syslog(LOG_ERR, "inet_ntop(): %m"); |
|
goto error; |
|
} |
|
} |
ipfw_free_ruleset(&rules); |
ipfw_free_ruleset(&rules); |
|
get_desc_time(*eport, *proto, desc, desclen, timestamp); |
return 0; |
return 0; |
} |
} |
|
|
Line 286 error:
|
Line 409 error:
|
ipfw_free_ruleset(&rules); |
ipfw_free_ruleset(&rules); |
return -1; |
return -1; |
} |
} |
|
|
|
/* upnp_get_portmappings_in_range() |
|
* return a list of all "external" ports for which a port |
|
* mapping exists */ |
|
unsigned short * |
|
get_portmappings_in_range(unsigned short startport, |
|
unsigned short endport, |
|
int proto, |
|
unsigned int * number) |
|
{ |
|
unsigned short * array = NULL; |
|
unsigned int capacity = 128; |
|
int i, count_rules, total_rules = 0; |
|
struct ip_fw * rules = NULL; |
|
|
|
if (ipfw_validate_protocol(proto) < 0) |
|
return NULL; |
|
|
|
do { |
|
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); |
|
if (count_rules < 0) |
|
goto error; |
|
} while (count_rules == 10); |
|
|
|
array = calloc(capacity, sizeof(unsigned short)); |
|
if(!array) { |
|
syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); |
|
goto error; |
|
} |
|
*number = 0; |
|
|
|
for (i=0; i<total_rules-1; i++) { |
|
const struct ip_fw const * ptr = &rules[i]; |
|
unsigned short eport = ptr->fw_uar.fw_pts[0]; |
|
if (proto == ptr->fw_prot |
|
&& startport <= eport |
|
&& eport <= endport) { |
|
if(*number >= capacity) { |
|
capacity += 128; |
|
array = realloc(array, sizeof(unsigned short)*capacity); |
|
if(!array) { |
|
syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); |
|
*number = 0; |
|
goto error; |
|
} |
|
} |
|
array[*number] = eport; |
|
(*number)++; |
|
} |
|
} |
|
error: |
|
if (rules != NULL) |
|
ipfw_free_ruleset(&rules); |
|
return array; |
|
} |
|
|