This commit fixes IPv4 and adds IPv6 pinhole to nftables.

Signed-off-by: Sven Auhagen <sven.auhagen@voleatech.de>
This commit is contained in:
Sven Auhagen 2019-06-03 12:31:53 +02:00 committed by Thomas Bernard
parent 765156b04a
commit 00ff23c428
14 changed files with 1128 additions and 344 deletions

View File

@ -46,7 +46,8 @@ BASEOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
upnppinhole.o pcplearndscp.o asyncsendto.o
LNXOBJS = linux/getifstats.o linux/ifacewatcher.o linux/getroute.o
NETFILTEROBJS = netfilter_nft/nftnlrdr.o netfilter_nft/nfct_get.o netfilter_nft/nftnlrdr_misc.o
NETFILTEROBJS = netfilter_nft/nftnlrdr.o netfilter_nft/nftpinhole.o \
netfilter_nft/nfct_get.o netfilter_nft/nftnlrdr_misc.o
ALLOBJS = $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS)
@ -210,7 +211,7 @@ upnputils.o: miniupnpdtypes.h getroute.h
getconnstatus.o: getconnstatus.h getifaddr.h
upnppinhole.o: macros.h config.h upnpredirect.h upnpglobalvars.h
upnppinhole.o: upnppermissions.h miniupnpdtypes.h upnpevents.h
#upnppinhole.o: netfilter/iptpinhole.h
upnppinhole.o: netfilter_nft/nftpinhole.h
pcplearndscp.o: config.h upnpglobalvars.h upnppermissions.h miniupnpdtypes.h
pcplearndscp.o: pcplearndscp.h
asyncsendto.o: asyncsendto.h
@ -222,8 +223,8 @@ linux/getroute.o: getroute.h upnputils.h
netfilter_nft/nftnlrdr.o: macros.h config.h netfilter_nft/nftnlrdr.h commonrdr.h
netfilter_nft/nftnlrdr.o: config.h upnpglobalvars.h upnppermissions.h
netfilter_nft/nftnlrdr.o: miniupnpdtypes.h
netfilter_nft/iptpinhole.o: config.h netfilter_nft/iptpinhole.h upnpglobalvars.h
netfilter_nft/iptpinhole.o: upnppermissions.h config.h miniupnpdtypes.h
netfilter_nft/nftpinhole.o: config.h netfilter_nft/nftpinhole.h upnpglobalvars.h
netfilter_nft/nftpinhole.o: upnppermissions.h config.h miniupnpdtypes.h
testupnpdescgen.o: macros.h config.h upnpdescgen.h upnpdescstrings.h
testupnpdescgen.o: getifaddr.h
upnpdescgen.o: config.h getifaddr.h upnpredirect.h upnpdescgen.h

View File

@ -5,15 +5,15 @@ LIBS = -lnftnl -lmnl
ARCH := $(shell uname -m | grep -q "x86_64" && echo 64)
all: test_nfct_get testnftnlrdr
all: test_nfct_get testnftnlrdr testnftpinhole
clean:
$(RM) *.o testnftnlcrdr testnftnlpinhole testnftnlrdr_peer \
$(RM) *.o testnftnlcrdr testnftpinhole testnftnlrdr_peer \
test_nfct_get testnftnlrdr
testnftnlrdr: nftnlrdr.o nftnlrdr_misc.o testnftnlrdr.o upnpglobalvars.o $(LIBS)
testiptpinhole: testiptpinhole.o iptpinhole.o upnpglobalvars.o $(LIBS)
testnftpinhole: nftpinhole.o nftnlrdr_misc.o testnftpinhole.o upnpglobalvars.o $(LIBS)
test_nfct_get: test_nfct_get.o test_nfct_get.o -lmnl -lnetfilter_conntrack
@ -27,7 +27,7 @@ nftnlrdr.o: nftnlrdr.c nftnlrdr.h
nftnlrdr_misc.o: nftnlrdr_misc.c
iptpinhole.o: iptpinhole.c iptpinhole.h
nftpinhole.o: nftpinhole.c nftpinhole.h
upnpglobalvars.o: ../upnpglobalvars.c ../upnpglobalvars.h
$(CC) -c -o $@ $<

View File

@ -131,10 +131,12 @@ add_redirect_rule2(const char * ifname,
d_printf(("add redirect rule2(%s, %s, %u, %s, %u, %d, %s)!\n",
ifname, rhost, eport, iaddr, iport, proto, desc));
r = rule_set_dnat(NFPROTO_IPV4, ifname, proto,
0, eport,
inet_addr(iaddr), iport, desc, NULL);
return nft_send_request(r, NFT_MSG_NEWRULE);
return nft_send_request(r, NFT_MSG_NEWRULE, RULE_CHAIN_REDIRECT);
}
/*
@ -154,12 +156,13 @@ add_peer_redirect_rule2(const char * ifname,
UNUSED(ifname); UNUSED(timestamp);
d_printf(("add peer redirect rule2()!\n"));
r = rule_set_snat(NFPROTO_IPV4, proto,
inet_addr(rhost), rport,
inet_addr(eaddr), eport,
inet_addr(iaddr), iport, desc, NULL);
return nft_send_request(r, NFT_MSG_NEWRULE);
return nft_send_request(r, NFT_MSG_NEWRULE, RULE_CHAIN_PEER);
}
/*
@ -179,13 +182,16 @@ add_filter_rule2(const char * ifname,
d_printf(("add_filter_rule2(%s, %s, %s, %d, %d, %d, %s)\n",
ifname, rhost, iaddr, eport, iport, proto, desc));
if (rhost != NULL && strcmp(rhost, "") != 0) {
rhost_addr = inet_addr(rhost);
}
r = rule_set_filter(NFPROTO_IPV4, ifname, proto,
rhost_addr, inet_addr(iaddr), eport, iport,
rhost_addr, inet_addr(iaddr),
eport, iport, 0,
desc, 0);
return nft_send_request(r, NFT_MSG_NEWRULE);
return nft_send_request(r, NFT_MSG_NEWRULE, RULE_CHAIN_FILTER);
}
/*
@ -213,12 +219,11 @@ delete_filter_rule(const char * ifname, unsigned short port, int proto)
struct nftnl_rule *r;
UNUSED(ifname);
reflesh_nft_cache(NFPROTO_IPV4);
LIST_FOREACH(p, &head, entry) {
reflesh_nft_cache_filter();
LIST_FOREACH(p, &head_filter, entry) {
if (p->eport == port && p->proto == proto && p->type == RULE_FILTER) {
r = rule_del_handle(p);
/* Todo: send bulk request */
nft_send_request(r, NFT_MSG_DELRULE);
nft_send_request(r, NFT_MSG_DELRULE, RULE_CHAIN_FILTER);
break;
}
}
@ -239,33 +244,67 @@ delete_redirect_and_filter_rules(unsigned short eport, int proto)
extern void print_rule(rule_t *r);
d_printf(("delete_redirect_and_filter_rules(%d %d)\n", eport, proto));
reflesh_nft_cache(NFPROTO_IPV4);
LIST_FOREACH(p, &head, entry) {
reflesh_nft_cache_redirect();
// Delete Redirect Rule
LIST_FOREACH(p, &head_redirect, entry) {
if (p->eport == eport && p->proto == proto &&
(p->type == RULE_NAT || p->type == RULE_SNAT)) {
(p->type == RULE_NAT && p->nat_type == NFT_NAT_DNAT)) {
iaddr = p->iaddr;
iport = p->iport;
r = rule_del_handle(p);
/* Todo: send bulk request */
nft_send_request(r, NFT_MSG_DELRULE);
nft_send_request(r, NFT_MSG_DELRULE, RULE_CHAIN_REDIRECT);
break;
}
}
if (iaddr == 0 && iport == 0) {
return -1;
}
reflesh_nft_cache(NFPROTO_IPV4);
LIST_FOREACH(p, &head, entry) {
if (iaddr != 0 && iport != 0) {
reflesh_nft_cache_filter();
// Delete Forward Rule
LIST_FOREACH(p, &head_filter, entry) {
if (p->eport == iport &&
p->iaddr == iaddr && p->type == RULE_FILTER) {
r = rule_del_handle(p);
/* Todo: send bulk request */
nft_send_request(r, NFT_MSG_DELRULE);
nft_send_request(r, NFT_MSG_DELRULE, RULE_CHAIN_FILTER);
break;
}
}
}
iaddr = 0;
iport = 0;
reflesh_nft_cache_peer();
// Delete Peer Rule
LIST_FOREACH(p, &head_peer, entry) {
if (p->eport == eport && p->proto == proto &&
(p->type == RULE_NAT && p->nat_type == NFT_NAT_SNAT)) {
iaddr = p->iaddr;
iport = p->iport;
r = rule_del_handle(p);
/* Todo: send bulk request */
nft_send_request(r, NFT_MSG_DELRULE, RULE_CHAIN_PEER);
break;
}
}
if (iaddr != 0 && iport != 0) {
reflesh_nft_cache_filter();
// Delete Forward Rule
LIST_FOREACH(p, &head_filter, entry) {
if (p->eport == iport &&
p->iaddr == iaddr && p->type == RULE_FILTER) {
r = rule_del_handle(p);
/* Todo: send bulk request */
nft_send_request(r, NFT_MSG_DELRULE, RULE_CHAIN_FILTER);
break;
}
}
}
return 0;
}
@ -283,56 +322,66 @@ get_peer_rule_by_index(int index,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes)
{
int i;
struct in_addr addr;
char *addr_str;
rule_t *r;
UNUSED(timestamp); UNUSED(packets); UNUSED(bytes);
UNUSED(timestamp);
d_printf(("get_peer_rule_by_index()\n"));
reflesh_nft_cache(NFPROTO_IPV4);
if (peer_cache == NULL) {
return -1;
}
reflesh_nft_cache_peer();
for (i = 0; peer_cache[i] != NULL; i++) {
if (index == i) {
r = peer_cache[i];
LIST_FOREACH(r, &head_peer, entry) {
if (r->index == index) {
if (ifname != NULL) {
if_indextoname(r->ingress_ifidx, ifname);
}
if (eport != NULL) {
*eport = r->eport;
}
if (iaddr != NULL) {
addr.s_addr = r->iaddr;
addr_str = inet_ntoa(addr);
strncpy(iaddr , addr_str, iaddrlen);
}
if (iport != NULL) {
*iport = r->iport;
}
if (proto != NULL) {
*proto = r->proto;
}
if (rhost != NULL) {
addr.s_addr = r->rhost;
addr_str = inet_ntoa(addr);
strncpy(iaddr , addr_str, rhostlen);
}
if (rport != NULL) {
*rport = r->rport;
}
if (desc != NULL) {
strncpy(desc, r->desc, desclen);
}
if (packets || bytes) {
if (packets)
*packets = r->packets;
if (bytes)
*bytes = r->bytes;
}
/*
* TODO: Implement counter in case of add {nat,filter}
*/
return 0;
}
}
return -1;
}
@ -369,43 +418,44 @@ get_redirect_rule_by_index(int index,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes)
{
int i;
struct in_addr addr;
char *addr_str;
rule_t *r;
UNUSED(timestamp); UNUSED(packets); UNUSED(bytes);
UNUSED(timestamp);
d_printf(("get_redirect_rule_by_index()\n"));
reflesh_nft_cache(NFPROTO_IPV4);
if (redirect_cache == NULL) {
return -1;
}
reflesh_nft_cache_redirect();
for (i = 0; redirect_cache[i] != NULL; i++) {
if (index == i) {
r = redirect_cache[i];
LIST_FOREACH(r, &head_redirect, entry) {
if (r->index == index) {
if (ifname != NULL) {
if_indextoname(r->ingress_ifidx, ifname);
}
if (eport != NULL) {
*eport = r->eport;
}
if (iaddr != NULL) {
addr.s_addr = r->iaddr;
addr_str = inet_ntoa(addr);
strncpy(iaddr , addr_str, iaddrlen);
}
if (iport != NULL) {
*iport = r->iport;
}
if (proto != NULL) {
*proto = r->proto;
}
if (rhost != NULL) {
addr.s_addr = r->rhost;
addr_str = inet_ntoa(addr);
strncpy(iaddr , addr_str, rhostlen);
}
if (desc != NULL && r->desc) {
strncpy(desc, r->desc, desclen);
}
@ -414,12 +464,20 @@ get_redirect_rule_by_index(int index,
*timestamp = get_timestamp(*eport, *proto);
}
if (packets || bytes) {
if (packets)
*packets = r->packets;
if (bytes)
*bytes = r->bytes;
}
/*
* TODO: Implement counter in case of add {nat,filter}
*/
return 0;
}
}
return -1;
}
@ -438,29 +496,31 @@ get_nat_redirect_rule(const char * nat_chain_name, const char * ifname,
{
rule_t *p;
struct in_addr addr;
char *addr_str;
UNUSED(nat_chain_name);
UNUSED(ifname);
UNUSED(iaddrlen);
UNUSED(timestamp);
UNUSED(packets);
UNUSED(bytes);
UNUSED(rhost);
UNUSED(rhostlen);
d_printf(("get_nat_redirect_rule()\n"));
reflesh_nft_cache(NFPROTO_IPV4);
reflesh_nft_cache_redirect();
LIST_FOREACH(p, &head, entry) {
LIST_FOREACH(p, &head_redirect, entry) {
if (p->proto == proto &&
p->eport == eport) {
if (p->rhost && rhost) {
addr.s_addr = p->rhost;
addr_str = inet_ntoa(addr);
strncpy(iaddr , addr_str, rhostlen);
if (p->iaddr) {
addr.s_addr = p->iaddr;
inet_ntop(AF_INET, &addr, iaddr, INET_ADDRSTRLEN);
}
if (desc != NULL && p->desc) {
strncpy(desc, p->desc, desclen);
}
if (iport)
*iport = p->iport;
if(timestamp != NULL)
@ -487,6 +547,7 @@ get_portmappings_in_range(unsigned short startport, unsigned short endport,
unsigned short *tmp;
d_printf(("get_portmappings_in_range()\n"));
*number = 0;
capacity = 128;
array = calloc(capacity, sizeof(unsigned short));
@ -496,7 +557,9 @@ get_portmappings_in_range(unsigned short startport, unsigned short endport,
return NULL;
}
LIST_FOREACH(p, &head, entry) {
reflesh_nft_cache_redirect();
LIST_FOREACH(p, &head_redirect, entry) {
if (p->proto == proto &&
startport <= p->eport &&
p->eport <= endport) {
@ -539,23 +602,23 @@ update_portmapping(const char * ifname, unsigned short eport, int proto,
unsigned short iport, const char * desc,
unsigned int timestamp)
{
char iaddr[4];
char iaddr_str[16];
char rhost[32];
char iaddr_str[INET_ADDRSTRLEN];
char rhost[INET_ADDRSTRLEN];
int r;
if (get_redirect_rule(NULL, eport, proto, iaddr, 0, NULL, NULL, 0, rhost, 0, NULL, 0, 0) < 0)
d_printf(("update_portmapping()\n"));
if (get_redirect_rule(NULL, eport, proto, iaddr_str, INET_ADDRSTRLEN, NULL, NULL, 0, rhost, INET_ADDRSTRLEN, NULL, 0, 0) < 0)
return -1;
r = delete_redirect_and_filter_rules(eport, proto);
if (r < 0)
return -1;
inet_ntop(AF_INET, &iaddr, iaddr_str, sizeof(iaddr_str));
if (add_redirect_rule2(ifname, rhost, eport, iaddr_str, iport, proto,
desc, timestamp) < 0)
return -1;
if (add_filter_rule2(ifname, rhost, iaddr_str, eport, iport, proto, desc) < 0)
return -1;
@ -570,13 +633,21 @@ list_redirect_rule(const char * ifname)
rule_t *p;
UNUSED(ifname);
reflesh_nft_cache(NFPROTO_IPV4);
LIST_FOREACH(p, &head, entry) {
reflesh_nft_cache_filter();
LIST_FOREACH(p, &head_filter, entry) {
print_rule(p);
}
reflesh_nft_cache_redirect();
LIST_FOREACH(p, &head_redirect, entry) {
print_rule(p);
}
reflesh_nft_cache_peer();
LIST_FOREACH(p, &head_peer, entry) {
print_rule(p);
}
return -1;
return 0;
}

View File

@ -27,6 +27,7 @@
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/ipv6.h>
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
@ -45,28 +46,30 @@
#define RULE_CACHE_INVALID 0
#define RULE_CACHE_VALID 1
const char * miniupnpd_nft_nat_chain = "miniupnpd";
const char * miniupnpd_nft_peer_chain = "miniupnpd-pcp-peer";
const char * miniupnpd_nft_forward_chain = "miniupnpd";
static struct mnl_socket *nl = NULL;
struct rule_list head = LIST_HEAD_INITIALIZER(head);
static uint32_t rule_list_validate = RULE_CACHE_INVALID;
uint32_t rule_list_length = 0;
uint32_t rule_list_peer_length = 0;
rule_t **redirect_cache;
rule_t **peer_cache;
// FILTER
struct rule_list head_filter = LIST_HEAD_INITIALIZER(head_filter);
// DNAT
struct rule_list head_redirect = LIST_HEAD_INITIALIZER(head_redirect);
// SNAT
struct rule_list head_peer = LIST_HEAD_INITIALIZER(head_peer);
static uint32_t rule_list_filter_validate = RULE_CACHE_INVALID;
static uint32_t rule_list_redirect_validate = RULE_CACHE_INVALID;
static uint32_t rule_list_peer_validate = RULE_CACHE_INVALID;
static const char *
get_family_string(uint32_t family)
{
switch (family) {
case NFPROTO_INET:
return "ipv4/6";
case NFPROTO_IPV4:
return "ipv4";
case NFPROTO_IPV6:
return "ipv6";
}
return "unknown family";
}
@ -79,6 +82,7 @@ get_proto_string(uint32_t proto)
case IPPROTO_UDP:
return "udp";
}
return "unknown proto";
}
@ -100,6 +104,8 @@ print_rule(rule_t *r)
{
struct in_addr addr;
char *iaddr_str = NULL, *rhost_str = NULL, *eaddr_str = NULL;
char iaddr6_str[INET6_ADDRSTRLEN];
char rhost6_str[INET6_ADDRSTRLEN];
char ifname_buf[IF_NAMESIZE];
switch (r->type) {
@ -159,6 +165,10 @@ print_rule(rule_t *r)
addr.s_addr = r->rhost;
rhost_str = strdupa(inet_ntoa(addr));
}
inet_ntop(AF_INET6, &r->iaddr6, iaddr6_str, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &r->rhost6, rhost6_str, INET6_ADDRSTRLEN);
if ( (r->iaddr != 0) || (r->rhost != 0) ) {
printf("%"PRIu64":[%s/%s] %s/%s, %s %s:%d: %s (%s)\n",
r->handle, r->table, r->chain,
get_family_string(r->family), get_proto_string(r->proto),
@ -166,6 +176,15 @@ print_rule(rule_t *r)
iaddr_str, r->eport,
get_verdict_string(r->filter_action),
r->desc);
} else {
printf("%"PRIu64":[%s/%s] %s/%s, %s %s:%d: %s (%s)\n",
r->handle, r->table, r->chain,
get_family_string(r->family), get_proto_string(r->proto),
rhost6_str,
iaddr6_str, r->eport,
get_verdict_string(r->filter_action),
r->desc);
}
break;
case RULE_COUNTER:
if (r->iaddr != 0) {
@ -354,6 +373,22 @@ parse_rule_payload(struct nftnl_expr *e, rule_t *r)
len == sizeof(uint8_t)) {
*regptr = RULE_REG_IP_PROTO;
return;
} else if (offset == offsetof(struct ipv6hdr, nexthdr) &&
len == sizeof(uint8_t)) {
*regptr = RULE_REG_IP6_PROTO;
return;
} else if (offset == offsetof(struct ipv6hdr, daddr) &&
len == sizeof(struct in6_addr)) {
*regptr = RULE_REG_IP6_DEST_ADDR;
return;
} else if (offset == offsetof(struct ipv6hdr, saddr) &&
len == sizeof(struct in6_addr)) {
*regptr = RULE_REG_IP6_SRC_ADDR;
return;
} else if (offset == offsetof(struct ipv6hdr, saddr) &&
len == sizeof(struct in6_addr) * 2) {
*regptr = RULE_REG_IP6_SD_ADDR;
return;
}
case NFT_PAYLOAD_TRANSPORT_HEADER:
if (offset == offsetof(struct tcphdr, dest) &&
@ -383,6 +418,7 @@ parse_rule_cmp(struct nftnl_expr *e, rule_t *r) {
uint32_t op, sreg;
uint16_t *ports;
in_addr_t *addrp;
struct in6_addr *addrp6;
data_val = (void *)nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &data_len);
sreg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
@ -406,6 +442,12 @@ parse_rule_cmp(struct nftnl_expr *e, rule_t *r) {
r->reg1_type = RULE_REG_NONE;
return;
}
case RULE_REG_IP6_SRC_ADDR:
if (data_len == sizeof(struct in6_addr) && op == NFT_CMP_EQ) {
r->rhost6 = *(struct in6_addr *)data_val;
r->reg1_type = RULE_REG_NONE;
return;
}
case RULE_REG_IP_DEST_ADDR:
if (data_len == sizeof(in_addr_t) && op == NFT_CMP_EQ) {
if (r->type == RULE_FILTER) {
@ -416,6 +458,16 @@ parse_rule_cmp(struct nftnl_expr *e, rule_t *r) {
r->reg1_type = RULE_REG_NONE;
return;
}
case RULE_REG_IP6_DEST_ADDR:
if (data_len == sizeof(struct in6_addr) && op == NFT_CMP_EQ) {
if (r->type == RULE_FILTER) {
r->iaddr6 = *(struct in6_addr *)data_val;
} else {
r->rhost6 = *(struct in6_addr *)data_val;
}
r->reg1_type = RULE_REG_NONE;
return;
}
case RULE_REG_IP_SD_ADDR:
if (data_len == sizeof(in_addr_t) * 2 && op == NFT_CMP_EQ) {
addrp = (in_addr_t *)data_val;
@ -424,7 +476,16 @@ parse_rule_cmp(struct nftnl_expr *e, rule_t *r) {
r->reg1_type = RULE_REG_NONE;
return;
}
case RULE_REG_IP6_SD_ADDR:
if (data_len == sizeof(struct in6_addr) * 2 && op == NFT_CMP_EQ) {
addrp6 = (struct in6_addr *)data_val;
r->iaddr6 = addrp6[0];
r->rhost6 = addrp6[1];
r->reg1_type = RULE_REG_NONE;
return;
}
case RULE_REG_IP_PROTO:
case RULE_REG_IP6_PROTO:
if (data_len == sizeof(uint8_t) && op == NFT_CMP_EQ) {
r->proto = *(uint8_t *)data_val;
r->reg1_type = RULE_REG_NONE;
@ -439,7 +500,7 @@ parse_rule_cmp(struct nftnl_expr *e, rule_t *r) {
case RULE_REG_TCP_SD_PORT:
if (data_len == sizeof(uint16_t) * 2 && op == NFT_CMP_EQ) {
ports = (uint16_t *)data_val;
r->iport = ntohs(ports[0]);
r->eport = ntohs(ports[0]);
r->rport = ntohs(ports[1]);
r->reg1_type = RULE_REG_NONE;
return;
@ -447,8 +508,10 @@ parse_rule_cmp(struct nftnl_expr *e, rule_t *r) {
default:
break;
}
syslog(LOG_DEBUG, "Unknown cmp (r1type:%d, data_len:%d, op:%d)",
r->reg1_type, data_len, op);
return;
}
@ -456,8 +519,7 @@ static int
rule_expr_cb(struct nftnl_expr *e, void *data)
{
rule_t *r = data;
const char *attr_name = nftnl_expr_get_str(e,
NFTNL_EXPR_NAME);
const char *attr_name = nftnl_expr_get_str(e, NFTNL_EXPR_NAME);
if (strncmp("cmp", attr_name, sizeof("cmp")) == 0) {
parse_rule_cmp(e, r);
@ -472,8 +534,9 @@ rule_expr_cb(struct nftnl_expr *e, void *data)
} else if (strncmp("immediate", attr_name, sizeof("immediate")) == 0) {
parse_rule_immediate(e, r);
} else {
syslog(LOG_ERR, "unknown attr: %s\n", attr_name);
syslog(LOG_DEBUG, "unknown attr: %s\n", attr_name);
}
return MNL_CB_OK;
}
@ -487,8 +550,12 @@ table_cb(const struct nlmsghdr *nlh, void *data)
struct nftnl_expr_iter *itr;
rule_t *r;
char *chain;
char *descr;
int index_filter, index_peer, index_redirect;
UNUSED(data);
index_filter = index_peer = index_redirect = 0;
r = malloc(sizeof(rule_t));
memset(r, 0, sizeof(rule_t));
@ -504,9 +571,9 @@ table_cb(const struct nlmsghdr *nlh, void *data)
}
chain = (char *)nftnl_rule_get_data(t, NFTNL_RULE_CHAIN, &len);
if (strcmp(chain, miniupnpd_nft_nat_chain) != 0 &&
strcmp(chain, miniupnpd_nft_peer_chain) != 0 &&
strcmp(chain, miniupnpd_nft_forward_chain) != 0) {
if (strcmp(chain, miniupnpd_nat_chain) != 0 &&
strcmp(chain, miniupnpd_nat_postrouting_chain) != 0 &&
strcmp(chain, miniupnpd_forward_chain) != 0) {
goto rule_skip;
}
@ -515,8 +582,11 @@ table_cb(const struct nlmsghdr *nlh, void *data)
r->chain = strdup(chain);
r->family = *(uint32_t*)nftnl_rule_get_data(t, NFTNL_RULE_FAMILY,
&len);
r->desc = (char *)nftnl_rule_get_data(t, NFTNL_RULE_USERDATA,
&len);
descr = (char *)nftnl_rule_get_data(t, NFTNL_RULE_USERDATA,
&r->desc_len);
if (r->desc_len > 0)
r->desc = strdup(descr);
r->handle = *(uint32_t*)nftnl_rule_get_data(t,
NFTNL_RULE_HANDLE,
&len);
@ -525,9 +595,6 @@ table_cb(const struct nlmsghdr *nlh, void *data)
} else if (strcmp(r->table, NFT_TABLE_FILTER) == 0) {
r->type = RULE_FILTER;
}
if (strcmp(r->chain, miniupnpd_nft_peer_chain) == 0) {
rule_list_peer_length++;
}
itr = nftnl_expr_iter_create(t);
@ -537,9 +604,18 @@ table_cb(const struct nlmsghdr *nlh, void *data)
if (r->type == RULE_NONE) {
free(r);
} else if (strcmp(r->chain, miniupnpd_nat_postrouting_chain) == 0) {
r->index = index_peer;
LIST_INSERT_HEAD(&head_peer, r, entry);
index_peer++;
} else if (strcmp(r->chain, miniupnpd_nat_chain) == 0) {
r->index = index_redirect;
LIST_INSERT_HEAD(&head_redirect, r, entry);
index_redirect++;
} else {
LIST_INSERT_HEAD(&head, r, entry);
rule_list_length++;
r->index = index_filter;
LIST_INSERT_HEAD(&head_filter, r, entry);
index_filter++;
}
rule_skip:
@ -550,66 +626,50 @@ err:
}
void
reflesh_nft_redirect_cache(void)
reflesh_nft_cache_filter()
{
rule_t *p;
int i;
uint32_t len;
if (redirect_cache != NULL) {
free(redirect_cache);
}
len = rule_list_length - rule_list_peer_length;
if (len == 0) {
redirect_cache = NULL;
if (rule_list_filter_validate == RULE_CACHE_VALID) {
return;
}
redirect_cache = (rule_t **)malloc(sizeof(rule_t *) * len);
bzero(redirect_cache, sizeof(rule_t *) * len);
reflesh_nft_cache(&head_filter, NFT_TABLE_FILTER, miniupnpd_forward_chain, NFPROTO_INET);
i = 0;
LIST_FOREACH(p, &head, entry) {
if (strcmp(p->chain, miniupnpd_nft_nat_chain) == 0 &&
(p->type == RULE_NAT || p->type == RULE_SNAT)) {
redirect_cache[i] = p;
i++;
}
}
rule_list_filter_validate = RULE_CACHE_VALID;
return;
}
void
reflesh_nft_peer_cache(void)
reflesh_nft_cache_peer()
{
rule_t *p;
int i;
if (peer_cache != NULL) {
free(peer_cache);
}
if (rule_list_peer_length == 0) {
peer_cache = NULL;
if (rule_list_peer_validate == RULE_CACHE_VALID) {
return;
}
peer_cache = (rule_t **)malloc(
sizeof(rule_t *) * rule_list_peer_length);
bzero(peer_cache, sizeof(rule_t *) * rule_list_peer_length);
i = 0;
LIST_FOREACH(p, &head, entry) {
if (strcmp(p->chain, miniupnpd_nft_peer_chain) == 0) {
peer_cache[i] = p;
i++;
}
}
reflesh_nft_cache(&head_peer, NFT_TABLE_NAT, miniupnpd_nat_postrouting_chain, NFPROTO_IPV4);
rule_list_peer_validate = RULE_CACHE_VALID;
return;
}
void
reflesh_nft_cache(uint32_t family)
reflesh_nft_cache_redirect()
{
if (rule_list_redirect_validate == RULE_CACHE_VALID) {
return;
}
reflesh_nft_cache(&head_redirect, NFT_TABLE_NAT, miniupnpd_nat_chain, NFPROTO_IPV4);
rule_list_redirect_validate = RULE_CACHE_VALID;
return;
}
void
reflesh_nft_cache(struct rule_list *head, char *table, const char *chain, uint32_t family)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
@ -618,12 +678,8 @@ reflesh_nft_cache(uint32_t family)
rule_t *p1, *p2;
int ret;
if (rule_list_validate == RULE_CACHE_VALID) {
return;
}
t = NULL;
p1 = LIST_FIRST(&head);
p1 = LIST_FIRST(head);
if (p1 != NULL) {
while(p1 != NULL) {
p2 = (rule_t *)LIST_NEXT(p1, entry);
@ -640,19 +696,7 @@ reflesh_nft_cache(uint32_t family)
p1 = p2;
}
}
LIST_INIT(&head);
t = nftnl_rule_alloc();
if (t == NULL) {
perror("OOM");
exit(EXIT_FAILURE);
}
seq = time(NULL);
nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family,
NLM_F_DUMP, seq);
nftnl_rule_nlmsg_build_payload(nlh, t);
nftnl_rule_free(t);
LIST_INIT(head);
if (nl == NULL) {
nl = mnl_socket_open(NETLINK_NETFILTER);
@ -668,13 +712,25 @@ reflesh_nft_cache(uint32_t family)
}
portid = mnl_socket_get_portid(nl);
t = nftnl_rule_alloc();
if (t == NULL) {
perror("OOM");
exit(EXIT_FAILURE);
}
seq = time(NULL);
nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family,
NLM_F_DUMP, seq);
nftnl_rule_set(t, NFTNL_RULE_TABLE, table);
nftnl_rule_set(t, NFTNL_RULE_CHAIN, chain);
nftnl_rule_nlmsg_build_payload(nlh, t);
nftnl_rule_free(t);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
exit(EXIT_FAILURE);
}
rule_list_peer_length = 0;
rule_list_length = 0;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, seq, portid, table_cb, &type);
@ -682,15 +738,14 @@ reflesh_nft_cache(uint32_t family)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("error");
exit(EXIT_FAILURE);
}
/* mnl_socket_close(nl); */
reflesh_nft_peer_cache();
reflesh_nft_redirect_cache();
rule_list_validate = RULE_CACHE_VALID;
return;
}
@ -831,20 +886,18 @@ expr_add_nat(struct nftnl_rule *r, uint32_t t, uint32_t family,
nftnl_expr_set_u32(e, NFTNL_EXPR_NAT_TYPE, t);
nftnl_expr_set_u32(e, NFTNL_EXPR_NAT_FAMILY, family);
/* To IP Address */
expr_set_reg_val_u32(r, NFT_REG_1, (uint32_t)addr_min);
nftnl_expr_set_u32(e, NFTNL_EXPR_NAT_REG_ADDR_MIN, NFT_REG_1);
nftnl_expr_set_u32(e, NFTNL_EXPR_NAT_REG_ADDR_MAX, NFT_REG_1);
/* To Port */
expr_set_reg_val_u16(r, NFT_REG_2, proto_min);
nftnl_expr_set_u16(e, NFTNL_EXPR_NAT_REG_PROTO_MIN, NFT_REG_2);
nftnl_expr_set_u16(e, NFTNL_EXPR_NAT_REG_PROTO_MAX, NFT_REG_2);
nftnl_expr_set_u32(e, NFTNL_EXPR_NAT_REG_PROTO_MIN, NFT_REG_2);
nftnl_expr_set_u32(e, NFTNL_EXPR_NAT_REG_PROTO_MAX, NFT_REG_2);
nftnl_rule_add_expr(r, e);
}
/*
* Todo: add expr for rhost
*/
struct nftnl_rule *
rule_set_snat(uint8_t family, uint8_t proto,
in_addr_t rhost, unsigned short rport,
@ -854,10 +907,11 @@ rule_set_snat(uint8_t family, uint8_t proto,
const char *handle)
{
struct nftnl_rule *r = NULL;
uint32_t destport;
in_addr_t addr[2];
uint16_t port[2];
uint16_t dport, sport;
uint32_t descr_len;
#ifdef DEBUG
char buf[8192];
#endif
UNUSED(handle);
r = nftnl_rule_alloc();
@ -867,46 +921,67 @@ rule_set_snat(uint8_t family, uint8_t proto,
}
nftnl_rule_set(r, NFTNL_RULE_TABLE, NFT_TABLE_NAT);
nftnl_rule_set(r, NFTNL_RULE_CHAIN, miniupnpd_nft_peer_chain);
nftnl_rule_set(r, NFTNL_RULE_CHAIN, miniupnpd_nat_postrouting_chain);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
if (descr != NULL) {
descr_len = strlen(descr);
nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
descr, descr_len);
}
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
addr[0] = ihost;
addr[1] = rhost;
/* Destination IP */
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, saddr), sizeof(uint32_t)*2);
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, addr, sizeof(uint32_t)*2);
offsetof(struct iphdr, daddr), sizeof(uint32_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &ihost, sizeof(uint32_t));
/* Source IP */
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, saddr), sizeof(in_addr_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &rhost, sizeof(in_addr_t));
/* Protocol */
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, protocol), sizeof(uint8_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));
port[0] = htons(iport);
port[1] = htons(rport);
/* Source and Destination Port of Protocol */
if (proto == IPPROTO_TCP) {
/* Destination Port */
dport = htons(iport);
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct tcphdr, source),
sizeof(uint32_t));
} else if (proto == IPPROTO_UDP) {
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct udphdr, source),
sizeof(uint32_t));
}
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, port, sizeof(uint32_t));
offsetof(struct tcphdr, dest), sizeof(uint16_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t));
destport = htons(eport);
expr_add_nat(r, NFT_NAT_SNAT, AF_INET, ehost, destport, 0);
/* Source Port */
sport = htons(rport);
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct tcphdr, source), sizeof(uint16_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &sport, sizeof(uint16_t));
} else if (proto == IPPROTO_UDP) {
/* Destination Port */
dport = htons(iport);
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct udphdr, dest), sizeof(uint16_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t));
/* Source Port */
sport = htons(rport);
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct udphdr, source), sizeof(uint16_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &sport, sizeof(uint16_t));
}
expr_add_nat(r, NFT_NAT_SNAT, family, ehost, htons(eport), 0);
#ifdef DEBUG
nftnl_rule_snprintf(buf, sizeof(buf), r, NFTNL_OUTPUT_DEFAULT, 0);
fprintf(stdout, "%s\n", buf);
#endif
return r;
}
/*
* Todo: add expr for rhost
*/
struct nftnl_rule *
rule_set_dnat(uint8_t family, const char * ifname, uint8_t proto,
in_addr_t rhost, unsigned short eport,
@ -919,9 +994,11 @@ rule_set_dnat(uint8_t family, const char * ifname, uint8_t proto,
uint64_t handle_num;
uint32_t if_idx;
uint32_t descr_len;
#ifdef DEBUG
char buf[8192];
#endif
UNUSED(handle);
UNUSED(rhost);
r = nftnl_rule_alloc();
if (r == NULL) {
@ -930,8 +1007,9 @@ rule_set_dnat(uint8_t family, const char * ifname, uint8_t proto,
}
nftnl_rule_set(r, NFTNL_RULE_TABLE, NFT_TABLE_NAT);
nftnl_rule_set(r, NFTNL_RULE_CHAIN, miniupnpd_nft_nat_chain);
nftnl_rule_set(r, NFTNL_RULE_CHAIN, miniupnpd_nat_chain);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
if (descr != NULL) {
descr_len = strlen(descr);
nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
@ -950,6 +1028,14 @@ rule_set_dnat(uint8_t family, const char * ifname, uint8_t proto,
sizeof(uint32_t));
}
/* Source IP */
if (rhost != 0) {
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, saddr), sizeof(in_addr_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &rhost, sizeof(in_addr_t));
}
/* Protocol */
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, protocol), sizeof(uint8_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));
@ -957,34 +1043,35 @@ rule_set_dnat(uint8_t family, const char * ifname, uint8_t proto,
if (proto == IPPROTO_TCP) {
dport = htons(eport);
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct tcphdr, dest),
sizeof(uint16_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport,
sizeof(uint16_t));
offsetof(struct tcphdr, dest), sizeof(uint16_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t));
} else if (proto == IPPROTO_UDP) {
dport = htons(eport);
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct udphdr, dest),
sizeof(uint16_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport,
sizeof(uint16_t));
offsetof(struct udphdr, dest), sizeof(uint16_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t));
}
expr_add_nat(r, NFT_NAT_DNAT, AF_INET, ihost, htons(iport), 0);
expr_add_nat(r, NFT_NAT_DNAT, family, ihost, htons(iport), 0);
#ifdef DEBUG
nftnl_rule_snprintf(buf, sizeof(buf), r, NFTNL_OUTPUT_DEFAULT, 0);
fprintf(stdout, "%s\n", buf);
#endif
return r;
}
struct nftnl_rule *
rule_set_filter(uint8_t family, const char * ifname, uint8_t proto,
in_addr_t rhost, in_addr_t iaddr, unsigned short eport,
unsigned short iport, const char *descr, const char *handle)
in_addr_t rhost, in_addr_t iaddr,
unsigned short eport, unsigned short iport,
unsigned short rport, const char *descr, const char *handle)
{
struct nftnl_rule *r = NULL;
uint16_t dport;
uint64_t handle_num;
uint32_t if_idx;
uint32_t descr_len;
#ifdef DEBUG
char buf[8192];
#endif
UNUSED(eport);
r = nftnl_rule_alloc();
@ -993,9 +1080,98 @@ rule_set_filter(uint8_t family, const char * ifname, uint8_t proto,
exit(EXIT_FAILURE);
}
r = rule_set_filter_common(r, family, ifname, proto, eport, iport, rport, descr, handle);
/* Destination IP */
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, daddr), sizeof(uint32_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &iaddr, sizeof(uint32_t));
/* Source IP */
if (rhost != 0) {
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, saddr), sizeof(in_addr_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &rhost,
sizeof(in_addr_t));
}
/* Protocol */
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, protocol), sizeof(uint8_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));
expr_set_reg_verdict(r, NF_ACCEPT);
#ifdef DEBUG
nftnl_rule_snprintf(buf, sizeof(buf), r, NFTNL_OUTPUT_DEFAULT, 0);
fprintf(stdout, "%s\n", buf);
#endif
return r;
}
struct nftnl_rule *
rule_set_filter6(uint8_t family, const char * ifname, uint8_t proto,
struct in6_addr *rhost6, struct in6_addr *iaddr6,
unsigned short eport, unsigned short iport,
unsigned short rport, const char *descr, const char *handle)
{
struct nftnl_rule *r = NULL;
#ifdef DEBUG
char buf[8192];
#endif
UNUSED(eport);
r = nftnl_rule_alloc();
if (r == NULL) {
perror("OOM");
exit(EXIT_FAILURE);
}
r = rule_set_filter_common(r, family, ifname, proto, eport, iport, rport, descr, handle);
/* Destination IP */
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct ipv6hdr, daddr), sizeof(struct in6_addr));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, iaddr6, sizeof(struct in6_addr));
/* Source IP */
if (rhost6) {
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct ipv6hdr, saddr), sizeof(struct in6_addr));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, rhost6, sizeof(struct in6_addr));
}
/* Protocol */
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct ipv6hdr, nexthdr), sizeof(uint8_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));
expr_set_reg_verdict(r, NF_ACCEPT);
#ifdef DEBUG
nftnl_rule_snprintf(buf, sizeof(buf), r, NFTNL_OUTPUT_DEFAULT, 0);
fprintf(stdout, "%s\n", buf);
#endif
return r;
}
struct nftnl_rule *
rule_set_filter_common(struct nftnl_rule *r, uint8_t family, const char * ifname,
uint8_t proto, unsigned short eport, unsigned short iport,
unsigned short rport, const char *descr, const char *handle)
{
uint16_t dport, sport;
uint64_t handle_num;
uint32_t if_idx;
uint32_t descr_len;
UNUSED(eport);
nftnl_rule_set(r, NFTNL_RULE_TABLE, NFT_TABLE_FILTER);
nftnl_rule_set(r, NFTNL_RULE_CHAIN, miniupnpd_nft_forward_chain);
nftnl_rule_set(r, NFTNL_RULE_CHAIN, miniupnpd_forward_chain);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
if (descr != NULL) {
descr_len = strlen(descr);
nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
@ -1014,28 +1190,29 @@ rule_set_filter(uint8_t family, const char * ifname, uint8_t proto,
sizeof(uint32_t));
}
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, daddr), sizeof(uint32_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &iaddr, sizeof(uint32_t));
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, protocol), sizeof(uint8_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));
/* Destination Port */
dport = htons(iport);
if (proto == IPPROTO_TCP) {
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct tcphdr, dest), sizeof(uint16_t));
} else if (proto == IPPROTO_UDP) {
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct udphdr, dest), sizeof(uint16_t));
}
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t));
if (rhost != 0) {
expr_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
offsetof(struct iphdr, saddr),
sizeof(in_addr_t));
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &rhost,
sizeof(in_addr_t));
/* Source Port */
if (rport != 0) {
sport = htons(rport);
if (proto == IPPROTO_TCP) {
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct tcphdr, source), sizeof(uint16_t));
} else if (proto == IPPROTO_UDP) {
expr_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
offsetof(struct udphdr, source), sizeof(uint16_t));
}
expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &sport, sizeof(uint16_t));
}
expr_set_reg_verdict(r, NF_ACCEPT);
return r;
}
@ -1077,7 +1254,7 @@ nft_mnl_batch_put(char *buf, uint16_t type, uint32_t seq)
}
int
nft_send_request(struct nftnl_rule * rule, uint16_t cmd)
nft_send_request(struct nftnl_rule * rule, uint16_t cmd, enum rule_chain_type chain_type)
{
struct nlmsghdr *nlh;
struct mnl_nlmsg_batch *batch;
@ -1085,7 +1262,13 @@ nft_send_request(struct nftnl_rule * rule, uint16_t cmd)
uint32_t seq = time(NULL);
int ret;
rule_list_validate = RULE_CACHE_INVALID;
if (chain_type == RULE_CHAIN_FILTER)
rule_list_filter_validate = RULE_CACHE_INVALID;
else if (chain_type == RULE_CHAIN_PEER)
rule_list_peer_validate = RULE_CACHE_INVALID;
else if (chain_type == RULE_CHAIN_REDIRECT)
rule_list_redirect_validate = RULE_CACHE_INVALID;
if (nl == NULL) {
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {

View File

@ -10,6 +10,7 @@
#define NFT_TABLE_NAT "nat"
#define NFT_TABLE_FILTER "filter"
#define NFT_DESCR_SIZE 1024
enum rule_reg_type {
RULE_REG_NONE,
@ -18,7 +19,11 @@ enum rule_reg_type {
RULE_REG_IP_SRC_ADDR,
RULE_REG_IP_DEST_ADDR,
RULE_REG_IP_SD_ADDR, /* source & dest */
RULE_REG_IP6_SRC_ADDR,
RULE_REG_IP6_DEST_ADDR,
RULE_REG_IP6_SD_ADDR, /* source & dest */
RULE_REG_IP_PROTO,
RULE_REG_IP6_PROTO,
RULE_REG_TCP_DPORT,
RULE_REG_TCP_SD_PORT, /* source & dest */
RULE_REG_IMM_VAL,
@ -28,11 +33,16 @@ enum rule_reg_type {
enum rule_type {
RULE_NONE,
RULE_NAT,
RULE_SNAT,
RULE_FILTER,
RULE_COUNTER,
};
enum rule_chain_type {
RULE_CHAIN_FILTER,
RULE_CHAIN_PEER,
RULE_CHAIN_REDIRECT,
};
typedef struct rule_ {
LIST_ENTRY(rule_t) entry;
char * table;
@ -47,6 +57,8 @@ typedef struct rule_ {
in_addr_t eaddr;
in_addr_t iaddr;
in_addr_t rhost;
struct in6_addr iaddr6;
struct in6_addr rhost6;
uint16_t eport;
uint16_t iport;
uint16_t rport;
@ -58,15 +70,17 @@ typedef struct rule_ {
uint64_t packets;
uint64_t bytes;
char * desc;
uint32_t desc_len;
int index;
} rule_t;
LIST_HEAD(rule_list, rule_);
extern struct rule_list head;
extern rule_t **peer_cache;
extern rule_t **redirect_cache;
extern struct rule_list head_filter;
extern struct rule_list head_redirect;
extern struct rule_list head_peer;
int
nft_send_request(struct nftnl_rule * rule, uint16_t cmd);
nft_send_request(struct nftnl_rule * rule, uint16_t cmd, enum rule_chain_type type);
struct nftnl_rule *
rule_set_dnat(uint8_t family, const char * ifname, uint8_t proto,
in_addr_t rhost, unsigned short eport,
@ -82,10 +96,21 @@ rule_set_snat(uint8_t family, uint8_t proto,
const char *handle);
struct nftnl_rule *
rule_set_filter(uint8_t family, const char * ifname, uint8_t proto,
in_addr_t rhost, in_addr_t iaddr, unsigned short eport,
unsigned short iport, const char * descr, const char *handle);
in_addr_t rhost, in_addr_t iaddr,
unsigned short eport, unsigned short iport,
unsigned short rport, const char * descr, const char *handle);
struct nftnl_rule *
rule_del_handle(rule_t *r);
void
reflesh_nft_cache(uint32_t family);
rule_set_filter6(uint8_t family, const char * ifname, uint8_t proto,
struct in6_addr *rhost6, struct in6_addr *iaddr6,
unsigned short eport, unsigned short iport,
unsigned short rport, const char *descr, const char *handle);
struct nftnl_rule *
rule_set_filter_common(struct nftnl_rule *r, uint8_t family, const char * ifname,
uint8_t proto, unsigned short eport, unsigned short iport,
unsigned short rport, const char *descr, const char *handle);
struct nftnl_rule *rule_del_handle(rule_t *r);
void reflesh_nft_cache_filter(void);
void reflesh_nft_cache_redirect(void);
void reflesh_nft_cache_peer(void);
void reflesh_nft_cache(struct rule_list *head, char *table, const char *chain, uint32_t family);
void print_rule(rule_t *r);

View File

@ -0,0 +1,434 @@
/* $Id: iptpinhole.c,v 1.19 2018/04/22 19:36:58 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2012-2018 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <dlfcn.h>
#include <limits.h>
#include "../upnputils.h"
#include "nftpinhole.h"
#include <linux/version.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include "tiny_nf_nat.h"
#include "../macros.h"
#include "../config.h"
#include "nftnlrdr.h"
#include "../upnpglobalvars.h"
#include "nftnlrdr_misc.h"
#ifdef DEBUG
#define d_printf(x) do { printf x; } while (0)
#else
#define d_printf(x)
#endif
#ifdef ENABLE_UPNPPINHOLE
static int next_uid = 1;
#define PINEHOLE_LABEL_FORMAT "pinhole-%d ts-%u: %s"
#define PINEHOLE_LABEL_FORMAT_SKIPDESC "pinhole-%d ts-%u: %*s"
void init_iptpinhole(void)
{
return;
}
void shutdown_iptpinhole(void)
{
return;
}
/*
ip saddr <rem_host> ip daddr <int_client> tcp sport <rem_port> tcp dport <int_port>
*/
int add_pinhole(const char * ifname,
const char * rem_host, unsigned short rem_port,
const char * int_client, unsigned short int_port,
int proto, const char * desc, unsigned int timestamp)
{
int uid, res;
char comment[NFT_DESCR_SIZE];
struct nftnl_rule *r = NULL;
struct in6_addr rhost_addr, ihost_addr;
struct in6_addr *rhost_addr_p;
UNUSED(rem_host);
UNUSED(rem_port);
uid = next_uid;
d_printf(("add_pinhole(%s, %s, %s, %d, %d, %d, %s)\n",
ifname, rem_host, int_client, rem_port, int_port, proto, desc));
if (rem_host && rem_host[0] != '\0' && rem_host[0] != '*') {
inet_pton(AF_INET6, rem_host, &rhost_addr);
rhost_addr_p = &rhost_addr;
} else {
rhost_addr_p = NULL;
}
inet_pton(AF_INET6, int_client, &ihost_addr);
snprintf(comment, NFT_DESCR_SIZE,
PINEHOLE_LABEL_FORMAT, uid, timestamp, desc);
r = rule_set_filter6(NFPROTO_INET, ifname, proto,
rhost_addr_p, &ihost_addr,
0, int_port, rem_port, comment, 0);
res = nft_send_request(r, NFT_MSG_NEWRULE, RULE_CHAIN_FILTER);
if (res < 0)
return -1;
if (++next_uid >= 65535) {
next_uid = 1;
}
return uid;
}
int
find_pinhole(const char * ifname,
const char * rem_host, unsigned short rem_port,
const char * int_client, unsigned short int_port,
int proto,
char *desc, int desc_len, unsigned int * timestamp)
{
rule_t *p;
struct in6_addr saddr;
struct in6_addr daddr;
int uid;
unsigned int ts;
UNUSED(ifname);
if (rem_host && (rem_host[0] != '\0')) {
inet_pton(AF_INET6, rem_host, &saddr);
} else {
memset(&saddr, 0, sizeof(struct in6_addr));
}
inet_pton(AF_INET6, int_client, &daddr);
d_printf(("find_pinhole()\n"));
reflesh_nft_cache_filter();
LIST_FOREACH(p, &head_filter, entry) {
// Only forward entries
if (p->type != RULE_FILTER)
continue;
if (p->desc_len == 0)
continue;
if ((proto == p->proto) && (rem_port == ntohs(p->rport))
&& (0 == memcmp(&saddr, &p->iaddr6, sizeof(struct in6_addr)))
&& (int_port == ntohs(p->eport)) &&
(0 == memcmp(&daddr, &p->iaddr6, sizeof(struct in6_addr)))) {
if (sscanf(p->desc, PINEHOLE_LABEL_FORMAT_SKIPDESC, &uid, &ts) != 2) {
syslog(LOG_DEBUG, "rule with label '%s' is not a IGD pinhole", p->desc);
continue;
}
if (timestamp)
*timestamp = ts;
if (desc) {
char * pd = strchr(p->desc, ':');
if(pd) {
pd += 2;
strncpy(desc, pd, desc_len);
}
}
return uid;
}
}
return -2; /* not found */
}
int
delete_pinhole(unsigned short uid)
{
rule_t *p;
struct nftnl_rule *r;
char label_start[NFT_DESCR_SIZE];
char tmp_label[NFT_DESCR_SIZE];
snprintf(label_start, sizeof(label_start),
"pinhole-%hu", uid);
d_printf(("find_pinhole()\n"));
reflesh_nft_cache_filter();
LIST_FOREACH(p, &head_filter, entry) {
// Only forward entries
if (p->type != RULE_FILTER)
continue;
if (p->desc_len == 0)
continue;
strncpy(tmp_label, p->desc, p->desc_len);
strtok(tmp_label, " ");
if (0 == strcmp(tmp_label, label_start)) {
r = rule_del_handle(p);
nft_send_request(r, NFT_MSG_DELRULE, RULE_CHAIN_FILTER);
return 0;
}
}
return -2;
}
int
update_pinhole(unsigned short uid, unsigned int timestamp)
{
char iaddr[INET6_ADDRSTRLEN];
char raddr[INET6_ADDRSTRLEN];
char label_start[NFT_DESCR_SIZE];
char tmp_label[NFT_DESCR_SIZE];
char desc[NFT_DESCR_SIZE];
char ifname[IFNAMSIZ];
char * tmp_p;
uint32_t ext_if_indx;
int proto;
unsigned short iport, rport;
rule_t *p;
d_printf(("update_pinhole()\n"));
reflesh_nft_cache_filter();
proto = -1;
LIST_FOREACH(p, &head_filter, entry) {
// Only forward entries
if (p->type != RULE_FILTER)
continue;
if (p->desc_len == 0)
continue;
strncpy(tmp_label, p->desc, p->desc_len);
strtok(tmp_label, " ");
if (0 == strcmp(tmp_label, label_start)) {
/* Source IP Address */
inet_ntop(AF_INET6, &p->rhost6, raddr, sizeof(raddr));
/* Source Port */
rport = p->eport;
/* Destination IP Address */
inet_ntop(AF_INET6, &p->iaddr6, iaddr, sizeof(iaddr));
/* Destination Port */
iport = p->iport;
proto = p->proto;
ext_if_indx = p->ingress_ifidx;
if_indextoname(ext_if_indx, ifname);
tmp_p = tmp_label;
strsep(&tmp_p, " ");
if (tmp_p) {
strncpy(desc, tmp_p, NFT_DESCR_SIZE);
} else {
desc[0] = '\0';
}
break;
}
}
if (proto == -1)
return -2;
delete_pinhole(uid);
add_pinhole(ifname, raddr, rport, iaddr, iport, proto, desc, timestamp);
return 0;
}
int
get_pinhole_info(unsigned short uid,
char * rem_host, int rem_hostlen,
unsigned short * rem_port,
char * int_client, int int_clientlen,
unsigned short * int_port,
int * proto, char * desc, int desclen,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes)
{
rule_t *p;
unsigned int ts;
char label_start[NFT_DESCR_SIZE];
char tmp_label[NFT_DESCR_SIZE];
snprintf(label_start, sizeof(label_start),
"pinhole-%hu", uid);
d_printf(("get_pinhole_info()\n"));
reflesh_nft_cache_filter();
LIST_FOREACH(p, &head_filter, entry) {
// Only forward entries
if (p->type != RULE_FILTER)
continue;
if (p->desc_len == 0)
continue;
strncpy(tmp_label, p->desc, p->desc_len);
strtok(tmp_label, " ");
if (0 == strcmp(tmp_label, label_start)) {
/* Source IP Address */
if (rem_host && (rem_host[0] != '\0')) {
if(inet_ntop(AF_INET6, &p->rhost6, rem_host, rem_hostlen) == NULL)
return -1;
}
/* Source Port */
if (rem_port)
*rem_port = p->rport;
/* Destination IP Address */
if (int_client) {
if(inet_ntop(AF_INET6, &p->iaddr6, int_client, int_clientlen) == NULL)
return -1;
}
/* Destination Port */
if (int_port)
*int_port = p->eport;
if (proto)
*proto = p->proto;
if (timestamp) {
if (sscanf(p->desc, PINEHOLE_LABEL_FORMAT_SKIPDESC,(int *) &uid, &ts) != 2) {
syslog(LOG_DEBUG, "rule with label '%s' is not a IGD pinhole", p->desc);
continue;
}
*timestamp = ts;
}
if (desc)
strncpy(desc, p->desc, desclen);
if (packets || bytes) {
if (packets)
*packets = p->packets;
if (bytes)
*bytes = p->bytes;
}
break;
}
}
d_printf(("end_pinhole_info()\n"));
return 0;
}
int get_pinhole_uid_by_index(int index)
{
UNUSED(index);
return -42;
}
int
clean_pinhole_list(unsigned int * next_timestamp)
{
rule_t *p;
struct nftnl_rule *r;
time_t current_time;
unsigned int ts;
int uid;
unsigned int min_ts = UINT_MAX;
int min_uid = INT_MAX, max_uid = -1;
int n = 0;
current_time = upnp_time();
d_printf(("clean_pinhole_list()\n"));
reflesh_nft_cache_filter();
LIST_FOREACH(p, &head_filter, entry) {
// Only forward entries
if (p->type != RULE_FILTER)
continue;
if (p->desc_len == 0)
continue;
if (sscanf(p->desc, PINEHOLE_LABEL_FORMAT_SKIPDESC, &uid, &ts) != 2) {
syslog(LOG_DEBUG, "rule with label '%s' is not a IGD pinhole", p->desc);
continue;
}
if (ts <= (unsigned int)current_time) {
syslog(LOG_INFO, "removing expired pinhole '%s'", p->desc);
r = rule_del_handle(p);
nft_send_request(r, NFT_MSG_DELRULE, RULE_CHAIN_FILTER);
n++;
} else {
if (uid > max_uid)
max_uid = uid;
else if (uid < min_uid)
min_uid = uid;
if (ts < min_ts)
min_ts = ts;
}
}
if (next_timestamp && (min_ts != UINT_MAX))
*next_timestamp = min_ts;
if (max_uid > 0) {
if (((min_uid - 32000) <= next_uid) && (next_uid <= max_uid)) {
next_uid = max_uid + 1;
}
if (next_uid >= 65535) {
next_uid = 1;
}
}
return n; /* number of rules removed */
}
#endif /* ENABLE_UPNPPINHOLE */

View File

@ -0,0 +1,43 @@
/* $Id: nftpinhole.h,v 1.10 2016/01/19 10:03:30 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2012-2016 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef NFTPINHOLE_H_INCLUDED
#define NFTPINHOLE_H_INCLUDED
#ifdef ENABLE_UPNPPINHOLE
#include <sys/types.h>
int find_pinhole(const char * ifname,
const char * rem_host, unsigned short rem_port,
const char * int_client, unsigned short int_port,
int proto,
char *desc, int desc_len, unsigned int * timestamp);
int add_pinhole(const char * ifname,
const char * rem_host, unsigned short rem_port,
const char * int_client, unsigned short int_port,
int proto, const char *desc, unsigned int timestamp);
int update_pinhole(unsigned short uid, unsigned int timestamp);
int delete_pinhole(unsigned short uid);
int
get_pinhole_info(unsigned short uid,
char * rem_host, int rem_hostlen, unsigned short * rem_port,
char * int_client, int int_clientlen,
unsigned short * int_port,
int * proto, char * desc, int desclen,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes);
int get_pinhole_uid_by_index(int index);
int clean_pinhole_list(unsigned int * next_timestamp);
#endif /* ENABLE_UPNPPINHOLE */
#endif

View File

@ -1,5 +1,5 @@
#! /sbin/nft -f
delete chain nat miniupnpd
delete chain nat miniupnpd-pcp-peer
delete chain filter miniupnpd
delete chain nat MINIUPNPD
delete chain nat MINIUPNPD-POSTROUTING
delete chain filter MINIUPNPD

View File

@ -1,5 +1,5 @@
#! /sbin/nft -f
flush chain ip nat miniupnpd
flush chain ip nat miniupnpd-pcp-peer
flush chain ip filter miniupnpd
flush chain ip nat MINIUPNPD
flush chain ip nat MINIUPNPD-POSTROUTING
flush chain inet filter MINIUPNPD

View File

@ -13,35 +13,35 @@ if [ $nft_nat_exists -eq "1" ]; then
fi
if [ $nft_filter_exists -eq "1" ]; then
echo "create filter"
nft "add table filter"
nft "add table inet filter"
fi
if [ $nft_mangle_exists -eq "1" ]; then
echo "create mangle"
nft "add table mangle"
fi
nft list chain nat miniupnpd > /dev/null
nft list chain nat MINIUPNPD > /dev/null
nft_nat_miniupnpd_exists=$?
nft list chain nat miniupnpd-pcp-peer > /dev/null
nft list chain nat MINIUPNPD-POSTROUTING > /dev/null
nft_nat_miniupnpd_pcp_peer_exists=$?
nft list chain filter miniupnpd > /dev/null
nft list chain filter MINIUPNPD > /dev/null
nft_filter_miniupnpd_exists=$?
nft list chain mangle miniupnpd > /dev/null
nft list chain mangle MINIUPNPD > /dev/null
nft_mangle_miniupnpd_exists=$?
if [ $nft_nat_miniupnpd_exists -eq "1" ]; then
echo "create chain in nat"
nft "add chain nat miniupnpd"
nft "add chain nat MINIUPNPD"
fi
if [ $nft_nat_miniupnpd_pcp_peer_exists -eq "1" ]; then
echo "create pcp peer chain in nat"
nft "add chain nat miniupnpd-pcp-peer"
nft "add chain nat MINIUPNPD-POSTROUTING"
fi
if [ $nft_filter_miniupnpd_exists -eq "1" ]; then
echo "create chain in filter "
nft "add chain filter miniupnpd"
nft "add chain filter MINIUPNPD"
fi
if [ $nft_mangle_miniupnpd_exists -eq "1" ]; then
echo "create chain in mangle"
nft "add chain mangle miniupnpd"
nft "add chain mangle MINIUPNPD"
fi

View File

@ -1,5 +1,5 @@
#! /sbin/nft -f
delete rule nat miniupnpd
delete rule nat miniupnpd-pcp-peer
delete rule filter miniupnpd
delete rule nat MINIUPNPD
delete rule nat MINIUPNPD-POSTROUTING
delete rule filter MINIUPNPD

View File

@ -1,4 +1,4 @@
/* $Id: testiptcrdr.c,v 1.18 2012/04/24 22:41:53 nanard Exp $ */
/* $Id: testnftnlrdr.c,v 1.1 2015/04/30 09:05:08 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2012 Thomas Bernard

View File

@ -0,0 +1,27 @@
/* $Id: testiptpinhole.c,v 1.1 2012/04/26 13:50:48 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2012 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <syslog.h>
#include "../config.h"
#include "nftpinhole.h"
#include "../commonrdr.h"
int main(int argc, char * * argv)
{
int uid;
openlog("testnftpinhole", LOG_PERROR|LOG_CONS, LOG_LOCAL0);
uid = add_pinhole("eth0", NULL, 0, "ff::123", 54321, IPPROTO_TCP);
return 0;
}

View File

@ -1,4 +1,4 @@
/* $Id: tiny_nf_nat.h,v 1.1 2011/07/30 13:14:36 nanard Exp $ */
/* $Id: tiny_nf_nat.h,v 1.1 2015/04/30 09:05:08 nanard Exp $ */
/* Only what miniupnpd needs, until linux-libc-dev gains nf_nat.h */
#ifndef TINY_NF_NAT_H