diff --git a/miniupnpd/Makefile.linux_nft b/miniupnpd/Makefile.linux_nft new file mode 100644 index 0000000..55e971b --- /dev/null +++ b/miniupnpd/Makefile.linux_nft @@ -0,0 +1,228 @@ +# MiniUPnP project +# (c) 2015 Tomofumi Hayashi +# http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ +# Author : Tomofumi Hayashi +# for use with GNU Make +# +# options can be passed to genconfig.sh through CONFIG_OPTIONS : +# $ CONFIG_OPTIONS="--ipv6 --igd2" make -f Makefile.linux +# +# To install use : +# $ DESTDIR=/dummyinstalldir make -f Makefile.linux_nft install +# or : +# $ INSTALLPREFIX=/usr/local make -f Makefile.linux_nft install +# or : +# $ make -f Makefile.linux install +# (default INSTALLPREFIX is /usr) +# +# +CFLAGS = -O -g -DDEBUG +CFLAGS ?= -Os +CFLAGS += -fno-strict-aliasing +CFLAGS += -fno-common +CPPFLAGS += -D_GNU_SOURCE +CFLAGS += -Wall +CFLAGS += -Wextra -Wstrict-prototypes -Wdeclaration-after-statement +#CFLAGS += -Wno-missing-field-initializers +CC ?= gcc +RM = rm -f +INSTALL = install +STRIP ?= strip +PKG_CONFIG ?= pkg-config +CP = cp + + +INSTALLPREFIX ?= $(PREFIX)/usr +SBININSTALLDIR = $(INSTALLPREFIX)/sbin +ETCINSTALLDIR = $(PREFIX)/etc/miniupnpd +MANINSTALLDIR = $(INSTALLPREFIX)/share/man/man8 + +BASEOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \ + upnpreplyparse.o minixml.o portinuse.o \ + upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \ + options.o upnppermissions.o minissdp.o natpmp.o pcpserver.o \ + upnpevents.o upnputils.o getconnstatus.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 + +ALLOBJS = $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS) + +PCFILE_FOUND := $(shell $(PKG_CONFIG) --exists libnftnl; echo $$?) + +ifeq (${PCFILE_FOUND},0) + +CFLAGS += $(shell $(PKG_CONFIG) --cflags libnftnl) +LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libnftnl) +LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L libnftnl) +LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-other libnftnl) +else + +ARCH ?= $(shell uname -m | grep -q "x86_64" && echo 64) +endif # ifdef PCFILE_FOUND + +LDLIBS += -lnfnetlink + +TEST := $(shell $(PKG_CONFIG) --atleast-version=1.0.2 libnetfilter_conntrack && $(PKG_CONFIG) --atleast-version=1.0.3 libmnl && echo 1) +ifeq ($(TEST),1) +CPPFLAGS += -DUSE_NFCT +LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libmnl) +LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libnetfilter_conntrack) +endif # ($(TEST),1) + +LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libssl) + +TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o + +EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ + testupnppermissions miniupnpdctl testgetifaddr \ + testgetroute testasyncsendto testportinuse + +.PHONY: all clean install depend genuuid + +all: $(EXECUTABLES) + +clean: + $(RM) $(ALLOBJS) + $(RM) $(EXECUTABLES) + $(RM) testupnpdescgen.o testgetifstats.o + $(RM) testupnppermissions.o testgetifaddr.o + $(RM) testgetroute.o testasyncsendto.o + $(RM) testportinuse.o + $(RM) miniupnpdctl.o + +install: miniupnpd miniupnpd.8 miniupnpd.conf genuuid \ + netfilter/iptables_init.sh netfilter/iptables_removeall.sh \ + netfilter/ip6tables_init.sh netfilter/ip6tables_removeall.sh \ + linux/miniupnpd.init.d.script + $(STRIP) miniupnpd + $(INSTALL) -d $(DESTDIR)$(SBININSTALLDIR) + $(INSTALL) miniupnpd $(DESTDIR)$(SBININSTALLDIR) + $(INSTALL) -d $(DESTDIR)$(ETCINSTALLDIR) + $(INSTALL) netfilter/iptables_init.sh $(DESTDIR)$(ETCINSTALLDIR) + $(INSTALL) netfilter/iptables_removeall.sh $(DESTDIR)$(ETCINSTALLDIR) + $(INSTALL) netfilter/ip6tables_init.sh $(DESTDIR)$(ETCINSTALLDIR) + $(INSTALL) netfilter/ip6tables_removeall.sh $(DESTDIR)$(ETCINSTALLDIR) + $(INSTALL) --mode=0644 -b miniupnpd.conf $(DESTDIR)$(ETCINSTALLDIR) + $(INSTALL) -d $(DESTDIR)$(PREFIX)/etc/init.d + $(INSTALL) linux/miniupnpd.init.d.script $(DESTDIR)$(PREFIX)/etc/init.d/miniupnpd + $(INSTALL) -d $(DESTDIR)$(MANINSTALLDIR) + $(INSTALL) --mode=0644 miniupnpd.8 $(DESTDIR)$(MANINSTALLDIR) + gzip -f $(DESTDIR)$(MANINSTALLDIR)/miniupnpd.8 + +# genuuid is using the uuidgen CLI tool which is part of libuuid +# from the e2fsprogs +# 'cat /proc/sys/kernel/random/uuid' could be also used +genuuid: +ifeq ($(TARGET_OPENWRT),) + sed -i -e "s/^uuid=[-0-9a-f]*/uuid=`(genuuid||uuidgen||uuid) 2>/dev/null`/" miniupnpd.conf +else + sed -i -e "s/^uuid=[-0-9a-f]*/uuid=`($(STAGING_DIR_HOST)/bin/genuuid||$(STAGING_DIR_HOST)/bin/uuidgen||$(STAGING_DIR_HOST)/bin/uuid) 2>/dev/null`/" miniupnpd.conf +endif + +miniupnpd: $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS) + +testupnpdescgen: $(TESTUPNPDESCGENOBJS) + +testgetifstats: testgetifstats.o linux/getifstats.o + +testupnppermissions: testupnppermissions.o upnppermissions.o + +testgetifaddr: testgetifaddr.o getifaddr.o + +testgetroute: testgetroute.o linux/getroute.o upnputils.o + +testasyncsendto: testasyncsendto.o asyncsendto.o upnputils.o \ + linux/getroute.o + +testportinuse: testportinuse.o portinuse.o getifaddr.o \ + netfilter_nft/nftnlrdr.o netfilter_nft/nftnlrdr_misc.o + +miniupnpdctl: miniupnpdctl.o + +config.h: genconfig.sh VERSION + ./genconfig.sh $(CONFIG_OPTIONS) + +depend: config.h + makedepend -f$(MAKEFILE_LIST) -Y \ + $(ALLOBJS:.o=.c) $(TESTUPNPDESCGENOBJS:.o=.c) \ + testgetifstats.c testupnppermissions.c testgetifaddr.c \ + testgetroute.c testasyncsendto.c testportinuse.c \ + miniupnpdctl.c 2>/dev/null + +# DO NOT DELETE + +miniupnpd.o: config.h macros.h upnpglobalvars.h upnppermissions.h +miniupnpd.o: miniupnpdtypes.h upnphttp.h upnpdescgen.h miniupnpdpath.h +miniupnpd.o: getifaddr.h upnpsoap.h options.h minissdp.h upnpredirect.h +miniupnpd.o: upnppinhole.h daemonize.h upnpevents.h asyncsendto.h natpmp.h +miniupnpd.o: pcpserver.h commonrdr.h upnputils.h ifacewatcher.h +upnphttp.o: config.h upnphttp.h upnpdescgen.h miniupnpdpath.h upnpsoap.h +upnphttp.o: upnpevents.h upnputils.h +upnpdescgen.o: config.h getifaddr.h upnpredirect.h upnpdescgen.h +upnpdescgen.o: miniupnpdpath.h upnpglobalvars.h upnppermissions.h +upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h upnpurns.h getconnstatus.h +upnpsoap.o: macros.h config.h upnpglobalvars.h upnppermissions.h +upnpsoap.o: miniupnpdtypes.h upnphttp.h upnpsoap.h upnpreplyparse.h +upnpsoap.o: upnpredirect.h upnppinhole.h getifaddr.h getifstats.h +upnpsoap.o: getconnstatus.h upnpurns.h +upnpreplyparse.o: upnpreplyparse.h minixml.h +minixml.o: minixml.h +portinuse.o: macros.h config.h upnpglobalvars.h upnppermissions.h +portinuse.o: miniupnpdtypes.h getifaddr.h portinuse.h netfilter_nft/nftnlrdr.h +portinuse.o: commonrdr.h +upnpredirect.o: macros.h config.h upnpredirect.h upnpglobalvars.h +upnpredirect.o: upnppermissions.h miniupnpdtypes.h upnpevents.h portinuse.h +upnpredirect.o: netfilter_nft/nftnlrdr.h commonrdr.h +getifaddr.o: config.h getifaddr.h +daemonize.o: daemonize.h config.h +upnpglobalvars.o: config.h upnpglobalvars.h upnppermissions.h +upnpglobalvars.o: miniupnpdtypes.h upnpdescstrings.h +options.o: config.h options.h upnppermissions.h upnpglobalvars.h +options.o: miniupnpdtypes.h +upnppermissions.o: config.h upnppermissions.h +minissdp.o: config.h upnpdescstrings.h miniupnpdpath.h upnphttp.h +minissdp.o: upnpglobalvars.h upnppermissions.h miniupnpdtypes.h minissdp.h +minissdp.o: upnputils.h getroute.h asyncsendto.h codelength.h +natpmp.o: macros.h config.h natpmp.h upnpglobalvars.h upnppermissions.h +natpmp.o: miniupnpdtypes.h getifaddr.h upnpredirect.h commonrdr.h upnputils.h +natpmp.o: portinuse.h asyncsendto.h +pcpserver.o: config.h pcpserver.h macros.h upnpglobalvars.h upnppermissions.h +pcpserver.o: miniupnpdtypes.h pcplearndscp.h upnpredirect.h commonrdr.h +pcpserver.o: getifaddr.h asyncsendto.h pcp_msg_struct.h netfilter_nft/nftnlrdr.h +pcpserver.o: commonrdr.h +upnpevents.o: config.h upnpevents.h miniupnpdpath.h upnpglobalvars.h +upnpevents.o: upnppermissions.h miniupnpdtypes.h upnpdescgen.h upnputils.h +upnputils.o: config.h upnputils.h upnpglobalvars.h upnppermissions.h +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 +pcplearndscp.o: config.h upnpglobalvars.h upnppermissions.h miniupnpdtypes.h +pcplearndscp.o: pcplearndscp.h +asyncsendto.o: asyncsendto.h +linux/getifstats.o: config.h getifstats.h +linux/ifacewatcher.o: config.h ifacewatcher.h config.h minissdp.h +linux/ifacewatcher.o: miniupnpdtypes.h getifaddr.h upnpglobalvars.h +linux/ifacewatcher.o: upnppermissions.h natpmp.h +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 +testupnpdescgen.o: macros.h config.h upnpdescgen.h upnpdescstrings.h +testupnpdescgen.o: getifaddr.h +upnpdescgen.o: config.h getifaddr.h upnpredirect.h upnpdescgen.h +upnpdescgen.o: miniupnpdpath.h upnpglobalvars.h upnppermissions.h +upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h upnpurns.h getconnstatus.h +testgetifstats.o: getifstats.h +testupnppermissions.o: upnppermissions.h config.h +testgetifaddr.o: config.h getifaddr.h +testgetroute.o: getroute.h upnputils.h upnpglobalvars.h upnppermissions.h +testgetroute.o: config.h miniupnpdtypes.h +testasyncsendto.o: miniupnpdtypes.h config.h upnputils.h asyncsendto.h +testportinuse.o: macros.h config.h portinuse.h +miniupnpdctl.o: macros.h diff --git a/miniupnpd/netfilter_nft/Makefile b/miniupnpd/netfilter_nft/Makefile new file mode 100644 index 0000000..19846cf --- /dev/null +++ b/miniupnpd/netfilter_nft/Makefile @@ -0,0 +1,33 @@ +CFLAGS?=-Wall -g -D_GNU_SOURCE -DDEBUG -Wstrict-prototypes -Wdeclaration-after-statement +CC = gcc + +LIBS = -lnftnl -lmnl + +ARCH := $(shell uname -m | grep -q "x86_64" && echo 64) + +all: test_nfct_get testnftnlrdr + +clean: + $(RM) *.o testnftnlcrdr testnftnlpinhole 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) + +test_nfct_get: test_nfct_get.o test_nfct_get.o -lmnl -lnetfilter_conntrack + +test_nfct_get.o: test_nfct_get.c + +testnftnlrdr_peer.o: testnftnlrdr_peer.c + +testnftnlrdr_dscp.o: testnftnlrdr_dscp.c + +nftnlrdr.o: nftnlrdr.c nftnlrdr.h + +nftnlrdr_misc.o: nftnlrdr_misc.c + +iptpinhole.o: iptpinhole.c iptpinhole.h + +upnpglobalvars.o: ../upnpglobalvars.c ../upnpglobalvars.h + $(CC) -c -o $@ $< diff --git a/miniupnpd/netfilter_nft/nfct_get.c b/miniupnpd/netfilter_nft/nfct_get.c new file mode 100644 index 0000000..cbffd28 --- /dev/null +++ b/miniupnpd/netfilter_nft/nfct_get.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include + +#ifdef USE_NFCT +#include +#include + +#include + +struct data_cb_s +{ + struct sockaddr_storage * ext; + uint8_t found; +}; + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nf_conntrack *ct; + struct data_cb_s * d = (struct data_cb_s*) data; + struct sockaddr_in* ext4 = (struct sockaddr_in*) d->ext; + + ct = nfct_new(); + if (ct == NULL) + return MNL_CB_OK; + nfct_nlmsg_parse(nlh, ct); + + if (data) { + ext4->sin_addr.s_addr = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST); + ext4->sin_port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST); + } + d->found = 1; + nfct_destroy(ct); + + return MNL_CB_OK; +} + +int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto, + struct sockaddr_storage* ret_ext) +{ + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + unsigned int seq, portid; + struct nf_conntrack *ct; + int ret; + struct data_cb_s data; + + if ((!src)&&(!dst)) { + return 0; + } + + if (src->sa_family != dst->sa_family) { + return 0; + } + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { +// perror("mnl_socket_open"); + goto free_nl; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +// perror("mnl_socket_bind"); + goto free_nl; + } + portid = mnl_socket_get_portid(nl); + + memset(buf, 0, sizeof(buf)); + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = src->sa_family; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + ct = nfct_new(); + if (ct == NULL) { + goto free_nl; + } + + nfct_set_attr_u8(ct, ATTR_L3PROTO, src->sa_family); + if (src->sa_family == AF_INET) { + struct sockaddr_in *src4 = (struct sockaddr_in *)src; + struct sockaddr_in *dst4 = (struct sockaddr_in *)dst; + nfct_set_attr_u32(ct, ATTR_IPV4_SRC, src4->sin_addr.s_addr); + nfct_set_attr_u32(ct, ATTR_IPV4_DST, dst4->sin_addr.s_addr); + nfct_set_attr_u16(ct, ATTR_PORT_SRC, src4->sin_port); + nfct_set_attr_u16(ct, ATTR_PORT_DST, dst4->sin_port); + } else if (src->sa_family == AF_INET6) { + struct sockaddr_in6 *src6 = (struct sockaddr_in6 *)src; + struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst; + nfct_set_attr(ct, ATTR_IPV6_SRC, &src6->sin6_addr); + nfct_set_attr(ct, ATTR_IPV6_DST, &dst6->sin6_addr); + nfct_set_attr_u16(ct, ATTR_PORT_SRC, src6->sin6_port); + nfct_set_attr_u16(ct, ATTR_PORT_DST, dst6->sin6_port); + } + nfct_set_attr_u8(ct, ATTR_L4PROTO, proto); + + nfct_nlmsg_build(nlh, ct); + + ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + if (ret == -1) { + goto free_ct; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + data.ext = ret_ext; + data.found = 0; + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, &data); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + +free_ct: + nfct_destroy(ct); +free_nl: + mnl_socket_close(nl); + + return data.found; +} + +#else +#define DST "dst=" +#define DST_PORT "dport=" +#define SRC "src=" +#define SRC_PORT "sport=" +#define IP_CONNTRACK_LOCATION "/proc/net/ip_conntrack" +#define NF_CONNTRACK_LOCATION "/proc/net/nf_conntrack" + +int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto, + struct sockaddr_storage* ret_ext) +{ + FILE *f; + int af; + + if (!src) + return -2; + + af = src->sa_family; + + if ((f = fopen(NF_CONNTRACK_LOCATION, "r")) == NULL) { + if ((f = fopen(IP_CONNTRACK_LOCATION, "r")) == NULL) { + printf("could not read info about connections from the kernel, " + "make sure netfilter is enabled in kernel or by modules.\n"); + return -1; + } + } + + while (!feof(f)) { + char line[256], *str; + memset(line, 0, sizeof(line)); + str = fgets(line, sizeof(line), f); + if (line[0] != 0) { + char *token, *saveptr; + int j; + uint8_t src_f, src_port_f, dst_f, dst_port_f; + src_f=src_port_f=dst_f=dst_port_f=0; + + for (j = 1; ; j++, str = NULL) { + token = strtok_r(str, " ", &saveptr); + if (token == NULL) + break; + + if ((j==2)&&(af!=atoi(token))) + break; + if ((j==4)&&(proto!=atoi(token))) + break; + if (j<=4) + continue; + + if (strncmp(token, SRC, sizeof(SRC) - 1) == 0) { + char *srcip = token + sizeof(SRC) - 1; + uint32_t buf[4]; + memset(buf,0,sizeof(buf)); + + if (inet_pton(af, srcip, buf)!=1) + break; + + if (af==AF_INET) { + struct sockaddr_in *src4=(struct sockaddr_in*)src; + if (!src_f) { + if (src4->sin_addr.s_addr != buf[0]) + break; + src_f = 1; + } + } + } + if (strncmp(token, SRC_PORT, sizeof(SRC_PORT) - 1) == 0) { + char *src_port = token + sizeof(SRC_PORT) - 1; + uint16_t port=atoi(src_port); + + if (af==AF_INET) { + struct sockaddr_in *src4=(struct sockaddr_in*)src; + if (!src_port_f) { + if (ntohs(src4->sin_port) != port) + break; + src_port_f = 1; + } + } + } + + if (strncmp(token, DST, sizeof(DST) - 1) == 0) { + char *dstip = token + sizeof(DST) - 1; + uint32_t buf[4]; + memset(buf,0,sizeof(buf)); + if (inet_pton(af, dstip, buf)!=1) + break; + if (af==AF_INET) { + struct sockaddr_in *dst4=(struct sockaddr_in*)dst; + if (!dst_f) { + if (dst4->sin_addr.s_addr != buf[0]) + break; + dst_f = 1; + } else { + struct sockaddr_in*ret4=(struct sockaddr_in*)ret_ext; + ret_ext->ss_family = AF_INET; + ret4->sin_addr.s_addr = buf[0]; + } + } + } + if (strncmp(token, DST_PORT, sizeof(DST_PORT)-1) == 0) { + char *dst_port = token + sizeof(DST_PORT) - 1; + uint16_t port=atoi(dst_port); + if (af==AF_INET) { + struct sockaddr_in *dst4=(struct sockaddr_in*)dst; + if (!dst_port_f) { + if (ntohs(dst4->sin_port) != port) + break; + dst_port_f = 1; + } else { + struct sockaddr_in*ret4=(struct sockaddr_in*)ret_ext; + ret_ext->ss_family = AF_INET; + ret4->sin_port = htons(port); + } + } + } + } + if (src_f && src_port_f && dst_f && dst_port_f) { + fclose(f); + return 1; + } + } + } + fclose(f); + + return 0; +} +#endif diff --git a/miniupnpd/netfilter_nft/nftnlrdr.c b/miniupnpd/netfilter_nft/nftnlrdr.c new file mode 100644 index 0000000..3c8b3db --- /dev/null +++ b/miniupnpd/netfilter_nft/nftnlrdr.c @@ -0,0 +1,478 @@ +/* + * MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2015 Tomofumi Hayashi + * + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include "tiny_nf_nat.h" + +#include "../macros.h" +#include "../config.h" +#include "nftnlrdr.h" +#include "../upnpglobalvars.h" + +#include "nftnlrdr_misc.h" + +/* dummy init and shutdown functions */ +int init_redirect(void) +{ + return 0; +} + +void shutdown_redirect(void) +{ + return; +} + + +int +add_redirect_rule2(const char * ifname, + const char * rhost, unsigned short eport, + const char * iaddr, unsigned short iport, int proto, + const char * desc, unsigned int timestamp) +{ + struct nft_rule *r; + UNUSED(timestamp); + 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); +} + +/* + * This function submit the rule as following: + * nft add rule nat miniupnpd-pcp-peer ip + * saddr ip daddr tcp sport + * tcp dport snat : + */ +int +add_peer_redirect_rule2(const char * ifname, + const char * rhost, unsigned short rport, + const char * eaddr, unsigned short eport, + const char * iaddr, unsigned short iport, int proto, + const char * desc, unsigned int timestamp) +{ + struct nft_rule *r; + UNUSED(ifname); UNUSED(timestamp); + + 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); +} + +/* + * This function submit the rule as following: + * nft add rule filter miniupnpd + * ip daddr tcp dport accept + * + */ +int +add_filter_rule2(const char * ifname, + const char * rhost, const char * iaddr, + unsigned short eport, unsigned short iport, + int proto, const char * desc) +{ + struct nft_rule *r = NULL; + in_addr_t rhost_addr = 0; + if (rhost != NULL) { + rhost_addr = inet_addr(rhost); + } + r = rule_set_filter(NFPROTO_IPV4, ifname, proto, + rhost_addr, inet_addr(iaddr), eport, iport, + desc, 0); + return nft_send_request(r, NFT_MSG_NEWRULE); +} + +/* + * add_peer_dscp_rule2() is not supported due to nft does not support + * dscp set. + */ +int +add_peer_dscp_rule2(const char * ifname, + const char * rhost, unsigned short rport, + unsigned char dscp, + const char * iaddr, unsigned short iport, int proto, + const char * desc, unsigned int timestamp) +{ + UNUSED(ifname); UNUSED(rhost); UNUSED(rport); + UNUSED(dscp); UNUSED(iaddr); UNUSED(iport); UNUSED(proto); + UNUSED(desc); UNUSED(timestamp); + syslog(LOG_ERR, "add_peer_dscp_rule2: not supported"); + return 0; +} + +/* + * Clear all rules corresponding eport/proto + */ +int +delete_redirect_and_filter_rules(unsigned short eport, int proto) +{ + rule_t *p; + struct nft_rule *r = NULL; + in_addr_t iaddr = 0; + uint16_t iport = 0; + extern void print_rule(rule_t *r) ; + + printf("delete_redirect_and_filter_rules(%d %d)\n", eport, proto); + reflesh_nft_cache(NFPROTO_IPV4); + LIST_FOREACH(p, &head, entry) { + if (p->eport == eport && p->proto == proto && + (p->type == RULE_NAT || p->type == RULE_SNAT)) { + iaddr = p->iaddr; + iport = p->iport; + r = rule_del_handle(p); + /* Todo: send bulk request */ + nft_send_request(r, NFT_MSG_DELRULE); + } + } + + if (iaddr == 0 && iport == 0) { + return -1; + } + reflesh_nft_cache(NFPROTO_IPV4); + LIST_FOREACH(p, &head, 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); + } + } + + return 0; +} + +/* + * get peer by index as array. + * return -1 when not found. + */ +int +get_peer_rule_by_index(int index, + char * ifname, unsigned short * eport, + char * iaddr, int iaddrlen, unsigned short * iport, + int * proto, char * desc, int desclen, + char * rhost, int rhostlen, unsigned short * rport, + 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); + + printf("get_peer_rule_by_index()\n"); + reflesh_nft_cache(NFPROTO_IPV4); + for (i = 0; peer_cache[i] != NULL; i++) { + if (index == i) { + r = peer_cache[i]; + if (ifname != NULL) { + if_indextoname(r->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); + } + + /* + * TODO: Implement counter in case of add {nat,filter} + */ + return 0; + } + } + return -1; +} + +/* + * get_redirect_rule() + * returns -1 if the rule is not found + */ +int +get_redirect_rule(const char * ifname, unsigned short eport, int proto, + char * iaddr, int iaddrlen, unsigned short * iport, + char * desc, int desclen, + char * rhost, int rhostlen, + unsigned int * timestamp, + u_int64_t * packets, u_int64_t * bytes) +{ + return get_nat_redirect_rule(NFT_TABLE_NAT, + ifname, eport, proto, + iaddr, iaddrlen, iport, + desc, desclen, + rhost, rhostlen, + timestamp, packets, bytes); +} + +/* + * get_redirect_rule_by_index() + * return -1 when the rule was not found + */ +int +get_redirect_rule_by_index(int index, + char * ifname, unsigned short * eport, + char * iaddr, int iaddrlen, unsigned short * iport, + int * proto, char * desc, int desclen, + char * rhost, int rhostlen, + 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); + + printf("get_redirect_rule_by_index()\n"); + reflesh_nft_cache(NFPROTO_IPV4); + for (i = 0; redirect_cache[i] != NULL; i++) { + if (index == i) { + r = redirect_cache[i]; + if (ifname != NULL) { + if_indextoname(r->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); + } + + /* + * TODO: Implement counter in case of add {nat,filter} + */ + return 0; + } + } + return -1; +} + +/* + * return -1 not found. + * return 0 found + */ +int +get_nat_redirect_rule(const char * nat_chain_name, const char * ifname, + unsigned short eport, int proto, + char * iaddr, int iaddrlen, unsigned short * iport, + char * desc, int desclen, + char * rhost, int rhostlen, + unsigned int * timestamp, + u_int64_t * packets, u_int64_t * bytes) +{ + rule_t *p; + struct in_addr addr; + char *addr_str; + UNUSED(nat_chain_name); + UNUSED(ifname); + UNUSED(iaddrlen); + UNUSED(timestamp); + UNUSED(packets); + UNUSED(bytes); + + printf("get_nat_redirect_rule()\n"); + reflesh_nft_cache(NFPROTO_IPV4); + + LIST_FOREACH(p, &head, 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 (desc != NULL && p->desc) { + strncpy(desc, p->desc, desclen); + } + *iport = p->iport; + return 0; + } + } + + 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) +{ + uint32_t capacity; + rule_t *p; + unsigned short *array; + unsigned short *tmp; + + printf("get_portmappings_in_range()\n"); + *number = 0; + capacity = 128; + array = calloc(capacity, sizeof(unsigned short)); + + if (array == NULL) { + syslog(LOG_ERR, "get_portmappings_in_range(): calloc error"); + return NULL; + } + + LIST_FOREACH(p, &head, entry) { + if (p->proto == proto && + startport <= p->eport && + p->eport <= endport) { + + if (*number >= capacity) { + tmp = realloc(array, + sizeof(unsigned short)*capacity); + if (tmp == NULL) { + syslog(LOG_ERR, + "get_portmappings_in_range(): " + "realloc(%u) error", + (unsigned)sizeof(unsigned short)*capacity); + *number = 0; + free(array); + return NULL; + } + array = tmp; + } + array[*number] = p->eport; + (*number)++; + } + } + return array; +} + +/* for debug */ +/* read the "filter" and "nat" tables */ +int +list_redirect_rule(const char * ifname) +{ + rule_t *p; + UNUSED(ifname); + + reflesh_nft_cache(NFPROTO_IPV4); + + LIST_FOREACH(p, &head, entry) { + print_rule(p); + } + + return -1; + return 0; +} + + +#if 0 +/* delete_rule_and_commit() : + * subfunction used in delete_redirect_and_filter_rules() */ +static int +delete_rule_and_commit(unsigned int index, IPTC_HANDLE h, + const char * miniupnpd_chain, + const char * logcaller) +{ +/* TODO: Implement it */ +} + +/* TODO: Implement it */ +static void +print_iface(const char * iface, const unsigned char * mask, int invert) +{ + unsigned i; + if(mask[0] == 0) + return; + if(invert) + printf("! "); + for(i=0; i> 24, (ip >> 16) & 0xff, + (ip >> 8) & 0xff, ip & 0xff); +} +#endif + +#endif /* if 0 */ diff --git a/miniupnpd/netfilter_nft/nftnlrdr.h b/miniupnpd/netfilter_nft/nftnlrdr.h new file mode 100644 index 0000000..1acf4b3 --- /dev/null +++ b/miniupnpd/netfilter_nft/nftnlrdr.h @@ -0,0 +1,84 @@ +/* + * MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2015 Tomofumi Hayashi + * + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution. + */ + +#ifndef NFTNLRDR_H_INCLUDED +#define NFTNLRDR_H_INCLUDED + +#include "../commonrdr.h" +int init_redirect(void); +void shutdown_redirect(void); + +int +add_redirect_rule2(const char * ifname, + const char * rhost, unsigned short eport, + const char * iaddr, unsigned short iport, int proto, + const char * desc, unsigned int timestamp); + +int +add_peer_redirect_rule2(const char * ifname, + const char * rhost, unsigned short rport, + const char * eaddr, unsigned short eport, + const char * iaddr, unsigned short iport, int proto, + const char * desc, unsigned int timestamp); + +int +add_filter_rule2(const char * ifname, + const char * rhost, const char * iaddr, + unsigned short eport, unsigned short iport, + int proto, const char * desc); + +int +delete_redirect_and_filter_rules(unsigned short eport, int proto); + +int +add_peer_dscp_rule2(const char * ifname, + const char * rhost, unsigned short rport, + unsigned char dscp, + const char * iaddr, unsigned short iport, int proto, + const char * desc, unsigned int timestamp); + +int +get_peer_rule_by_index(int index, + char * ifname, unsigned short * eport, + char * iaddr, int iaddrlen, unsigned short * iport, + int * proto, char * desc, int desclen, + char * rhost, int rhostlen, unsigned short * rport, + unsigned int * timestamp, + u_int64_t * packets, u_int64_t * bytes); +int +get_nat_redirect_rule(const char * nat_chain_name, const char * ifname, + unsigned short eport, int proto, + char * iaddr, int iaddrlen, unsigned short * iport, + char * desc, int desclen, + char * rhost, int rhostlen, + unsigned int * timestamp, + u_int64_t * packets, u_int64_t * bytes); +int +get_redirect_rule_by_index(int index, + char * ifname, unsigned short * eport, + char * iaddr, int iaddrlen, unsigned short * iport, + int * proto, char * desc, int desclen, + char * rhost, int rhostlen, + unsigned int * timestamp, + u_int64_t * packets, u_int64_t * bytes); + +unsigned short * +get_portmappings_in_range(unsigned short startport, unsigned short endport, + int proto, unsigned int * number); + +/* in nfct_get.c */ +int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto, + struct sockaddr* ret_ext); + +/* for debug */ +int +list_redirect_rule(const char * ifname); + +#endif + diff --git a/miniupnpd/netfilter_nft/nftnlrdr_misc.c b/miniupnpd/netfilter_nft/nftnlrdr_misc.c new file mode 100644 index 0000000..2e61910 --- /dev/null +++ b/miniupnpd/netfilter_nft/nftnlrdr_misc.c @@ -0,0 +1,1079 @@ +/* + * MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2015 Tomofumi Hayashi + * + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "nftnlrdr_misc.h" +#include "../macros.h" +#include "../upnpglobalvars.h" + +#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; + + +static const char * +get_family_string(uint32_t family) +{ + switch (family) { + case NFPROTO_IPV4: + return "ipv4"; + } + return "unknown family"; +} + +static const char * +get_proto_string(uint32_t proto) +{ + switch (proto) { + case IPPROTO_TCP: + return "tcp"; + case IPPROTO_UDP: + return "udp"; + } + return "unknown proto"; +} + +static const char * +get_verdict_string(uint32_t val) +{ + switch (val) { + case NF_ACCEPT: + return "accept"; + case NF_DROP: + return "drop"; + default: + return "unknown verdict"; + } +} + +void +print_rule(rule_t *r) +{ + struct in_addr addr; + char *iaddr_str = NULL, *rhost_str = NULL, *eaddr_str = NULL; + char ifname_buf[IF_NAMESIZE]; + + switch (r->type) { + case RULE_NAT: + if (r->iaddr != 0) { + addr.s_addr = r->iaddr; + iaddr_str = strdupa(inet_ntoa(addr)); + } + if (r->rhost != 0) { + addr.s_addr = r->rhost; + rhost_str = strdupa(inet_ntoa(addr)); + } + if (r->eaddr != 0) { + addr.s_addr = r->eaddr; + eaddr_str = strdupa(inet_ntoa(addr)); + } + if (r->nat_type == NFT_NAT_DNAT) { + printf("%"PRIu64":[%s/%s] iif %s, %s/%s, %d -> " + "%s:%d (%s)\n", + r->handle, + r->table, r->chain, + if_indextoname(r->ifidx, ifname_buf), + get_family_string(r->family), + get_proto_string(r->proto), r->eport, + iaddr_str, r->iport, + r->desc); + } else if (r->nat_type == NFT_NAT_SNAT) { + printf("%"PRIu64":[%s/%s] " + "nat type:%d, family:%d, ifidx: %d, " + "eaddr: %s, eport:%d, " + "proto:%d, iaddr: %s, " + "iport:%d, rhost:%s rport:%d (%s)\n", + r->handle, r->table, r->chain, + r->nat_type, r->family, r->ifidx, + eaddr_str, r->eport, + r->proto, iaddr_str, r->iport, + rhost_str, r->rport, + r->desc); + } else { + printf("%"PRIu64":[%s/%s] " + "nat type:%d, family:%d, ifidx: %d, " + "eaddr: %s, eport:%d, " + "proto:%d, iaddr: %s, iport:%d, rhost:%s (%s)\n", + r->handle, r->table, r->chain, + r->nat_type, r->family, r->ifidx, + eaddr_str, r->eport, + r->proto, iaddr_str, r->iport, rhost_str, + r->desc); + } + break; + case RULE_FILTER: + if (r->iaddr != 0) { + addr.s_addr = r->iaddr; + iaddr_str = strdupa(inet_ntoa(addr)); + } + if (r->rhost != 0) { + addr.s_addr = r->rhost; + rhost_str = strdupa(inet_ntoa(addr)); + } + 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), + rhost_str, + iaddr_str, r->eport, + get_verdict_string(r->filter_action), + r->desc); + break; + case RULE_COUNTER: + if (r->iaddr != 0) { + addr.s_addr = r->iaddr; + iaddr_str = strdupa(inet_ntoa(addr)); + } + if (r->rhost != 0) { + addr.s_addr = r->iaddr; + rhost_str = strdupa(inet_ntoa(addr)); + } + printf("%"PRIu64":[%s/%s] %s/%s, %s:%d: " + "packets:%"PRIu64", bytes:%"PRIu64"\n", + r->handle, r->table, r->chain, + get_family_string(r->family), get_proto_string(r->proto), + iaddr_str, r->eport, r->packets, r->bytes); + break; + default: + printf("XXX: unknown type: %d\n", r->type); + } +} + +static enum rule_reg_type * +get_reg_type_ptr(rule_t *r, uint32_t dreg) +{ + switch (dreg) { + case NFT_REG_1: + return &r->reg1_type; + case NFT_REG_2: + return &r->reg2_type; + default: + return NULL; + } +} + +static uint32_t * +get_reg_val_ptr(rule_t *r, uint32_t dreg) +{ + switch (dreg) { + case NFT_REG_1: + return &r->reg1_val; + case NFT_REG_2: + return &r->reg2_val; + default: + return NULL; + } +} + +static void +set_reg (rule_t *r, uint32_t dreg, enum rule_reg_type type, uint32_t val) +{ + if (dreg == NFT_REG_1) { + r->reg1_type = type; + if (type == RULE_REG_IMM_VAL) { + r->reg1_val = val; + } + } else if (dreg == NFT_REG_2) { + r->reg2_type = type; + if (type == RULE_REG_IMM_VAL) { + r->reg2_val = val; + } + } else if (dreg == NFT_REG_VERDICT) { + if (r->type == RULE_FILTER) { + r->filter_action = val; + } + } else { + syslog(LOG_ERR, "%s: unknown reg:%d", "set_reg", dreg); + } + return ; +} + +static inline void +parse_rule_immediate(struct nft_rule_expr *e, rule_t *r) +{ + uint32_t dreg, reg_val; + + dreg = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_DREG); + + if (dreg == NFT_REG_VERDICT) { + reg_val = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); + } else { + reg_val = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_DATA); + } + set_reg(r, dreg, RULE_REG_IMM_VAL, reg_val); + return; +} + +static inline void +parse_rule_counter(struct nft_rule_expr *e, rule_t *r) +{ + r->type = RULE_COUNTER; + r->bytes = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES); + r->packets = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS); + + return; +} + +static inline void +parse_rule_meta(struct nft_rule_expr *e, rule_t *r) +{ + uint32_t key = nft_rule_expr_get_u32(e, NFT_EXPR_META_KEY); + uint32_t dreg = nft_rule_expr_get_u32(e, NFT_EXPR_META_DREG); + enum rule_reg_type reg_type; + + switch (key) { + case NFT_META_IIF: + reg_type = RULE_REG_IIF; + set_reg(r, dreg, reg_type, 0); + return ; + } + syslog(LOG_DEBUG, "parse_rule_meta :Not support key %d\n", key); + + return; +} + +static inline void +parse_rule_nat(struct nft_rule_expr *e, rule_t *r) +{ + uint32_t addr_min_reg, addr_max_reg, proto_min_reg, proto_max_reg; + r->type = RULE_NAT; + + r->nat_type = nft_rule_expr_get_u32(e, NFT_EXPR_NAT_TYPE); + r->family = nft_rule_expr_get_u32(e, NFT_EXPR_NAT_FAMILY); + addr_min_reg = nft_rule_expr_get_u32(e, NFT_EXPR_NAT_REG_ADDR_MIN); + addr_max_reg = nft_rule_expr_get_u32(e, NFT_EXPR_NAT_REG_ADDR_MAX); + proto_min_reg = nft_rule_expr_get_u32(e, NFT_EXPR_NAT_REG_PROTO_MIN); + proto_max_reg = nft_rule_expr_get_u32(e, NFT_EXPR_NAT_REG_PROTO_MAX); + + if (addr_min_reg != addr_max_reg || + proto_min_reg != proto_max_reg) { + syslog(LOG_ERR, "Unsupport proto/addr range for NAT"); + } + + if (r->nat_type == NFT_NAT_DNAT) { + r->iaddr = (in_addr_t)*get_reg_val_ptr(r, addr_min_reg); + r->iport = ntohl(*get_reg_val_ptr(r, proto_min_reg)); + } else if (r->nat_type == NFT_NAT_SNAT) { + r->eaddr = (in_addr_t)*get_reg_val_ptr(r, addr_min_reg); + r->eport = ntohl(*get_reg_val_ptr(r, proto_min_reg)); + } + + set_reg(r, NFT_REG_1, RULE_REG_NONE, 0); + set_reg(r, NFT_REG_2, RULE_REG_NONE, 0); + return; +} + +static inline void +parse_rule_payload(struct nft_rule_expr *e, rule_t *r) +{ + uint32_t base, dreg, offset, len; + uint32_t *regptr; + + dreg = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_DREG); + base = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_BASE); + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + len = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_LEN); + regptr = get_reg_type_ptr(r, dreg); + + switch (base) { + case NFT_PAYLOAD_NETWORK_HEADER: + if (offset == offsetof(struct iphdr, daddr) && + len == sizeof(in_addr_t)) { + *regptr = RULE_REG_IP_DEST_ADDR; + return; + } else if (offset == offsetof(struct iphdr, saddr) && + len == sizeof(in_addr_t)) { + *regptr = RULE_REG_IP_SRC_ADDR; + return; + } else if (offset == offsetof(struct iphdr, saddr) && + len == sizeof(in_addr_t) * 2) { + *regptr = RULE_REG_IP_SD_ADDR; + return; + } else if (offset == offsetof(struct iphdr, protocol) && + len == sizeof(uint8_t)) { + *regptr = RULE_REG_IP_PROTO; + return; + } + case NFT_PAYLOAD_TRANSPORT_HEADER: + if (offset == offsetof(struct tcphdr, dest) && + len == sizeof(uint16_t)) { + *regptr = RULE_REG_TCP_DPORT; + return; + } else if (offset == offsetof(struct tcphdr, source) && + len == sizeof(uint16_t) * 2) { + *regptr = RULE_REG_TCP_SD_PORT; + return; + } + } + syslog(LOG_DEBUG, + "Unsupport payload: (dreg:%d, base:%d, offset:%d, len:%d)", + dreg, base, offset, len); + return; +} + +/* + * + * Note: Currently support only NFT_REG_1 + */ +static inline void +parse_rule_cmp(struct nft_rule_expr *e, rule_t *r) { + uint32_t data_len; + void *data_val; + uint32_t op, sreg; + uint16_t *ports; + in_addr_t *addrp; + + data_val = (void *)nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &data_len); + sreg = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_SREG); + op = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP); + + if (sreg != NFT_REG_1) { + syslog(LOG_ERR, "parse_rule_cmp: Unsupport reg:%d", sreg); + return; + } + + switch (r->reg1_type) { + case RULE_REG_IIF: + if (data_len == sizeof(uint32_t) && op == NFT_CMP_EQ) { + r->ifidx = *(uint32_t *)data_val; + r->reg1_type = RULE_REG_NONE; + return; + } + case RULE_REG_IP_SRC_ADDR: + if (data_len == sizeof(in_addr_t) && op == NFT_CMP_EQ) { + r->rhost = *(in_addr_t *)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) { + r->iaddr = *(in_addr_t *)data_val; + } else { + r->rhost = *(in_addr_t *)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; + r->iaddr = addrp[0]; + r->rhost = addrp[1]; + r->reg1_type = RULE_REG_NONE; + return; + } + case RULE_REG_IP_PROTO: + if (data_len == sizeof(uint8_t) && op == NFT_CMP_EQ) { + r->proto = *(uint8_t *)data_val; + r->reg1_type = RULE_REG_NONE; + return; + } + case RULE_REG_TCP_DPORT: + if (data_len == sizeof(uint16_t) && op == NFT_CMP_EQ) { + r->eport = ntohs(*(uint16_t *)data_val); + r->reg1_type = RULE_REG_NONE; + return; + } + 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->rport = ntohs(ports[1]); + r->reg1_type = RULE_REG_NONE; + return; + } + default: + break; + } + syslog(LOG_DEBUG, "Unknown cmp (r1type:%d, data_len:%d, op:%d)", + r->reg1_type, data_len, op); + return; +} + +static int +rule_expr_cb(struct nft_rule_expr *e, void *data) +{ + rule_t *r = data; + const char *attr_name = nft_rule_expr_get_str(e, + NFT_RULE_EXPR_ATTR_NAME); + + if (strncmp("cmp", attr_name, sizeof("cmp")) == 0) { + parse_rule_cmp(e, r); + } else if (strncmp("nat", attr_name, sizeof("nat")) == 0) { + parse_rule_nat(e, r); + } else if (strncmp("meta", attr_name, sizeof("meta")) == 0) { + parse_rule_meta(e, r); + } else if (strncmp("counter", attr_name, sizeof("counter")) == 0) { + parse_rule_counter(e, r); + } else if (strncmp("payload", attr_name, sizeof("payload")) == 0) { + parse_rule_payload(e, r); + } else if (strncmp("immediate", attr_name, sizeof("immediate")) == 0) { + parse_rule_immediate(e, r); + } else { + printf("unknown attr: %s\n", attr_name); + } + return MNL_CB_OK; +} + + +static int +table_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_rule *t; + uint32_t len; + struct nft_rule_expr *expr; + struct nft_rule_expr_iter *itr; + rule_t *r; + UNUSED(data); + + r = malloc(sizeof(rule_t)); + + memset(r, 0, sizeof(rule_t)); + t = nft_rule_alloc(); + if (t == NULL) { + perror("OOM"); + goto err; + } + + if (nft_rule_nlmsg_parse(nlh, t) < 0) { + perror("nft_rule_nlmsg_parse"); + goto err_free; + } + + r->table = strdup( + (char *)nft_rule_attr_get_data(t, NFT_RULE_ATTR_TABLE, &len)); + r->chain = strdup( + (char *)nft_rule_attr_get_data(t, NFT_RULE_ATTR_CHAIN, &len)); + r->family = *(uint32_t*)nft_rule_attr_get_data(t, NFT_RULE_ATTR_FAMILY, + &len); + r->desc = (char *)nft_rule_attr_get_data(t, NFT_RULE_ATTR_USERDATA, + &len); + r->handle = *(uint32_t*)nft_rule_attr_get_data(t, + NFT_RULE_ATTR_HANDLE, + &len); + if (strcmp(r->table, NFT_TABLE_NAT) == 0) { + r->type = RULE_NAT; + } 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 = nft_rule_expr_iter_create(t); + + while ((expr = nft_rule_expr_iter_next(itr)) != NULL) { + rule_expr_cb(expr, r); + } + LIST_INSERT_HEAD(&head, r, entry); + rule_list_length++; + +err_free: + nft_rule_free(t); +err: + return MNL_CB_OK; +} + +void +reflesh_nft_redirect_cache(void) +{ + rule_t *p; + int i; + uint32_t len; + + free(redirect_cache); + len = rule_list_length - rule_list_peer_length; + redirect_cache = (rule_t **)malloc(sizeof(rule_t *) * len); + bzero(redirect_cache, sizeof(rule_t *) * len); + + 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++; + } + } + + return; +} + +void +reflesh_nft_peer_cache(void) +{ + rule_t *p; + int i; + + free(peer_cache); + 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++; + } + } + + return; +} + +void +reflesh_nft_cache(uint32_t family) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, type = NFT_OUTPUT_DEFAULT; + struct nft_rule *t; + rule_t *p1, *p2; + int ret; + + if (rule_list_validate == RULE_CACHE_VALID) + return; + + t = NULL; + p1 = LIST_FIRST(&head); + if (p1 != NULL) { + while(p1 != NULL) { + p2 = (rule_t *)LIST_NEXT(p1, entry); + free(p1->desc); + free(p1->table); + free(p1->chain); + free(p1); + p1 = p2; + } + } + LIST_INIT(&head); + + t = nft_rule_alloc(); + if (t == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + seq = time(NULL); + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, + NLM_F_DUMP, seq); + nft_rule_nlmsg_build_payload(nlh, t); + nft_rule_free(t); + + if (nl == NULL) { + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + } + portid = mnl_socket_get_portid(nl); + + 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); + if (ret <= 0) + 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; +} + +static void +expr_add_payload(struct nft_rule *r, uint32_t base, uint32_t dreg, + uint32_t offset, uint32_t len) +{ + struct nft_rule_expr *e; + + e = nft_rule_expr_alloc("payload"); + if (e == NULL) { + perror("expr payload oom"); + exit(EXIT_FAILURE); + } + + nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_BASE, base); + nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_DREG, dreg); + nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_OFFSET, offset); + nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_LEN, len); + + nft_rule_add_expr(r, e); +} + +#if 0 +static void +expr_add_bitwise(struct nft_rule *r, uint32_t sreg, uint32_t dreg, + uint32_t len, uint32_t mask, uint32_t xor) +{ + struct nft_rule_expr *e; + + e = nft_rule_expr_alloc("bitwise"); + if (e == NULL) { + perror("expr cmp bitwise"); + exit(EXIT_FAILURE); + } + + nft_rule_expr_set_u32(e, NFT_EXPR_BITWISE_SREG, sreg); + nft_rule_expr_set_u32(e, NFT_EXPR_BITWISE_DREG, dreg); + nft_rule_expr_set_u32(e, NFT_EXPR_BITWISE_LEN, len); + nft_rule_expr_set(e, NFT_EXPR_BITWISE_MASK, &mask, sizeof(mask)); + nft_rule_expr_set(e, NFT_EXPR_BITWISE_XOR, &xor, sizeof(xor)); + + nft_rule_add_expr(r, e); +} +#endif + +static void +expr_add_cmp(struct nft_rule *r, uint32_t sreg, uint32_t op, + const void *data, uint32_t data_len) +{ + struct nft_rule_expr *e; + + e = nft_rule_expr_alloc("cmp"); + if (e == NULL) { + perror("expr cmp oom"); + exit(EXIT_FAILURE); + } + + nft_rule_expr_set_u32(e, NFT_EXPR_CMP_SREG, sreg); + nft_rule_expr_set_u32(e, NFT_EXPR_CMP_OP, op); + nft_rule_expr_set(e, NFT_EXPR_CMP_DATA, data, data_len); + + nft_rule_add_expr(r, e); +} + +static void +expr_add_meta(struct nft_rule *r, uint32_t meta_key, uint32_t dreg) +{ + struct nft_rule_expr *e; + + e = nft_rule_expr_alloc("meta"); + if (e == NULL) { + perror("expr meta oom"); + exit(EXIT_FAILURE); + } + + nft_rule_expr_set_u32(e, NFT_EXPR_META_KEY, meta_key); + nft_rule_expr_set_u32(e, NFT_EXPR_META_DREG, dreg); + + nft_rule_add_expr(r, e); +} + +static void +expr_set_reg_val(struct nft_rule *r, enum nft_registers dreg, uint32_t val) +{ + struct nft_rule_expr *e; + e = nft_rule_expr_alloc("immediate"); + if (e == NULL) { + perror("expr dreg oom"); + exit(EXIT_FAILURE); + } + nft_rule_expr_set_u32(e, NFT_EXPR_IMM_DREG, dreg); + nft_rule_expr_set_u32(e, NFT_EXPR_IMM_DATA, val); + nft_rule_add_expr(r, e); +} + +static void +expr_set_reg_verdict(struct nft_rule *r, uint32_t val) +{ + struct nft_rule_expr *e; + e = nft_rule_expr_alloc("immediate"); + if (e == NULL) { + perror("expr dreg oom"); + exit(EXIT_FAILURE); + } + nft_rule_expr_set_u32(e, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT); + nft_rule_expr_set_u32(e, NFT_EXPR_IMM_VERDICT, val); + nft_rule_add_expr(r, e); +} + +static void +expr_add_nat(struct nft_rule *r, uint32_t t, uint32_t family, + in_addr_t addr_min, uint32_t proto_min, uint32_t flags) +{ + struct nft_rule_expr *e; + UNUSED(flags); + + e = nft_rule_expr_alloc("nat"); + if (e == NULL) { + perror("expr nat oom"); + exit(EXIT_FAILURE); + } + + nft_rule_expr_set_u32(e, NFT_EXPR_NAT_TYPE, t); + nft_rule_expr_set_u32(e, NFT_EXPR_NAT_FAMILY, family); + + expr_set_reg_val(r, NFT_REG_1, (uint32_t)addr_min); + nft_rule_expr_set_u32(e, NFT_EXPR_NAT_REG_ADDR_MIN, NFT_REG_1); + nft_rule_expr_set_u32(e, NFT_EXPR_NAT_REG_ADDR_MAX, NFT_REG_1); + expr_set_reg_val(r, NFT_REG_2, proto_min); + nft_rule_expr_set_u32(e, NFT_EXPR_NAT_REG_PROTO_MIN, NFT_REG_2); + nft_rule_expr_set_u32(e, NFT_EXPR_NAT_REG_PROTO_MAX, NFT_REG_2); + + nft_rule_add_expr(r, e); +} + + +/* + * Todo: add expr for rhost + */ +struct nft_rule * +rule_set_snat(uint8_t family, uint8_t proto, + in_addr_t rhost, unsigned short rport, + in_addr_t ehost, unsigned short eport, + in_addr_t ihost, unsigned short iport, + const char *descr, + const char *handle) +{ + struct nft_rule *r = NULL; + uint32_t destport; + in_addr_t addr[2]; + uint16_t port[2]; + uint32_t descr_len; + UNUSED(handle); + + r = nft_rule_alloc(); + if (r == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, NFT_TABLE_NAT); + nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, miniupnpd_nft_peer_chain); + if (descr != NULL) { + descr_len = strlen(descr); + nft_rule_attr_set_data(r, NFT_RULE_ATTR_USERDATA, + descr, descr_len); + } + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, family); + + addr[0] = ihost; + addr[1] = rhost; + 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); + + 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); + if (proto == IPPROTO_TCP) { + 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)); + + destport = htonl(eport); + expr_add_nat(r, NFT_NAT_SNAT, AF_INET, ehost, destport, 0); + + return r; +} + +/* + * Todo: add expr for rhost + */ +struct nft_rule * +rule_set_dnat(uint8_t family, const char * ifname, uint8_t proto, + in_addr_t rhost, unsigned short eport, + in_addr_t ihost, uint32_t iport, + const char *descr, + const char *handle) +{ + struct nft_rule *r = NULL; + uint16_t dport; + uint64_t handle_num; + uint32_t if_idx; + uint32_t descr_len; + + UNUSED(handle); + UNUSED(rhost); + + r = nft_rule_alloc(); + if (r == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, NFT_TABLE_NAT); + nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, miniupnpd_nft_nat_chain); + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, family); + if (descr != NULL) { + descr_len = strlen(descr); + nft_rule_attr_set_data(r, NFT_RULE_ATTR_USERDATA, + descr, descr_len); + } + + if (handle != NULL) { + handle_num = atoll(handle); + nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle_num); + } + + if (ifname != NULL) { + if_idx = (uint32_t)if_nametoindex(ifname); + expr_add_meta(r, NFT_META_IIF, NFT_REG_1); + expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &if_idx, + 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)); + + 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)); + } 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)); + } + + expr_add_nat(r, NFT_NAT_DNAT, AF_INET, ihost, htonl(iport), 0); + + return r; +} + +struct nft_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) +{ + struct nft_rule *r = NULL; + uint16_t dport; + uint64_t handle_num; + uint32_t if_idx; + uint32_t descr_len; + UNUSED(eport); + + r = nft_rule_alloc(); + if (r == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, NFT_TABLE_FILTER); + nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, miniupnpd_nft_forward_chain); + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, family); + if (descr != NULL) { + descr_len = strlen(descr); + nft_rule_attr_set_data(r, NFT_RULE_ATTR_USERDATA, + descr, descr_len); + } + + if (handle != NULL) { + handle_num = atoll(handle); + nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle_num); + } + + if (ifname != NULL) { + if_idx = (uint32_t)if_nametoindex(ifname); + expr_add_meta(r, NFT_META_IIF, NFT_REG_1); + expr_add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &if_idx, + 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)); + + dport = htons(iport); + 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)); + + 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)); + } + + expr_set_reg_verdict(r, NF_ACCEPT); + + return r; +} + +struct nft_rule * +rule_del_handle(rule_t *rule) +{ + struct nft_rule *r = NULL; + + r = nft_rule_alloc(); + if (r == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, rule->table); + nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, rule->chain); + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, rule->family); + nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, rule->handle); + + return r; +} + +static void +nft_mnl_batch_put(char *buf, uint16_t type, uint32_t seq) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfg; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_seq = seq; + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = NFNL_SUBSYS_NFTABLES; +} + +int +nft_send_request(struct nft_rule * rule, uint16_t cmd) +{ + struct nlmsghdr *nlh; + struct mnl_nlmsg_batch *batch; + char buf[MNL_SOCKET_BUFFER_SIZE]; + uint32_t seq = time(NULL); + int ret; + + rule_list_validate = RULE_CACHE_INVALID; + if (nl == NULL) { + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + return -1; + } + } + + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + nft_mnl_batch_put(mnl_nlmsg_batch_current(batch), + NFNL_MSG_BATCH_BEGIN, seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nft_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + cmd, + nft_rule_attr_get_u32(rule, NFT_RULE_ATTR_FAMILY), + NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, + seq++); + + nft_rule_nlmsg_build_payload(nlh, rule); + nft_rule_free(rule); + mnl_nlmsg_batch_next(batch); + + nft_mnl_batch_put(mnl_nlmsg_batch_current(batch), NFNL_MSG_BATCH_END, + seq++); + mnl_nlmsg_batch_next(batch); + + ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch)); + if (ret == -1) { + perror("mnl_socket_sendto"); + return -1; + } + + mnl_nlmsg_batch_stop(batch); + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return -1; + } + + ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), NULL, NULL); + if (ret < 0) { + perror("mnl_cb_run"); + return -1; + } + + /* mnl_socket_close(nl); */ + return 0; +} diff --git a/miniupnpd/netfilter_nft/nftnlrdr_misc.h b/miniupnpd/netfilter_nft/nftnlrdr_misc.h new file mode 100644 index 0000000..383701e --- /dev/null +++ b/miniupnpd/netfilter_nft/nftnlrdr_misc.h @@ -0,0 +1,89 @@ +/* + * MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2015 Tomofumi Hayashi + * + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution. + */ +#include + +#define NFT_TABLE_NAT "nat" +#define NFT_TABLE_FILTER "filter" + +enum rule_reg_type { + RULE_REG_NONE, + RULE_REG_IIF, + RULE_REG_IP_SRC_ADDR, + RULE_REG_IP_DEST_ADDR, + RULE_REG_IP_SD_ADDR, /* source & dest */ + RULE_REG_IP_PROTO, + RULE_REG_TCP_DPORT, + RULE_REG_TCP_SD_PORT, /* source & dest */ + RULE_REG_IMM_VAL, + RULE_REG_MAX, +}; + +enum rule_type { + RULE_NONE, + RULE_NAT, + RULE_SNAT, + RULE_FILTER, + RULE_COUNTER, +}; + +typedef struct rule_ { + LIST_ENTRY(rule_t) entry; + char * table; + char * chain; + uint64_t handle; + enum rule_type type; + uint32_t nat_type; + uint32_t filter_action; + uint32_t family; + uint32_t ifidx; + in_addr_t eaddr; + in_addr_t iaddr; + in_addr_t rhost; + uint16_t eport; + uint16_t iport; + uint16_t rport; + uint8_t proto; + enum rule_reg_type reg1_type; + enum rule_reg_type reg2_type; + uint32_t reg1_val; + uint32_t reg2_val; + uint64_t packets; + uint64_t bytes; + char *desc; +} rule_t; + +LIST_HEAD(rule_list, rule_); +extern struct rule_list head; +extern rule_t **peer_cache; +extern rule_t **redirect_cache; + +int +nft_send_request(struct nft_rule * rule, uint16_t cmd); +struct nft_rule * +rule_set_dnat(uint8_t family, const char * ifname, uint8_t proto, + in_addr_t rhost, unsigned short eport, + in_addr_t ihost, uint32_t iport, + const char *descr, + const char *handle); +struct nft_rule * +rule_set_snat(uint8_t family, uint8_t proto, + in_addr_t rhost, unsigned short rport, + in_addr_t ehost, unsigned short eport, + in_addr_t ihost, unsigned short iport, + const char *descr, + const char *handle); +struct nft_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); +struct nft_rule * +rule_del_handle(rule_t *r); +void +reflesh_nft_cache(uint32_t family); +void print_rule(rule_t *r); diff --git a/miniupnpd/netfilter_nft/scripts/nft_delete_chain.sh b/miniupnpd/netfilter_nft/scripts/nft_delete_chain.sh new file mode 100755 index 0000000..97f9fbe --- /dev/null +++ b/miniupnpd/netfilter_nft/scripts/nft_delete_chain.sh @@ -0,0 +1,5 @@ +#! /sbin/nft -f + +delete chain nat miniupnpd +delete chain nat miniupnpd-pcp-peer +delete chain filter miniupnpd diff --git a/miniupnpd/netfilter_nft/scripts/nft_flush.sh b/miniupnpd/netfilter_nft/scripts/nft_flush.sh new file mode 100755 index 0000000..bd6c878 --- /dev/null +++ b/miniupnpd/netfilter_nft/scripts/nft_flush.sh @@ -0,0 +1,5 @@ +#! /sbin/nft -f + +flush chain ip nat miniupnpd +flush chain ip nat miniupnpd-pcp-peer +flush chain ip filter miniupnpd diff --git a/miniupnpd/netfilter_nft/scripts/nft_init.sh b/miniupnpd/netfilter_nft/scripts/nft_init.sh new file mode 100755 index 0000000..f4c87da --- /dev/null +++ b/miniupnpd/netfilter_nft/scripts/nft_init.sh @@ -0,0 +1,51 @@ +#! /bin/sh + +nft list table nat > /dev/null +nft_nat_exists=$? +nft list table filter > /dev/null +nft_filter_exists=$? +nft list table mangle > /dev/null +nft_mangle_exists=$? + +if [ $nft_nat_exists -eq "1" ]; then + echo "create nat" + nft "add table nat" +fi +if [ $nft_filter_exists -eq "1" ]; then + echo "create filter" + nft "add table 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_nat_miniupnpd_exists=$? +nft list chain nat miniupnpd-pcp-peer > /dev/null +nft_nat_miniupnpd_pcp_peer_exists=$? +nft list chain filter miniupnpd > /dev/null +nft_filter_miniupnpd_exists=$? +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 { \ + type nat hook prerouting priority -200 ; }" +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 { \ + type nat hook postrouting priority -300 ; }" +fi +if [ $nft_filter_miniupnpd_exists -eq "1" ]; then + echo "create chain in filter " + nft "add chain filter miniupnpd { \ + type nat hook forward priority -200 ; }" +fi +if [ $nft_mangle_miniupnpd_exists -eq "1" ]; then + echo "create chain in mangle" + nft "add chain mangle miniupnpd { \ + type nat hook prerouting priority -100 ; }" +fi diff --git a/miniupnpd/netfilter_nft/scripts/nft_removeall.sh b/miniupnpd/netfilter_nft/scripts/nft_removeall.sh new file mode 100755 index 0000000..11c38e7 --- /dev/null +++ b/miniupnpd/netfilter_nft/scripts/nft_removeall.sh @@ -0,0 +1,5 @@ +#! /sbin/nft -f + +delete rule nat miniupnpd +delete rule nat miniupnpd-pcp-peer +delete rule filter miniupnpd diff --git a/miniupnpd/netfilter_nft/test_nfct_get.c b/miniupnpd/netfilter_nft/test_nfct_get.c new file mode 100644 index 0000000..af8c07d --- /dev/null +++ b/miniupnpd/netfilter_nft/test_nfct_get.c @@ -0,0 +1,50 @@ +#include "nfct_get.c" + +int main(int argc, char *argv[]) +{ + struct sockaddr_storage src, dst, ext; + char buff[INET6_ADDRSTRLEN]; + + if (argc!=5) + return 0; + + if (1 != inet_pton(AF_INET, argv[1], + &((struct sockaddr_in*)&src)->sin_addr)) { + if (1 != inet_pton(AF_INET6, argv[1], + &((struct sockaddr_in6*) &src)->sin6_addr)) { + perror("bad input param"); + } else { + ((struct sockaddr_in6*)(&src))->sin6_port = htons(atoi(argv[2])); + src.ss_family = AF_INET6; + } + } else { + ((struct sockaddr_in*)(&src))->sin_port = htons(atoi(argv[2])); + src.ss_family = AF_INET; + } + + if (1 != inet_pton(AF_INET, argv[3], + &((struct sockaddr_in*)&dst)->sin_addr)) { + if (1 != inet_pton(AF_INET6, argv[3], + &((struct sockaddr_in6*) &dst)->sin6_addr)) { + perror("bad input param"); + } else { + ((struct sockaddr_in6*)(&dst))->sin6_port = htons(atoi(argv[4])); + dst.ss_family = AF_INET6; + } + } else { + ((struct sockaddr_in*)(&dst))->sin_port = htons(atoi(argv[4])); + dst.ss_family = AF_INET; + } + + if (get_nat_ext_addr((struct sockaddr*)&src, (struct sockaddr*)&dst, + IPPROTO_TCP, &ext)) { + printf("Ext address %s:%d\n", + inet_ntop(src.ss_family, + &((struct sockaddr_in*)&ext)->sin_addr, + buff, sizeof(buff)), + ntohs(((struct sockaddr_in*)(&ext))->sin_port)); + } else { + printf("no entry\n"); + } + return 0; +} diff --git a/miniupnpd/netfilter_nft/testnftnlrdr.c b/miniupnpd/netfilter_nft/testnftnlrdr.c new file mode 100644 index 0000000..efeccb3 --- /dev/null +++ b/miniupnpd/netfilter_nft/testnftnlrdr.c @@ -0,0 +1,91 @@ +/* $Id: testiptcrdr.c,v 1.18 2012/04/24 22:41:53 nanard Exp $ */ +/* MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2006-2012 Thomas Bernard + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution */ + +#include +#include +#include +#include + +#include "nftnlrdr.h" +#include "../commonrdr.h" + +#ifndef PRIu64 +#define PRIu64 "llu" +#endif + +static int +add_filter_rule(int proto, const char * rhost, + const char * iaddr, unsigned short iport) +{ + return add_filter_rule2(NULL, rhost, iaddr, 0, iport, proto, NULL); +} + +static int +addnatrule(int proto, unsigned short eport, + const char * iaddr, unsigned short iport, + const char * rhost) +{ + return add_redirect_rule2(NULL, rhost, eport, iaddr, iport, proto, NULL, 0); +} + +int +main(int argc, char ** argv) +{ + unsigned short eport, iport; + const char * iaddr; + + if(argc<4) { + printf("Usage %s \n", argv[0]); + return -1; + } + openlog("testnftnlrdr", LOG_PERROR|LOG_CONS, LOG_LOCAL0); + eport = (unsigned short)atoi(argv[1]); + iaddr = argv[2]; + iport = (unsigned short)atoi(argv[3]); + printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport); + if(addnatrule(IPPROTO_TCP, eport, iaddr, iport, NULL) < 0) { + printf("addnatrule() failed!\n"); + return -1; + } + if(add_filter_rule(IPPROTO_TCP, NULL, iaddr, iport) < 0) { + printf("add_filter_rule() failed!\n"); + return -1; + } + /* test */ + { + unsigned short p1, p2; + char addr[16]; + int proto2; + char desc[256]; + char rhost[256]; + unsigned int timestamp; + u_int64_t packets, bytes; + + desc[0] = '\0'; + printf("test0\n"); + if(get_redirect_rule_by_index(0, "", &p1, + addr, sizeof(addr), &p2, + &proto2, desc, sizeof(desc), + rhost, sizeof(rhost), + ×tamp, + &packets, &bytes) < 0) + { + printf("rule not found\n"); + } + else + { + printf("redirected port %hu to %s:%hu proto %d packets=%" PRIu64 " bytes=%" PRIu64 "\n", + p1, addr, p2, proto2, packets, bytes); + } + printf("test\n"); + } + printf("trying to list nat rules :\n"); + list_redirect_rule(argv[1]); + printf("deleting\n"); + delete_redirect_and_filter_rules(eport, IPPROTO_TCP); + return 0; +} diff --git a/miniupnpd/netfilter_nft/tiny_nf_nat.h b/miniupnpd/netfilter_nft/tiny_nf_nat.h new file mode 100644 index 0000000..f7f5b27 --- /dev/null +++ b/miniupnpd/netfilter_nft/tiny_nf_nat.h @@ -0,0 +1,37 @@ +/* $Id: tiny_nf_nat.h,v 1.1 2011/07/30 13:14:36 nanard Exp $ */ +/* Only what miniupnpd needs, until linux-libc-dev gains nf_nat.h */ + +#ifndef TINY_NF_NAT_H +#define TINY_NF_NAT_H + +#include + +#define IP_NAT_RANGE_MAP_IPS 1 +#define IP_NAT_RANGE_PROTO_SPECIFIED 2 +#define IP_NAT_RANGE_PROTO_RANDOM 4 +#define IP_NAT_RANGE_PERSISTENT 8 + +union nf_conntrack_man_proto { + __be16 all; + struct { __be16 port; } tcp; + struct { __be16 port; } udp; + struct { __be16 id; } icmp; + struct { __be16 port; } dccp; + struct { __be16 port; } sctp; + struct { __be16 key; } gre; +}; + +struct nf_nat_range { + unsigned int flags; + __be32 min_ip, max_ip; + union nf_conntrack_man_proto min, max; +}; + +struct nf_nat_multi_range_compat { + unsigned int rangesize; + struct nf_nat_range range[1]; +}; + +#define nf_nat_multi_range nf_nat_multi_range_compat + +#endif /*TINY_NF_NAT_H*/