version 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 |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* (c) 2006-2010 Thomas Bernard | * (c) 2006-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 */ |
|
|
Line 55
|
Line 55
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
|
|
|
#include "../macros.h" |
#include "../config.h" |
#include "../config.h" |
#include "obsdrdr.h" |
#include "obsdrdr.h" |
#include "../upnpglobalvars.h" |
#include "../upnpglobalvars.h" |
|
|
/* anchor name */ | /* list too keep timestamps for port mappings having a lease duration */ |
static const char anchor_name[] = "miniupnpd"; | struct timestamp_entry { |
| struct timestamp_entry * next; |
| unsigned int timestamp; |
| unsigned short eport; |
| short protocol; |
| }; |
|
|
|
static struct timestamp_entry * timestamp_list = NULL; |
|
|
|
static unsigned int |
|
get_timestamp(unsigned short eport, int proto) |
|
{ |
|
struct timestamp_entry * e; |
|
e = timestamp_list; |
|
while(e) { |
|
if(e->eport == eport && e->protocol == (short)proto) |
|
return e->timestamp; |
|
e = e->next; |
|
} |
|
return 0; |
|
} |
|
|
|
static void |
|
remove_timestamp_entry(unsigned short eport, int proto) |
|
{ |
|
struct timestamp_entry * e; |
|
struct timestamp_entry * * p; |
|
p = ×tamp_list; |
|
e = *p; |
|
while(e) { |
|
if(e->eport == eport && e->protocol == (short)proto) { |
|
/* remove the entry */ |
|
*p = e->next; |
|
free(e); |
|
return; |
|
} |
|
p = &(e->next); |
|
e = *p; |
|
} |
|
} |
|
|
/* /dev/pf when opened */ |
/* /dev/pf when opened */ |
static int dev = -1; | int dev = -1; |
|
|
/* shutdown_redirect() : |
/* shutdown_redirect() : |
* close the /dev/pf device */ |
* close the /dev/pf device */ |
Line 138 error:
|
Line 178 error:
|
/* add_redirect_rule2() : |
/* add_redirect_rule2() : |
* create a rdr rule */ |
* create a rdr rule */ |
int |
int |
add_redirect_rule2(const char * ifname, unsigned short eport, | add_redirect_rule2(const char * ifname, |
| const char * rhost, unsigned short eport, |
const char * iaddr, unsigned short iport, int proto, |
const char * iaddr, unsigned short iport, int proto, |
const char * desc) | const char * desc, unsigned int timestamp) |
{ |
{ |
int r; |
int r; |
struct pfioc_rule pcr; |
struct pfioc_rule pcr; |
Line 171 add_redirect_rule2(const char * ifname, unsigned short
|
Line 212 add_redirect_rule2(const char * ifname, unsigned short
|
if(1) |
if(1) |
{ |
{ |
pcr.rule.direction = PF_IN; |
pcr.rule.direction = PF_IN; |
//pcr.rule.src.addr.type = PF_ADDR_NONE; | /*pcr.rule.src.addr.type = PF_ADDR_NONE;*/ |
pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; |
pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; |
pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; |
pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; |
pcr.rule.nat.addr.type = PF_ADDR_NONE; |
pcr.rule.nat.addr.type = PF_ADDR_NONE; |
pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; |
pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; |
#endif |
#endif |
| |
pcr.rule.dst.port_op = PF_OP_EQ; |
pcr.rule.dst.port_op = PF_OP_EQ; |
pcr.rule.dst.port[0] = htons(eport); |
pcr.rule.dst.port[0] = htons(eport); |
pcr.rule.dst.port[1] = htons(eport); |
pcr.rule.dst.port[1] = htons(eport); |
Line 205 add_redirect_rule2(const char * ifname, unsigned short
|
Line 246 add_redirect_rule2(const char * ifname, unsigned short
|
#ifdef PFRULE_HAS_RTABLEID |
#ifdef PFRULE_HAS_RTABLEID |
pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ |
pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ |
#endif |
#endif |
|
#ifdef PFRULE_HAS_ONRDOMAIN |
|
pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ |
|
#endif |
pcr.rule.quick = 1; |
pcr.rule.quick = 1; |
pcr.rule.keep_state = PF_STATE_NORMAL; |
pcr.rule.keep_state = PF_STATE_NORMAL; |
if(tag) |
if(tag) |
strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); |
strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); |
strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); |
strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); |
|
if(rhost && rhost[0] != '\0' && rhost[0] != '*') |
|
{ |
|
inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); |
|
pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); |
|
} |
#ifndef PF_NEWSTYLE |
#ifndef PF_NEWSTYLE |
pcr.rule.rpool.proxy_port[0] = iport; |
pcr.rule.rpool.proxy_port[0] = iport; |
pcr.rule.rpool.proxy_port[1] = iport; |
pcr.rule.rpool.proxy_port[1] = iport; |
Line 255 add_redirect_rule2(const char * ifname, unsigned short
|
Line 304 add_redirect_rule2(const char * ifname, unsigned short
|
free(a); |
free(a); |
#endif |
#endif |
} |
} |
|
if(r == 0 && timestamp > 0) |
|
{ |
|
struct timestamp_entry * tmp; |
|
tmp = malloc(sizeof(struct timestamp_entry)); |
|
if(tmp) |
|
{ |
|
tmp->next = timestamp_list; |
|
tmp->timestamp = timestamp; |
|
tmp->eport = eport; |
|
tmp->protocol = (short)proto; |
|
timestamp_list = tmp; |
|
} |
|
} |
return r; |
return r; |
} |
} |
|
|
/* thanks to Seth Mos for this function */ |
/* thanks to Seth Mos for this function */ |
int |
int |
add_filter_rule2(const char * ifname, const char * iaddr, | add_filter_rule2(const char * ifname, |
| const char * rhost, const char * iaddr, |
unsigned short eport, unsigned short iport, |
unsigned short eport, unsigned short iport, |
int proto, const char * desc) |
int proto, const char * desc) |
{ |
{ |
#ifndef PF_ENABLE_FILTER_RULES |
#ifndef PF_ENABLE_FILTER_RULES |
|
UNUSED(ifname); |
|
UNUSED(rhost); UNUSED(iaddr); |
|
UNUSED(eport); UNUSED(iport); |
|
UNUSED(proto); UNUSED(desc); |
return 0; |
return 0; |
#else |
#else |
int r; |
int r; |
Line 296 add_filter_rule2(const char * ifname, const char * iad
|
Line 363 add_filter_rule2(const char * ifname, const char * iad
|
if(1) |
if(1) |
{ |
{ |
#endif |
#endif |
| |
pcr.rule.dst.port_op = PF_OP_EQ; |
pcr.rule.dst.port_op = PF_OP_EQ; |
pcr.rule.dst.port[0] = htons(eport); |
pcr.rule.dst.port[0] = htons(eport); |
pcr.rule.direction = PF_IN; |
pcr.rule.direction = PF_IN; |
Line 314 add_filter_rule2(const char * ifname, const char * iad
|
Line 381 add_filter_rule2(const char * ifname, const char * iad
|
pcr.rule.flags = TH_SYN; |
pcr.rule.flags = TH_SYN; |
pcr.rule.flagset = (TH_SYN|TH_ACK); |
pcr.rule.flagset = (TH_SYN|TH_ACK); |
#ifdef PFRULE_HAS_RTABLEID |
#ifdef PFRULE_HAS_RTABLEID |
pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ | pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ |
#endif |
#endif |
|
#ifdef PFRULE_HAS_ONRDOMAIN |
|
pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ |
|
#endif |
pcr.rule.keep_state = 1; |
pcr.rule.keep_state = 1; |
strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); |
strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); |
if(queue) |
if(queue) |
Line 323 add_filter_rule2(const char * ifname, const char * iad
|
Line 393 add_filter_rule2(const char * ifname, const char * iad
|
if(tag) |
if(tag) |
strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); |
strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); |
|
|
|
if(rhost && rhost[0] != '\0' && rhost[0] != '*') |
|
{ |
|
inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); |
|
pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); |
|
} |
#ifndef PF_NEWSTYLE |
#ifndef PF_NEWSTYLE |
pcr.rule.rpool.proxy_port[0] = eport; |
pcr.rule.rpool.proxy_port[0] = eport; |
a = calloc(1, sizeof(struct pf_pooladdr)); |
a = calloc(1, sizeof(struct pf_pooladdr)); |
Line 332 add_filter_rule2(const char * ifname, const char * iad
|
Line 407 add_filter_rule2(const char * ifname, const char * iad
|
TAILQ_INIT(&pcr.rule.rpool.list); |
TAILQ_INIT(&pcr.rule.rpool.list); |
inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); |
inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); |
TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); |
TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); |
| |
/* we have any - any port = # keep state label */ |
/* we have any - any port = # keep state label */ |
/* we want any - iaddr port = # keep state label */ |
/* we want any - iaddr port = # keep state label */ |
/* memcpy(&pcr.rule.dst, a, sizeof(struct pf_pooladdr)); */ |
/* memcpy(&pcr.rule.dst, a, sizeof(struct pf_pooladdr)); */ |
Line 381 int
|
Line 456 int
|
get_redirect_rule(const char * ifname, unsigned short eport, int proto, |
get_redirect_rule(const char * ifname, unsigned short eport, int proto, |
char * iaddr, int iaddrlen, unsigned short * iport, |
char * iaddr, int iaddrlen, unsigned short * iport, |
char * desc, int desclen, |
char * desc, int desclen, |
|
char * rhost, int rhostlen, |
|
unsigned int * timestamp, |
u_int64_t * packets, u_int64_t * bytes) |
u_int64_t * packets, u_int64_t * bytes) |
{ |
{ |
int i, n; |
int i, n; |
Line 388 get_redirect_rule(const char * ifname, unsigned short
|
Line 465 get_redirect_rule(const char * ifname, unsigned short
|
#ifndef PF_NEWSTYLE |
#ifndef PF_NEWSTYLE |
struct pfioc_pooladdr pp; |
struct pfioc_pooladdr pp; |
#endif |
#endif |
|
UNUSED(ifname); |
|
|
if(dev<0) { |
if(dev<0) { |
syslog(LOG_ERR, "pf device is not open"); |
syslog(LOG_ERR, "pf device is not open"); |
return -1; |
return -1; |
Line 461 get_redirect_rule(const char * ifname, unsigned short
|
Line 540 get_redirect_rule(const char * ifname, unsigned short
|
inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, |
inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, |
iaddr, iaddrlen); |
iaddr, iaddrlen); |
#endif |
#endif |
|
if(rhost && rhostlen > 0) |
|
{ |
|
if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) |
|
{ |
|
rhost[0] = '\0'; /* empty string */ |
|
} |
|
else |
|
{ |
|
inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, |
|
rhost, rhostlen); |
|
} |
|
} |
|
if(timestamp) |
|
*timestamp = get_timestamp(eport, proto); |
return 0; |
return 0; |
} |
} |
} |
} |
Line 473 delete_redirect_rule(const char * ifname, unsigned sho
|
Line 566 delete_redirect_rule(const char * ifname, unsigned sho
|
{ |
{ |
int i, n; |
int i, n; |
struct pfioc_rule pr; |
struct pfioc_rule pr; |
|
UNUSED(ifname); |
|
|
if(dev<0) { |
if(dev<0) { |
syslog(LOG_ERR, "pf device is not open"); |
syslog(LOG_ERR, "pf device is not open"); |
return -1; |
return -1; |
Line 513 delete_redirect_rule(const char * ifname, unsigned sho
|
Line 608 delete_redirect_rule(const char * ifname, unsigned sho
|
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); |
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); |
goto error; |
goto error; |
} |
} |
|
remove_timestamp_entry(eport, proto); |
return 0; |
return 0; |
} |
} |
} |
} |
Line 524 int
|
Line 620 int
|
delete_filter_rule(const char * ifname, unsigned short eport, int proto) |
delete_filter_rule(const char * ifname, unsigned short eport, int proto) |
{ |
{ |
#ifndef PF_ENABLE_FILTER_RULES |
#ifndef PF_ENABLE_FILTER_RULES |
|
UNUSED(ifname); UNUSED(eport); UNUSED(proto); |
return 0; |
return 0; |
#else |
#else |
int i, n; |
int i, n; |
Line 578 get_redirect_rule_by_index(int index,
|
Line 675 get_redirect_rule_by_index(int index,
|
char * ifname, unsigned short * eport, |
char * ifname, unsigned short * eport, |
char * iaddr, int iaddrlen, unsigned short * iport, |
char * iaddr, int iaddrlen, unsigned short * iport, |
int * proto, char * desc, int desclen, |
int * proto, char * desc, int desclen, |
|
char * rhost, int rhostlen, |
|
unsigned int * timestamp, |
u_int64_t * packets, u_int64_t * bytes) |
u_int64_t * packets, u_int64_t * bytes) |
{ |
{ |
int n; |
int n; |
Line 660 get_redirect_rule_by_index(int index,
|
Line 759 get_redirect_rule_by_index(int index,
|
inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, |
inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, |
iaddr, iaddrlen); |
iaddr, iaddrlen); |
#endif |
#endif |
|
if(rhost && rhostlen > 0) |
|
{ |
|
if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) |
|
{ |
|
rhost[0] = '\0'; /* empty string */ |
|
} |
|
else |
|
{ |
|
inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, |
|
rhost, rhostlen); |
|
} |
|
} |
|
if(timestamp) |
|
*timestamp = get_timestamp(*eport, *proto); |
return 0; |
return 0; |
error: |
error: |
return -1; |
return -1; |
} |
} |
|
|
|
/* return an (malloc'ed) array of "external" port for which there is |
|
* a port mapping. number is the size of the array */ |
|
unsigned short * |
|
get_portmappings_in_range(unsigned short startport, unsigned short endport, |
|
int proto, unsigned int * number) |
|
{ |
|
unsigned short * array; |
|
unsigned int capacity; |
|
int i, n; |
|
unsigned short eport; |
|
struct pfioc_rule pr; |
|
|
|
*number = 0; |
|
if(dev<0) { |
|
syslog(LOG_ERR, "pf device is not open"); |
|
return NULL; |
|
} |
|
capacity = 128; |
|
array = calloc(capacity, sizeof(unsigned short)); |
|
if(!array) |
|
{ |
|
syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); |
|
return NULL; |
|
} |
|
memset(&pr, 0, sizeof(pr)); |
|
strlcpy(pr.anchor, anchor_name, MAXPATHLEN); |
|
#ifndef PF_NEWSTYLE |
|
pr.rule.action = PF_RDR; |
|
#endif |
|
if(ioctl(dev, DIOCGETRULES, &pr) < 0) |
|
{ |
|
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); |
|
free(array); |
|
return NULL; |
|
} |
|
n = pr.nr; |
|
for(i=0; i<n; i++) |
|
{ |
|
pr.nr = i; |
|
if(ioctl(dev, DIOCGETRULE, &pr) < 0) |
|
{ |
|
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); |
|
continue; |
|
} |
|
eport = ntohs(pr.rule.dst.port[0]); |
|
if( (eport == ntohs(pr.rule.dst.port[1])) |
|
&& (pr.rule.proto == proto) |
|
&& (startport <= eport) && (eport <= endport) ) |
|
{ |
|
if(*number >= capacity) |
|
{ |
|
/* need to increase the capacity of the array */ |
|
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; |
|
return NULL; |
|
} |
|
} |
|
array[*number] = eport; |
|
(*number)++; |
|
} |
|
} |
|
return array; |
|
} |
|
|
/* this function is only for testing */ |
/* this function is only for testing */ |
#if TEST |
#if TEST |
void |
void |
Line 695 list_rules(void)
|
Line 876 list_rules(void)
|
pr.nr = i; |
pr.nr = i; |
if(ioctl(dev, DIOCGETRULE, &pr) < 0) |
if(ioctl(dev, DIOCGETRULE, &pr) < 0) |
perror("DIOCGETRULE"); |
perror("DIOCGETRULE"); |
printf(" %s %d:%d -> %d:%d proto %d keep_state=%d action=%d\n", | printf(" %s %s %d:%d -> %d:%d proto %d keep_state=%d action=%d\n", |
pr.rule.ifname, |
pr.rule.ifname, |
|
inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, buf, 32), |
(int)ntohs(pr.rule.dst.port[0]), |
(int)ntohs(pr.rule.dst.port[0]), |
(int)ntohs(pr.rule.dst.port[1]), |
(int)ntohs(pr.rule.dst.port[1]), |
#ifndef PF_NEWSTYLE |
#ifndef PF_NEWSTYLE |