|
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$ */ |
| /* |
/* |
| * 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-2012 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> |
| |
|
| // | /* |
| // This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD. | This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD. |
| // Needed here because on some systems <sys/uio.h> gets included by things | Needed here because on some systems <sys/uio.h> gets included by things |
| // like <sys/socket.h> | like <sys/socket.h> |
| // | */ |
| #ifndef _KERNEL |
#ifndef _KERNEL |
| # define ADD_KERNEL |
# define ADD_KERNEL |
| # define _KERNEL |
# define _KERNEL |
|
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; |
| if (ipfw_validate_ifname(ifname) < 0) |
if (ipfw_validate_ifname(ifname) < 0) |
| return -1; |
return -1; |
| | |
| 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 | #if 0 |
| rule.context = (void *)desc; // TODO keep this? | rule.fw_number = 1000; /* rule number */ |
| rule.fw_prot = proto; // protocol | rule.context = (void *)desc; /* The description is kept in a separate list */ |
| rule.fw_flg |= IP_FW_F_IIFACE; // interfaces to check | #endif |
| rule.fw_flg |= IP_FW_F_IIFNAME; // interfaces to check by name | rule.fw_prot = proto; /* protocol */ |
| rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); // packet direction | rule.fw_flg |= IP_FW_F_IIFACE; /* interfaces to check */ |
| rule.fw_flg |= IP_FW_F_FWD; // forward action | rule.fw_flg |= IP_FW_F_IIFNAME; /* interfaces to check by name */ |
| | rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); /* packet direction */ |
| | 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 |
| if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) { |
if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) { |
| syslog(LOG_ERR, "inet_aton(): %m"); |
syslog(LOG_ERR, "inet_aton(): %m"); |
| return -1; |
return -1; |
| } | } |
| 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 126 int get_redirect_rule(
|
Line 216 int get_redirect_rule(
|
| const char * ifname, |
const char * ifname, |
| unsigned short eport, |
unsigned short eport, |
| int proto, |
int proto, |
| char * iaddr, | char * iaddr, |
| int iaddrlen, | int iaddrlen, |
| 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) |
| { |
{ |
| int i, count_rules, total_rules = 0; |
int i, count_rules, total_rules = 0; |
| struct ip_fw * rules = NULL; |
struct ip_fw * rules = NULL; |
| | |
| if (ipfw_validate_protocol(proto) < 0) |
if (ipfw_validate_protocol(proto) < 0) |
| 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) |
| goto error; |
goto error; |
| } while (count_rules == 10); |
} while (count_rules == 10); |
| | |
| for (i=0; i<total_rules-1; i++) { |
for (i=0; i<total_rules-1; i++) { |
| const struct ip_fw const * ptr = &rules[i]; |
const struct ip_fw const * ptr = &rules[i]; |
| if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) { |
if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) { |
|
Line 157 int get_redirect_rule(
|
Line 252 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; |
| } | } |
| } |
} |
| // And what if we found more than 1 matching rule? | 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? */ |
| ipfw_free_ruleset(&rules); |
ipfw_free_ruleset(&rules); |
| |
get_desc_time(eport, proto, desc, desclen, timestamp); |
| return 0; |
return 0; |
| } |
} |
| } |
} |
| |
|
| error: |
error: |
| if (rules != NULL) |
if (rules != NULL) |
| ipfw_free_ruleset(&rules); | ipfw_free_ruleset(&rules); |
| return -1; |
return -1; |
| } |
} |
| |
|
| int delete_redirect_rule( |
int delete_redirect_rule( |
| const char * ifname, |
const char * ifname, |
| unsigned short eport, |
unsigned short eport, |
| int proto) | int proto) |
| { |
{ |
| int i, count_rules, total_rules = 0; |
int i, count_rules, total_rules = 0; |
| struct ip_fw * rules = NULL; |
struct ip_fw * rules = NULL; |
| | |
| if (ipfw_validate_protocol(proto) < 0) |
if (ipfw_validate_protocol(proto) < 0) |
| return -1; |
return -1; |
| if (ipfw_validate_ifname(ifname) < 0) |
if (ipfw_validate_ifname(ifname) < 0) |
| return -1; |
return -1; |
| | |
| 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) |
| goto error; |
goto error; |
| } while (count_rules == 10); |
} while (count_rules == 10); |
| | |
| for (i=0; i<total_rules-1; i++) { |
for (i=0; i<total_rules-1; i++) { |
| const struct ip_fw const * ptr = &rules[i]; |
const struct ip_fw const * ptr = &rules[i]; |
| if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) { |
if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) { |
| if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0) |
if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0) |
| 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; |
| } |
} |
| } |
} |
| | |
| error: |
error: |
| if (rules != NULL) |
if (rules != NULL) |
| ipfw_free_ruleset(&rules); | ipfw_free_ruleset(&rules); |
| return -1; |
return -1; |
| } |
} |
| |
|
| 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 0; /* nothing to do, always success */ |
| } |
} |
| |
|
| int delete_filter_rule( |
int delete_filter_rule( |
| const char * ifname, | const char * ifname, |
| unsigned short eport, | unsigned short eport, |
| int proto) | int proto) |
| { |
{ |
| return -1; | return 0; /* nothing to do, always success */ |
| } |
} |
| |
|
| int get_redirect_rule_by_index( |
int get_redirect_rule_by_index( |
| int index, |
int index, |
| char * ifname, | char * ifname, |
| unsigned short * eport, |
unsigned short * eport, |
| char * iaddr, | char * iaddr, |
| int iaddrlen, | int iaddrlen, |
| unsigned short * iport, |
unsigned short * iport, |
| int * proto, | int * proto, |
| char * desc, | char * desc, |
| int desclen, |
int desclen, |
| u_int64_t * packets, | char * rhost, |
| | int rhostlen, |
| | unsigned int * timestamp, |
| | u_int64_t * packets, |
| u_int64_t * bytes) |
u_int64_t * bytes) |
| { |
{ |
| int total_rules = 0; |
int total_rules = 0; |
| struct ip_fw * rules = NULL; |
struct ip_fw * rules = NULL; |
| |
|
| 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; |
| } |
} |
| |
|
| error: |
error: |
| if (rules != NULL) |
if (rules != NULL) |
| 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; |
| |
} |
| |
|