From 507be27b937a415efca5da65cb17e09b4abf3fdf Mon Sep 17 00:00:00 2001 From: Shibby Date: Wed, 3 Jun 2015 15:22:17 +0200 Subject: [PATCH] Miniupnpd: update to 1.9 (20150430) --- release/src/router/miniupnpd/Changelog.txt | 23 +- release/src/router/miniupnpd/LICENSE | 2 +- release/src/router/miniupnpd/Makefile.linux | 5 +- .../{Makefile.linux => Makefile.linux_nft} | 94 +- release/src/router/miniupnpd/bsdqueue.h | 531 --------- release/src/router/miniupnpd/genconfig.sh | 8 +- release/src/router/miniupnpd/linux/getroute.c | 31 +- release/src/router/miniupnpd/linux/ifacewatcher.c | 10 +- release/src/router/miniupnpd/minissdp.c | 10 +- release/src/router/miniupnpd/miniupnpd.c | 43 +- release/src/router/miniupnpd/miniupnpd.conf | 89 +- release/src/router/miniupnpd/natpmp.c | 10 +- release/src/router/miniupnpd/netfilter/iptcrdr.c | 590 +++++++++- release/src/router/miniupnpd/netfilter/iptcrdr.h | 24 + .../src/router/miniupnpd/netfilter/iptpinhole.c | 25 +- .../src/router/miniupnpd/netfilter_nft/Makefile | 33 + .../src/router/miniupnpd/netfilter_nft/README.md | 21 + .../src/router/miniupnpd/netfilter_nft/nfct_get.c | 258 +++++ .../src/router/miniupnpd/netfilter_nft/nftnlrdr.c | 499 +++++++++ .../src/router/miniupnpd/netfilter_nft/nftnlrdr.h | 84 ++ .../router/miniupnpd/netfilter_nft/nftnlrdr_misc.c | 1145 ++++++++++++++++++++ .../router/miniupnpd/netfilter_nft/nftnlrdr_misc.h | 91 ++ .../netfilter_nft/scripts/nft_delete_chain.sh | 5 + .../miniupnpd/netfilter_nft/scripts/nft_flush.sh | 5 + .../miniupnpd/netfilter_nft/scripts/nft_init.sh | 47 + .../netfilter_nft/scripts/nft_removeall.sh | 5 + .../router/miniupnpd/netfilter_nft/test_nfct_get.c | 50 + .../router/miniupnpd/netfilter_nft/testnftnlrdr.c | 91 ++ .../router/miniupnpd/netfilter_nft/tiny_nf_nat.h | 37 + release/src/router/miniupnpd/pf/obsdrdr.c | 11 +- release/src/router/miniupnpd/upnpglobalvars.c | 28 +- release/src/router/miniupnpd/upnphttp.c | 69 +- release/src/router/miniupnpd/upnphttp.h | 4 +- release/src/router/miniupnpd/upnpsoap.c | 29 +- 34 files changed, 3271 insertions(+), 736 deletions(-) copy release/src/router/miniupnpd/{Makefile.linux => Makefile.linux_nft} (74%) delete mode 100644 release/src/router/miniupnpd/bsdqueue.h create mode 100644 release/src/router/miniupnpd/netfilter_nft/Makefile create mode 100644 release/src/router/miniupnpd/netfilter_nft/README.md create mode 100644 release/src/router/miniupnpd/netfilter_nft/nfct_get.c create mode 100644 release/src/router/miniupnpd/netfilter_nft/nftnlrdr.c create mode 100644 release/src/router/miniupnpd/netfilter_nft/nftnlrdr.h create mode 100644 release/src/router/miniupnpd/netfilter_nft/nftnlrdr_misc.c create mode 100644 release/src/router/miniupnpd/netfilter_nft/nftnlrdr_misc.h create mode 100644 release/src/router/miniupnpd/netfilter_nft/scripts/nft_delete_chain.sh create mode 100644 release/src/router/miniupnpd/netfilter_nft/scripts/nft_flush.sh create mode 100644 release/src/router/miniupnpd/netfilter_nft/scripts/nft_init.sh create mode 100644 release/src/router/miniupnpd/netfilter_nft/scripts/nft_removeall.sh create mode 100644 release/src/router/miniupnpd/netfilter_nft/test_nfct_get.c create mode 100644 release/src/router/miniupnpd/netfilter_nft/testnftnlrdr.c create mode 100644 release/src/router/miniupnpd/netfilter_nft/tiny_nf_nat.h diff --git a/release/src/router/miniupnpd/Changelog.txt b/release/src/router/miniupnpd/Changelog.txt index 8c142c0a87..c5340d0851 100644 --- a/release/src/router/miniupnpd/Changelog.txt +++ b/release/src/router/miniupnpd/Changelog.txt @@ -1,4 +1,25 @@ -$Id: Changelog.txt,v 1.391 2014/12/09 09:48:04 nanard Exp $ +$Id: Changelog.txt,v 1.399 2015/04/30 09:05:07 nanard Exp $ + +2015/04/30: + Adding linux/nftables support + +2015/04/26: + Remove dependency to libnfnetlink + fix typos in miniupnpd.conf + +2015/03/09: + fix get_portmappings_in_range() for linux/netfilter + +2015/03/07: + don't die when IPv6 is enabled and interface has no IPv4 address + +2015/02/10: + IP wildcard for AddPinhole() is empty string + +2014/12/10: + Checking Host: HTTP request header to prevent DNS rebinding attack + configurable BOOTID.UPNP.ORG SSDP header + use time for BOOTID.UPNP.ORG value 2014/12/09: fix upnp_add_inboundpinhole() : check inet_pton() return diff --git a/release/src/router/miniupnpd/LICENSE b/release/src/router/miniupnpd/LICENSE index efaa9c6dc6..2ae76011ac 100644 --- a/release/src/router/miniupnpd/LICENSE +++ b/release/src/router/miniupnpd/LICENSE @@ -1,5 +1,5 @@ MiniUPnPd -Copyright (c) 2006-2011, Thomas BERNARD +Copyright (c) 2006-2015, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/release/src/router/miniupnpd/Makefile.linux b/release/src/router/miniupnpd/Makefile.linux index d0013e5ab1..061c23e868 100644 --- a/release/src/router/miniupnpd/Makefile.linux +++ b/release/src/router/miniupnpd/Makefile.linux @@ -1,6 +1,6 @@ -# $Id: Makefile.linux,v 1.88 2014/10/21 14:04:15 nanard Exp $ +# $Id: Makefile.linux,v 1.90 2015/04/26 14:43:28 nanard Exp $ # MiniUPnP project -# (c) 2006-2014 Thomas Bernard +# (c) 2006-2015 Thomas Bernard # http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ # Author : Thomas Bernard # for use with GNU Make @@ -99,6 +99,7 @@ clean: $(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 \ diff --git a/release/src/router/miniupnpd/Makefile.linux b/release/src/router/miniupnpd/Makefile.linux_nft similarity index 74% copy from release/src/router/miniupnpd/Makefile.linux copy to release/src/router/miniupnpd/Makefile.linux_nft index d0013e5ab1..863c1be1a6 100644 --- a/release/src/router/miniupnpd/Makefile.linux +++ b/release/src/router/miniupnpd/Makefile.linux_nft @@ -1,36 +1,29 @@ -# $Id: Makefile.linux,v 1.88 2014/10/21 14:04:15 nanard Exp $ # MiniUPnP project -# (c) 2006-2014 Thomas Bernard +# (c) 2015 Tomofumi Hayashi # http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ -# Author : Thomas Bernard +# 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 install +# $ DESTDIR=/dummyinstalldir make -f Makefile.linux_nft install # or : -# $ INSTALLPREFIX=/usr/local make -f Makefile.linux install +# $ INSTALLPREFIX=/usr/local make -f Makefile.linux_nft install # or : # $ make -f Makefile.linux install # (default INSTALLPREFIX is /usr) # -# if your system hasn't iptables libiptc headers and binary correctly -# installed, you need to get iptables sources from http://netfilter.org/ -# ./configure them and build them then miniupnpd will build using : -# $ IPTABLESPATH=/path/to/iptables-1.4.1 make -f Makefile.linux # -#CFLAGS = -O -g -DDEBUG +CFLAGS = -O -g #-DDEBUG CFLAGS ?= -Os CFLAGS += -fno-strict-aliasing CFLAGS += -fno-common -CPPFLAGS += -D_GNU_SOURCE +CPPFLAGS += -D_GNU_SOURCE CFLAGS += -Wall CFLAGS += -Wextra -Wstrict-prototypes -Wdeclaration-after-statement -CFLAGS += $(EXTRACFLAGS) #CFLAGS += -Wno-missing-field-initializers -#CFLAGS += -ansi # iptables headers does use typeof which is a gcc extension CC ?= gcc RM = rm -f INSTALL = install @@ -52,42 +45,40 @@ 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/iptcrdr.o netfilter/iptpinhole.o netfilter/nfct_get.o +NETFILTEROBJS = netfilter_nft/nftnlrdr.o netfilter_nft/nfct_get.o netfilter_nft/nftnlrdr_misc.o ALLOBJS = $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS) -ifdef IPTABLESPATH -CFLAGS := $(CFLAGS) -I$(IPTABLESPATH)/include/ -LDFLAGS := $(LDFLAFGS) -L$(IPTABLESPATH)/libiptc/ +PCFILE_FOUND := $(shell $(PKG_CONFIG) --exists libnftnl; echo $$?) -# get iptables version and set IPTABLES_143 macro if needed -IPTABLESVERSION := $(shell grep "\#define VERSION" $(IPTABLESPATH)/config.h | tr -d \" |cut -d" " -f3 ) -IPTABLESVERSION1 := $(shell echo $(IPTABLESVERSION) | cut -d. -f1 ) -IPTABLESVERSION2 := $(shell echo $(IPTABLESVERSION) | cut -d. -f2 ) -IPTABLESVERSION3 := $(shell echo $(IPTABLESVERSION) | cut -d. -f3 ) +ifeq (${PCFILE_FOUND},0) -# test if iptables version >= 1.4.3 -TEST := $(shell [ \( \( $(IPTABLESVERSION1) -ge 1 \) -a \( $(IPTABLESVERSION2) -ge 4 \) \) -a \( $(IPTABLESVERSION3) -ge 3 \) ] && echo 1 ) -ifeq ($(TEST), 1) -CPPFLAGS := $(CFLAGS) -DIPTABLES_143 -# the following sucks, but works -LDLIBS = $(IPTABLESPATH)/libiptc/.libs/libip4tc.o -else # ifeq ($(TEST), 1) -LDLIBS = $(IPTABLESPATH)/libiptc/libiptc.a -endif # ifeq ($(TEST), 1) -else #ifdef IPTABLESPATH +PKG_CONFIG_LIBS = libnftnl libmnl +CFLAGS += $(shell $(PKG_CONFIG) --cflags $(PKG_CONFIG_LIBS)) +LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l $(PKG_CONFIG_LIBS)) +LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L $(PKG_CONFIG_LIBS)) +LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-other $(PKG_CONFIG_LIBS)) +else + +ARCH ?= $(shell uname -m | grep -q "x86_64" && echo 64) +endif # ifdef PCFILE_FOUND + +#LDLIBS += -lnfnetlink -CPPFLAGS += -I../iptables/include -LDFLAGS = -L../iptables -liptc +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) -endif #ifdef IPTABLESPATH +LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libssl) TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o -EXECUTABLES = miniupnpd -#EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ -# testupnppermissions miniupnpdctl testgetifaddr \ -# testgetroute testasyncsendto testportinuse +EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ + testupnppermissions miniupnpdctl testgetifaddr \ + testgetroute testasyncsendto testportinuse .PHONY: all clean install depend genuuid @@ -99,6 +90,7 @@ clean: $(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 \ @@ -146,7 +138,7 @@ testasyncsendto: testasyncsendto.o asyncsendto.o upnputils.o \ linux/getroute.o testportinuse: testportinuse.o portinuse.o getifaddr.o \ - netfilter/iptcrdr.o + netfilter_nft/nftnlrdr.o netfilter_nft/nftnlrdr_misc.o miniupnpdctl: miniupnpdctl.o @@ -179,11 +171,11 @@ 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/iptcrdr.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/iptcrdr.h commonrdr.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 @@ -199,7 +191,7 @@ 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/iptcrdr.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 @@ -208,7 +200,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/iptpinhole.h pcplearndscp.o: config.h upnpglobalvars.h upnppermissions.h miniupnpdtypes.h pcplearndscp.o: pcplearndscp.h asyncsendto.o: asyncsendto.h @@ -217,19 +209,19 @@ 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/iptcrdr.o: macros.h config.h netfilter/iptcrdr.h commonrdr.h -netfilter/iptcrdr.o: config.h upnpglobalvars.h upnppermissions.h -netfilter/iptcrdr.o: miniupnpdtypes.h -netfilter/iptpinhole.o: config.h netfilter/iptpinhole.h upnpglobalvars.h -netfilter/iptpinhole.o: upnppermissions.h config.h miniupnpdtypes.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: config.h getifstats.h +testgetifstats.o: getifstats.h testupnppermissions.o: upnppermissions.h config.h -testgetifaddr.o: getifaddr.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 diff --git a/release/src/router/miniupnpd/bsdqueue.h b/release/src/router/miniupnpd/bsdqueue.h deleted file mode 100644 index c6afe1f7c4..0000000000 --- a/release/src/router/miniupnpd/bsdqueue.h +++ /dev/null @@ -1,531 +0,0 @@ -/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */ -/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines five types of data structures: singly-linked lists, - * lists, simple queues, tail queues, and circular queues. - * - * - * A singly-linked list is headed by a single forward pointer. The elements - * are singly linked for minimum space and pointer manipulation overhead at - * the expense of O(n) removal for arbitrary elements. New elements can be - * added to the list after an existing element or at the head of the list. - * Elements being removed from the head of the list should use the explicit - * macro for this purpose for optimum efficiency. A singly-linked list may - * only be traversed in the forward direction. Singly-linked lists are ideal - * for applications with large datasets and few or no removals or for - * implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * A circle queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the list. - * A circle queue may be traversed in either direction, but has a more - * complex end of list detection. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -#ifdef QUEUE_MACRO_DEBUG -#define _Q_INVALIDATE(a) (a) = ((void *)-1) -#else -#define _Q_INVALIDATE(a) -#endif - -/* - * Singly-linked List definitions. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#ifdef SLIST_ENTRY -#undef SLIST_ENTRY -#endif - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List access methods. - */ -#define SLIST_FIRST(head) ((head)->slh_first) -#define SLIST_END(head) NULL -#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_FOREACH(var, head, field) \ - for((var) = SLIST_FIRST(head); \ - (var) != SLIST_END(head); \ - (var) = SLIST_NEXT(var, field)) - -#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ - for ((varp) = &SLIST_FIRST((head)); \ - ((var) = *(varp)) != SLIST_END(head); \ - (varp) = &SLIST_NEXT((var), field)) - -/* - * Singly-linked List functions. - */ -#define SLIST_INIT(head) { \ - SLIST_FIRST(head) = SLIST_END(head); \ -} - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (0) - -#define SLIST_REMOVE_NEXT(head, elm, field) do { \ - (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if ((head)->slh_first == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->slh_first; \ - \ - while (curelm->field.sle_next != (elm)) \ - curelm = curelm->field.sle_next; \ - curelm->field.sle_next = \ - curelm->field.sle_next->field.sle_next; \ - _Q_INVALIDATE((elm)->field.sle_next); \ - } \ -} while (0) - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List access methods - */ -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_END(head) NULL -#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_FOREACH(var, head, field) \ - for((var) = LIST_FIRST(head); \ - (var)!= LIST_END(head); \ - (var) = LIST_NEXT(var, field)) - -/* - * List functions. - */ -#define LIST_INIT(head) do { \ - LIST_FIRST(head) = LIST_END(head); \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (0) - -#define LIST_REMOVE(elm, field) do { \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -#define LIST_REPLACE(elm, elm2, field) do { \ - if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ - (elm2)->field.le_next->field.le_prev = \ - &(elm2)->field.le_next; \ - (elm2)->field.le_prev = (elm)->field.le_prev; \ - *(elm2)->field.le_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -/* - * Simple queue definitions. - */ -#define SIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define SIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue access methods. - */ -#define SIMPLEQ_FIRST(head) ((head)->sqh_first) -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) -#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - -#define SIMPLEQ_FOREACH(var, head, field) \ - for((var) = SIMPLEQ_FIRST(head); \ - (var) != SIMPLEQ_END(head); \ - (var) = SIMPLEQ_NEXT(var, field)) - -/* - * Simple queue functions. - */ -#define SIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (0) - -#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (0) - -#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (0) - -#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -/* - * Tail queue definitions. - */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} - -/* - * tail queue access methods - */ -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_END(head) NULL -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -/* XXX */ -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) -#define TAILQ_EMPTY(head) \ - (TAILQ_FIRST(head) == TAILQ_END(head)) - -#define TAILQ_FOREACH(var, head, field) \ - for((var) = TAILQ_FIRST(head); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_NEXT(var, field)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for((var) = TAILQ_LAST(head, headname); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_PREV(var, headname, field)) - -/* - * Tail queue functions. - */ -#define TAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_REMOVE(head, elm, field) do { \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -#define TAILQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ - (elm2)->field.tqe_next->field.tqe_prev = \ - &(elm2)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm2)->field.tqe_next; \ - (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ - *(elm2)->field.tqe_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -/* - * Circular queue definitions. - */ -#define CIRCLEQ_HEAD(name, type) \ -struct name { \ - struct type *cqh_first; /* first element */ \ - struct type *cqh_last; /* last element */ \ -} - -#define CIRCLEQ_HEAD_INITIALIZER(head) \ - { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } - -#define CIRCLEQ_ENTRY(type) \ -struct { \ - struct type *cqe_next; /* next element */ \ - struct type *cqe_prev; /* previous element */ \ -} - -/* - * Circular queue access methods - */ -#define CIRCLEQ_FIRST(head) ((head)->cqh_first) -#define CIRCLEQ_LAST(head) ((head)->cqh_last) -#define CIRCLEQ_END(head) ((void *)(head)) -#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) -#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) -#define CIRCLEQ_EMPTY(head) \ - (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) - -#define CIRCLEQ_FOREACH(var, head, field) \ - for((var) = CIRCLEQ_FIRST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_NEXT(var, field)) - -#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ - for((var) = CIRCLEQ_LAST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_PREV(var, field)) - -/* - * Circular queue functions. - */ -#define CIRCLEQ_INIT(head) do { \ - (head)->cqh_first = CIRCLEQ_END(head); \ - (head)->cqh_last = CIRCLEQ_END(head); \ -} while (0) - -#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm)->field.cqe_next; \ - (elm)->field.cqe_prev = (listelm); \ - if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (listelm)->field.cqe_next->field.cqe_prev = (elm); \ - (listelm)->field.cqe_next = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm); \ - (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ - if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (listelm)->field.cqe_prev->field.cqe_next = (elm); \ - (listelm)->field.cqe_prev = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.cqe_next = (head)->cqh_first; \ - (elm)->field.cqe_prev = CIRCLEQ_END(head); \ - if ((head)->cqh_last == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (head)->cqh_first->field.cqe_prev = (elm); \ - (head)->cqh_first = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.cqe_next = CIRCLEQ_END(head); \ - (elm)->field.cqe_prev = (head)->cqh_last; \ - if ((head)->cqh_first == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (head)->cqh_last->field.cqe_next = (elm); \ - (head)->cqh_last = (elm); \ -} while (0) - -#define CIRCLEQ_REMOVE(head, elm, field) do { \ - if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm)->field.cqe_prev; \ - else \ - (elm)->field.cqe_next->field.cqe_prev = \ - (elm)->field.cqe_prev; \ - if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm)->field.cqe_next; \ - else \ - (elm)->field.cqe_prev->field.cqe_next = \ - (elm)->field.cqe_next; \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ - CIRCLEQ_END(head)) \ - (head).cqh_last = (elm2); \ - else \ - (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ - if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ - CIRCLEQ_END(head)) \ - (head).cqh_first = (elm2); \ - else \ - (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#endif /* !_SYS_QUEUE_H_ */ diff --git a/release/src/router/miniupnpd/genconfig.sh b/release/src/router/miniupnpd/genconfig.sh index 72be1393ef..f3d0ed2380 100755 --- a/release/src/router/miniupnpd/genconfig.sh +++ b/release/src/router/miniupnpd/genconfig.sh @@ -1,5 +1,5 @@ #! /bin/sh -# $Id: genconfig.sh,v 1.77 2014/12/04 10:21:33 nanard Exp $ +# $Id: genconfig.sh,v 1.78 2014/12/10 09:34:42 nanard Exp $ # miniupnp daemon # http://miniupnp.free.fr or http://miniupnp.tuxfamily.org/ # (c) 2006-2014 Thomas Bernard @@ -517,6 +517,12 @@ cat >> ${CONFIGFILE} <> ${CONFIGFILE} <> ${CONFIGFILE} ${MV} ${CONFIGFILE} ${CONFIGFILE_FINAL} diff --git a/release/src/router/miniupnpd/linux/getroute.c b/release/src/router/miniupnpd/linux/getroute.c index 56acf82613..4daddf57e7 100644 --- a/release/src/router/miniupnpd/linux/getroute.c +++ b/release/src/router/miniupnpd/linux/getroute.c @@ -1,7 +1,7 @@ -/* $Id: getroute.c,v 1.4 2013/02/06 10:50:04 nanard Exp $ */ +/* $Id: getroute.c,v 1.6 2015/04/26 14:43:28 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2013 Thomas Bernard + * (c) 2006-2015 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -18,7 +18,12 @@ /*#include */ #include #include +#ifdef USE_LIBNFNETLINK +/* define USE_LIBNFNETLINK in order to use libnfnetlink + * instead of custom code + * see https://github.com/miniupnp/miniupnp/issues/110 */ #include +#endif /* USE_LIBNFNETLINK */ #include "../getroute.h" #include "../upnputils.h" @@ -48,6 +53,9 @@ get_src_for_route_to(const struct sockaddr * dst, }; const struct sockaddr_in * dst4; const struct sockaddr_in6 * dst6; +#ifndef USE_LIBNFNETLINK + struct rtattr * rta; +#endif /* USE_LIBNFNETLINK */ memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); @@ -68,15 +76,32 @@ get_src_for_route_to(const struct sockaddr * dst, syslog(LOG_DEBUG, "get_src_for_route_to (%s)", dst_str); } /* add address */ +#ifndef USE_LIBNFNETLINK + rta = (struct rtattr *)(((char*)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); + rta->rta_type = RTA_DST; +#endif /* USE_LIBNFNETLINK */ if(dst->sa_family == AF_INET) { dst4 = (const struct sockaddr_in *)dst; +#ifdef USE_LIBNFNETLINK nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst4->sin_addr, 4); +#else + rta->rta_len = RTA_SPACE(sizeof(dst4->sin_addr)); + memcpy(RTA_DATA(rta), &dst4->sin_addr, sizeof(dst4->sin_addr)); +#endif /* USE_LIBNFNETLINK */ req.r.rtm_dst_len = 32; } else { dst6 = (const struct sockaddr_in6 *)dst; +#ifdef USE_LIBNFNETLINK nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst6->sin6_addr, 16); +#else + rta->rta_len = RTA_SPACE(sizeof(dst6->sin6_addr)); + memcpy(RTA_DATA(rta), &dst6->sin6_addr, sizeof(dst6->sin6_addr)); +#endif /* USE_LIBNFNETLINK */ req.r.rtm_dst_len = 128; } +#ifndef USE_LIBNFNETLINK + req.n.nlmsg_len += rta->rta_len; +#endif /* USE_LIBNFNETLINK */ fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { @@ -147,7 +172,7 @@ get_src_for_route_to(const struct sockaddr * dst, if(src_len && src) { if(*src_len < RTA_PAYLOAD(rta)) { syslog(LOG_WARNING, "cannot copy src: %u<%lu", - (unsigned)*src_len, RTA_PAYLOAD(rta)); + (unsigned)*src_len, (unsigned long)RTA_PAYLOAD(rta)); goto error; } *src_len = RTA_PAYLOAD(rta); diff --git a/release/src/router/miniupnpd/linux/ifacewatcher.c b/release/src/router/miniupnpd/linux/ifacewatcher.c index bb4f9106dc..62527b8a10 100644 --- a/release/src/router/miniupnpd/linux/ifacewatcher.c +++ b/release/src/router/miniupnpd/linux/ifacewatcher.c @@ -1,7 +1,7 @@ -/* $Id: ifacewatcher.c,v 1.7 2012/05/27 22:16:10 nanard Exp $ */ +/* $Id: ifacewatcher.c,v 1.8 2015/03/07 15:52:33 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2012 Thomas Bernard + * (c) 2006-2015 Thomas Bernard * * ifacewatcher.c * @@ -237,7 +237,10 @@ ProcessInterfaceWatchNotify(int s) struct iovec iov; struct msghdr hdr; struct nlmsghdr *nlhdr; +#if 0 +/* disabled at the moment */ struct ifinfomsg *ifi; +#endif struct ifaddrmsg *ifa; int len; @@ -279,8 +282,9 @@ ProcessInterfaceWatchNotify(int s) case RTM_DELLINK: is_del = 1; case RTM_NEWLINK: - ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr); #if 0 +/* disabled at the moment */ + ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr); if(is_del) { if(ProcessInterfaceDown(ifi) < 0) syslog(LOG_ERR, "ProcessInterfaceDown(ifi) failed"); diff --git a/release/src/router/miniupnpd/minissdp.c b/release/src/router/miniupnpd/minissdp.c index deab6cf468..92f7b75270 100644 --- a/release/src/router/miniupnpd/minissdp.c +++ b/release/src/router/miniupnpd/minissdp.c @@ -1,7 +1,7 @@ -/* $Id: minissdp.c,v 1.72 2014/10/22 11:54:45 nanard Exp $ */ +/* $Id: minissdp.c,v 1.74 2015/04/30 08:59:51 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2014 Thomas Bernard + * (c) 2006-2015 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -883,7 +883,9 @@ ProcessSSDPData(int s, const char *bufr, int n, { if (lan_addr == NULL) { - syslog(LOG_ERR, "Can't find in which sub network the client is"); + syslog(LOG_ERR, + "Can't find in which sub network the client %s is", + sender_str); return; } announced_host = lan_addr->str; @@ -1167,6 +1169,8 @@ SendSSDPGoodbye(int * sockets, int n_sockets) for(j=0; j 60*60*24 && upnp_bootid == 1) { + /* We know we are not January the 1st 1970 */ + upnp_bootid = (unsigned int)startup_time; + /* from UDA v1.1 : + * A convenient mechanism is to set this field value to the time + * that the device sends its initial announcement, expressed as + * seconds elapsed since midnight January 1, 1970; */ + } +#endif /* USE_TIME_AS_BOOTID */ if(sysuptime) { /* use system uptime instead of daemon uptime */ @@ -854,8 +864,16 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str) memcpy(lan_addr->ifname, str, n); lan_addr->ifname[n] = '\0'; if(getifaddr(lan_addr->ifname, lan_addr->str, sizeof(lan_addr->str), - &lan_addr->addr, &lan_addr->mask) < 0) + &lan_addr->addr, &lan_addr->mask) < 0) { +#ifdef ENABLE_IPV6 + fprintf(stderr, "interface \"%s\" has no IPv4 address\n", str); + lan_addr->str[0] = '\0'; + lan_addr->addr.s_addr = htonl(0x00000000u); + lan_addr->mask.s_addr = htonl(0xffffffffu); +#else /* ENABLE_IPV6 */ goto parselan_error; +#endif /* ENABLE_IPV6 */ + } /*printf("%s => %s\n", lan_addr->ifname, lan_addr->str);*/ } else @@ -1249,6 +1267,12 @@ init(int argc, char * * argv, struct runtime_vars * v) } else switch(argv[i][1]) { + case 'b': + if(i+1 < argc) { + upnp_bootid = (unsigned int)strtoul(argv[++i], NULL, 10); + } else + fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); + break; case 'o': if(i+1 < argc) use_ext_ip_addr = argv[++i]; @@ -1639,16 +1663,16 @@ print_usage: "\t\t[-u uuid] [-s serial] [-m model_number] \n" "\t\t[-t notify_interval] [-P pid_filename] " #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION - "[-z fiendly_name]\n" + "[-z fiendly_name]" #endif - "\t\t[-B down up] [-w url] [-r clean_ruleset_interval]\n" + "\n\t\t[-B down up] [-w url] [-r clean_ruleset_interval]\n" #ifdef USE_PF "\t\t[-q queue] [-T tag]\n" #endif #ifdef ENABLE_NFQUEUE "\t\t[-Q queue] [-n name]\n" #endif - "\t\t[-A \"permission rule\"]\n" + "\t\t[-A \"permission rule\"] [-b BOOTID]\n" "\nNotes:\n\tThere can be one or several listening_ips.\n" "\tNotify interval is in seconds. Default is 30 seconds.\n" "\tDefault pid file is '%s'.\n" @@ -1661,7 +1685,7 @@ print_usage: "\t-U causes miniupnpd to report system uptime instead " "of daemon uptime.\n" #ifdef ENABLE_NATPMP - "\t-N enable NAT-PMP functionality.\n" + "\t-N enables NAT-PMP functionality.\n" #endif "\t-B sets bitrates reported by daemon in bits per second.\n" "\t-w sets the presentation url. Default is http address on port 80\n" @@ -1678,6 +1702,7 @@ print_usage: "\texamples :\n" "\t \"allow 1024-65535 192.168.1.0/24 1024-65535\"\n" "\t \"deny 0-65535 0.0.0.0/0 0-65535\"\n" + "\t-b sets the value of BOOTID.UPNP.ORG SSDP header\n" "\t-h prints this help and quits.\n" "", argv[0], pidfilename, DEFAULT_CONFIG); return 1; @@ -1780,7 +1805,7 @@ main(int argc, char * * argv) return 0; } - syslog(LOG_INFO, "Starting%s%swith external interface %s", + syslog(LOG_INFO, "version " MINIUPNPD_VERSION " starting%s%sext if %s BOOTID=%u", #ifdef ENABLE_NATPMP #ifdef ENABLE_PCP GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP/PCP " : " ", @@ -1791,7 +1816,7 @@ main(int argc, char * * argv) " ", #endif GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "", - ext_if_name); + ext_if_name, upnp_bootid); if(GETFLAG(ENABLEUPNPMASK)) { diff --git a/release/src/router/miniupnpd/miniupnpd.conf b/release/src/router/miniupnpd/miniupnpd.conf index 90bd5ade83..36040f4127 100644 --- a/release/src/router/miniupnpd/miniupnpd.conf +++ b/release/src/router/miniupnpd/miniupnpd.conf @@ -1,143 +1,141 @@ # WAN network interface #ext_ifname=eth1 #ext_ifname=xl1 -# if the WAN interface has several IP addresses, you +# If the WAN interface has several IP addresses, you # can specify the one to use below #ext_ip= # LAN network interfaces IPs / networks -# there can be multiple listening ips for SSDP traffic. -# should be under the form nnn.nnn.nnn.nnn/nn -# It can also be the network interface name (ie "eth0") -# It if mandatory to use the network interface name to enable IPv6 +# There can be multiple listening IPs for SSDP traffic +# It can be IP address or network interface name (ie. "eth0") +# It is mandatory to use the network interface name in order to enable IPv6 # HTTP is available on all interfaces. -# When MULTIPLE_EXTERNAL_IP is enabled, the external ip -# address associated with the subnet follows. for example : +# When MULTIPLE_EXTERNAL_IP is enabled, the external IP +# address associated with the subnet follows. For example: # listening_ip=192.168.0.1/24 88.22.44.13 #listening_ip=192.168.0.1/24 #listening_ip=10.5.0.0/16 #listening_ip=eth0 # CAUTION: mixing up WAN and LAN interfaces may introduce security risks! -# be sure to assign the correct interfaces to LAN and WAN and consider +# Be sure to assign the correct interfaces to LAN and WAN and consider # implementing UPnP permission rules at the bottom of this configuration file -# port for HTTP (descriptions and SOAP) traffic. set 0 for autoselect. +# Port for HTTP (descriptions and SOAP) traffic. Set to 0 for autoselect. #http_port=0 -# port for HTTPS. set to 0 for autoselect (default) +# Port for HTTPS. Set to 0 for autoselect (default) #https_port=0 -# path to the unix socket used to communicate with MiniSSDPd +# Path to the UNIX socket used to communicate with MiniSSDPd # If running, MiniSSDPd will manage M-SEARCH answering. # default is /var/run/minissdpd.sock #minissdpdsocket=/var/run/minissdpd.sock -# enable NAT-PMP support (default is no) +# Enable NAT-PMP support (default is no) #enable_natpmp=yes -# enable UPNP support (default is yes) +# Enable UPNP support (default is yes) #enable_upnp=no -# PCP : -# configure minimal and maximal lifetime of the port mapping in seconds +# PCP +# Configure the minimum and maximum lifetime of a port mapping in seconds # 120s and 86400s (24h) are suggested values from PCP-base #min_lifetime=120 #max_lifetime=86400 -# chain names for netfilter (not used for pf or ipf). +# Chain names for netfilter (not used for pf or ipf). # default is MINIUPNPD for both #upnp_forward_chain=forwardUPnP #upnp_nat_chain=UPnP -# lease file location +# Lease file location #lease_file=/var/log/upnp.leases -# to enable the next few runtime options, see compile time +# To enable the next few runtime options, see compile time # ENABLE_MANUFACTURER_INFO_CONFIGURATION (config.h) -# name of this service, default is "`uname -s` router" +# Name of this service, default is "`uname -s` router" #friendly_name=MiniUPnPd router -# manufacturer name, default is "`uname -s`" +# Manufacturer name, default is "`uname -s`" #manufacturer_name=Manufacturer corp -# manufacturer url, default is URL of OS verndor +# Manufacturer URL, default is URL of OS vendor #manufacturer_url=http://miniupnp.free.fr/ -# model name, default is "`uname -s` router" +# Model name, default is "`uname -s` router" #model_name=Router Model -# model description, default is "`uname -s` router" +# Model description, default is "`uname -s` router" #model_description=Very Secure Router - Model -# model url, default is URL of OS vendor +# Model URL, default is URL of OS vendor #model_url=http://miniupnp.free.fr/ -# bitrates reported by daemon in bits per second +# Bitrates reported by daemon in bits per second # by default miniupnpd tries to get WAN interface speed #bitrate_up=1000000 #bitrate_down=10000000 -# "secure" mode : when enabled, UPnP client are allowed to add mappings only -# to their IP. +# Secure Mode, UPnP clients can only add mappings to their own IP #secure_mode=yes secure_mode=no -# default presentation url is http address on port 80 +# Default presentation URL is HTTP address on port 80 # If set to an empty string, no presentationURL element will appear # in the XML description of the device, which prevents MS Windows # from displaying an icon in the "Network Connections" panel. #presentation_url=http://www.mylan/index.php -# report system uptime instead of daemon uptime +# Report system uptime instead of daemon uptime system_uptime=yes -# notify interval in seconds. default is 30 seconds. +# Notify interval in seconds. default is 30 seconds. #notify_interval=240 notify_interval=60 -# unused rules cleaning. +# Unused rules cleaning. # never remove any rule before this threshold for the number # of redirections is exceeded. default to 20 #clean_ruleset_threshold=10 -# clean process work interval in seconds. default to 0 (disabled). +# Clean process work interval in seconds. default to 0 (disabled). # a 600 seconds (10 minutes) interval makes sense clean_ruleset_interval=600 -# log packets in pf (default is no) +# Log packets in pf (default is no) #packet_log=no -# anchor name in pf (default is miniupnpd) +# Anchor name in pf (default is miniupnpd) #anchor=miniupnpd # ALTQ queue in pf -# filter rules must be used for this to be used. +# Filter rules must be used for this to be used. # compile with PF_ENABLE_FILTER_RULES (see config.h file) #queue=queue_name1 -# tag name in pf +# Tag name in pf #tag=tag_name1 -# make filter rules in pf quick or not. default is yes +# Make filter rules in pf quick or not. default is yes # active when compiled with PF_ENABLE_FILTER_RULES (see config.h file) #quickrules=no -# uuid : generate your own with "make genuuid" +# UUID, generate your own UUID with "make genuuid" uuid=00000000-0000-0000-0000-000000000000 -# serial and model number the daemon will report to clients -# in its XML description +# Daemon's serial and model number when reporting to clients +# (in XML description) #serial=12345678 #model_number=1 # UPnP permission rules -# (allow|deny) (external port range) ip/mask (internal port range) +# (allow|deny) (external port range) IP/mask (internal port range) # A port range is - or if there is only # one port in the range. -# ip/mask format must be nn.nn.nn.nn/nn -# it is advised to only allow redirection of port above 1024 -# and to finish the rule set with "deny 0-65535 0.0.0.0/0 0-65535" +# IP/mask format must be nnn.nnn.nnn.nnn/nn +# It is advised to only allow redirection of port >= 1024 +# and end the rule set with "deny 0-65535 0.0.0.0/0 0-65535" # The following default ruleset allows specific LAN side IP addresses -# to request only ephemeral ports. it is recommended that users +# to request only ephemeral ports. It is recommended that users # modify the IP ranges to match their own internal networks, and # also consider implementing network-specific restrictions # CAUTION: failure to enforce any rules may permit insecure requests to be made! @@ -146,4 +144,3 @@ allow 1024-65535 192.168.1.0/24 1024-65535 allow 1024-65535 192.168.0.0/23 22 allow 12345 192.168.7.113/32 54321 deny 0-65535 0.0.0.0/0 0-65535 - diff --git a/release/src/router/miniupnpd/natpmp.c b/release/src/router/miniupnpd/natpmp.c index baa0e740b8..455748e43d 100644 --- a/release/src/router/miniupnpd/natpmp.c +++ b/release/src/router/miniupnpd/natpmp.c @@ -1,6 +1,6 @@ -/* $Id: natpmp.c,v 1.50 2014/10/27 16:11:31 nanard Exp $ */ +/* $Id: natpmp.c,v 1.51 2015/02/08 09:18:15 nanard Exp $ */ /* MiniUPnP project - * (c) 2007-2014 Thomas Bernard + * (c) 2007-2015 Thomas Bernard * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -165,7 +165,7 @@ int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr, struct sockaddr_in6 * receiveraddr, unsigned char * msg_buff, size_t msg_buff_size) { -#if IPV6_PKTINFO +#ifdef IPV6_PKTINFO struct iovec iov; uint8_t c[1000]; struct msghdr msg; @@ -218,7 +218,7 @@ int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr, } } } -#else +#else /* IPV6_PKTINFO */ int n; n = recvfrom(s, msg_buff, msg_buff_size, 0, @@ -234,7 +234,7 @@ int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr, } return n; } -#endif +#endif /* IPV6_PKTINFO */ return n; } diff --git a/release/src/router/miniupnpd/netfilter/iptcrdr.c b/release/src/router/miniupnpd/netfilter/iptcrdr.c index 4a8e20b73d..7ba3810ed5 100644 --- a/release/src/router/miniupnpd/netfilter/iptcrdr.c +++ b/release/src/router/miniupnpd/netfilter/iptcrdr.c @@ -1,7 +1,7 @@ -/* $Id: iptcrdr.c,v 1.52 2014/03/28 12:03:36 nanard Exp $ */ +/* $Id: iptcrdr.c,v 1.54 2015/03/09 10:02:54 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2011 Thomas Bernard + * (c) 2006-2015 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include @@ -13,7 +13,8 @@ #include #include #include -#include +#include +#include #include #include @@ -29,8 +30,8 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) #define LIST_POISON2 ((void *) 0x00200200 ) -#if 1 -#include "../../iptables-1.4.x/include/net/netfilter/nf_nat.h" +#if 0 +#include #else #include "tiny_nf_nat.h" #endif @@ -42,7 +43,7 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) #include #else -#if 1 +#if 0 #include #else #include "tiny_nf_nat.h" @@ -71,6 +72,17 @@ static int add_filter_rule(int proto, const char * rhost, const char * iaddr, unsigned short iport); +static int +addpeernatrule(int proto, + const char * eaddr, unsigned short eport, + const char * iaddr, unsigned short iport, + const char * rhost, unsigned short rport); + +static int +addpeerdscprule(int proto, unsigned char dscp, + const char * iaddr, unsigned short iport, + const char * rhost, unsigned short rport); + /* dummy init and shutdown functions */ int init_redirect(void) { @@ -215,6 +227,41 @@ add_redirect_rule2(const char * ifname, return r; } +/* add_redirect_rule2() */ +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 r; + UNUSED(ifname); + + r = addpeernatrule(proto, eaddr, eport, iaddr, iport, rhost, rport); + if(r >= 0) + add_redirect_desc(eport, proto, desc, timestamp); + return r; +} + +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 r; + UNUSED(ifname); + UNUSED(desc); + UNUSED(timestamp); + + r = addpeerdscprule(proto, dscp, iaddr, iport, rhost, rport); +/* if(r >= 0) + add_redirect_desc(dscp, proto, desc, timestamp); */ + return r; +} + int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, @@ -433,6 +480,120 @@ get_redirect_rule_by_index(int index, return r; } +/* get_peer_rule_by_index() + * return -1 when the rule was 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 r = -1; +#if USE_INDEX_FROM_DESC_LIST && 0 + r = get_redirect_desc_by_index(index, eport, proto, + desc, desclen, timestamp); + if (r==0) + { + r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport, + 0, 0, packets, bytes); + } +#else + int i = 0; + IPTC_HANDLE h; + const struct ipt_entry * e; + const struct ipt_entry_target * target; + const struct ip_nat_multi_range * mr; + const struct ipt_entry_match *match; + UNUSED(ifname); + + h = iptc_init("nat"); + if(!h) + { + syslog(LOG_ERR, "get_peer_rule_by_index() : " + "iptc_init() failed : %s", + iptc_strerror(errno)); + return -1; + } + if(!iptc_is_chain(miniupnpd_peer_chain, h)) + { + syslog(LOG_ERR, "chain %s not found", miniupnpd_peer_chain); + } + else + { +#ifdef IPTABLES_143 + for(e = iptc_first_rule(miniupnpd_peer_chain, h); + e; + e = iptc_next_rule(e, h)) +#else + for(e = iptc_first_rule(miniupnpd_peer_chain, &h); + e; + e = iptc_next_rule(e, &h)) +#endif + { + if(i==index) + { + *proto = e->ip.proto; + match = (const struct ipt_entry_match *)&e->elems; + if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) + { + const struct ipt_tcp * info; + info = (const struct ipt_tcp *)match->data; + if (rport) + *rport = info->dpts[0]; + if (iport) + *iport = info->spts[0]; + } + else + { + const struct ipt_udp * info; + info = (const struct ipt_udp *)match->data; + if (rport) + *rport = info->dpts[0]; + if (iport) + *iport = info->spts[0]; + } + target = (void *)e + e->target_offset; + mr = (const struct ip_nat_multi_range *)&target->data[0]; + *eport = ntohs(mr->range[0].min.all); + get_redirect_desc(*eport, *proto, desc, desclen, timestamp); + if(packets) + *packets = e->counters.pcnt; + if(bytes) + *bytes = e->counters.bcnt; + /* rhost */ + if(rhost && rhostlen > 0) { + if(e->ip.dst.s_addr) { + snprintip(rhost, rhostlen, ntohl(e->ip.dst.s_addr)); + } else { + rhost[0] = '\0'; + } + } + if(iaddr && iaddrlen > 0) { + if(e->ip.src.s_addr) { + snprintip(iaddr, iaddrlen, ntohl(e->ip.src.s_addr)); + } else { + rhost[0] = '\0'; + } + } + r = 0; + break; + } + i++; + } + } + if(h) +#ifdef IPTABLES_143 + iptc_free(h); +#else + iptc_free(&h); +#endif +#endif + return r; +} + /* delete_rule_and_commit() : * subfunction used in delete_redirect_and_filter_rules() */ static int @@ -475,7 +636,7 @@ delete_rule_and_commit(unsigned int index, IPTC_HANDLE h, int delete_redirect_and_filter_rules(unsigned short eport, int proto) { - int r = -1; + int r = -1, r2 = -1; unsigned index = 0; unsigned i = 0; IPTC_HANDLE h; @@ -592,22 +753,134 @@ delete_redirect_and_filter_rules(unsigned short eport, int proto) if(iaddr != e->ip.dst.s_addr) continue; index = i; + syslog(LOG_INFO, "Trying to delete filter rule at index %u", index); + r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule"); + h = NULL; break; } } - syslog(LOG_INFO, "Trying to delete filter rule at index %u", index); - r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule"); } + if(h) +#ifdef IPTABLES_143 + iptc_free(h); +#else + iptc_free(&h); +#endif + } + + /*delete PEER rule*/ + if((h = iptc_init("nat"))) + { + i = 0; + /* we must find the right index for the filter rule */ +#ifdef IPTABLES_143 + for(e = iptc_first_rule(miniupnpd_peer_chain, h); + e; + e = iptc_next_rule(e, h), i++) +#else + for(e = iptc_first_rule(miniupnpd_peer_chain, &h); + e; + e = iptc_next_rule(e, &h), i++) +#endif + { + if(proto==e->ip.proto) + { + target = (void *)e + e->target_offset; + mr = (const struct ip_nat_multi_range *)&target->data[0]; + if (eport != ntohs(mr->range[0].min.all)) { + continue; + } + iaddr = e->ip.src.s_addr; + match = (const struct ipt_entry_match *)&e->elems; + if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) + { + const struct ipt_tcp * info; + info = (const struct ipt_tcp *)match->data; + iport = info->spts[0]; + } + else + { + const struct ipt_udp * info; + info = (const struct ipt_udp *)match->data; + iport = info->dpts[0]; + } + + index = i; + syslog(LOG_INFO, "Trying to delete peer rule at index %u", index); + r2 = delete_rule_and_commit(index, h, miniupnpd_peer_chain, "delete_peer_rule"); + h = NULL; + break; + } + } + } + + if(h) +#ifdef IPTABLES_143 + iptc_free(h); +#else + iptc_free(&h); +#endif + /*delete DSCP rule*/ + if((r2==0)&&(h = iptc_init("mangle"))) + { + i = 0; + index = -1; + /* we must find the right index for the filter rule */ +#ifdef IPTABLES_143 + for(e = iptc_first_rule(miniupnpd_nat_chain, h); + e; + e = iptc_next_rule(e, h), i++) +#else + for(e = iptc_first_rule(miniupnpd_nat_chain, &h); + e; + e = iptc_next_rule(e, &h), i++) +#endif + { + if(proto==e->ip.proto) + { + match = (const struct ipt_entry_match *)&e->elems; + /*syslog(LOG_DEBUG, "filter rule #%u: %s %s", + i, match->u.user.name, inet_ntoa(e->ip.dst));*/ + if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) + { + const struct ipt_tcp * info; + info = (const struct ipt_tcp *)match->data; + if(iport != info->spts[0]) + continue; + } + else + { + const struct ipt_udp * info; + info = (const struct ipt_udp *)match->data; + if(iport != info->spts[0]) + continue; + } + if(iaddr != e->ip.src.s_addr) + continue; + index = i; + syslog(LOG_INFO, "Trying to delete dscp rule at index %u", index); + r2 = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_dscp_rule"); + h = NULL; + break; + } + } + if (h) + #ifdef IPTABLES_143 + iptc_free(h); + #else + iptc_free(&h); + #endif } + del_redirect_desc(eport, proto); - return r; + return r*r2; } /* ==================================== */ /* TODO : add the -m state --state NEW,ESTABLISHED,RELATED * only for the filter rule */ static struct ipt_entry_match * -get_tcp_match(unsigned short dport) +get_tcp_match(unsigned short dport, unsigned short sport) { struct ipt_entry_match *match; struct ipt_tcp * tcpinfo; @@ -618,15 +891,25 @@ get_tcp_match(unsigned short dport) match->u.match_size = size; strncpy(match->u.user.name, "tcp", sizeof(match->u.user.name)); tcpinfo = (struct ipt_tcp *)match->data; - tcpinfo->spts[0] = 0; /* all source ports */ - tcpinfo->spts[1] = 0xFFFF; - tcpinfo->dpts[0] = dport; /* specified destination port */ - tcpinfo->dpts[1] = dport; + if (sport == 0) { + tcpinfo->spts[0] = 0; /* all source ports */ + tcpinfo->spts[1] = 0xFFFF; + } else { + tcpinfo->spts[0] = sport; /* specified source port */ + tcpinfo->spts[1] = sport; + } + if (dport == 0) { + tcpinfo->dpts[0] = 0; /* all destination ports */ + tcpinfo->dpts[1] = 0xFFFF; + } else { + tcpinfo->dpts[0] = dport; /* specified destination port */ + tcpinfo->dpts[1] = dport; + } return match; } static struct ipt_entry_match * -get_udp_match(unsigned short dport) +get_udp_match(unsigned short dport, unsigned short sport) { struct ipt_entry_match *match; struct ipt_udp * udpinfo; @@ -637,10 +920,20 @@ get_udp_match(unsigned short dport) match->u.match_size = size; strncpy(match->u.user.name, "udp", sizeof(match->u.user.name)); udpinfo = (struct ipt_udp *)match->data; - udpinfo->spts[0] = 0; /* all source ports */ - udpinfo->spts[1] = 0xFFFF; - udpinfo->dpts[0] = dport; /* specified destination port */ - udpinfo->dpts[1] = dport; + if (sport == 0) { + udpinfo->spts[0] = 0; /* all source ports */ + udpinfo->spts[1] = 0xFFFF; + } else { + udpinfo->spts[0] = sport; /* specified source port */ + udpinfo->spts[1] = sport; + } + if (dport == 0) { + udpinfo->dpts[0] = 0; /* all destination ports */ + udpinfo->dpts[1] = 0xFFFF; + } else { + udpinfo->dpts[0] = dport; /* specified destination port */ + udpinfo->dpts[1] = dport; + } return match; } @@ -668,6 +961,48 @@ get_dnat_target(const char * daddr, unsigned short dport) return target; } +static struct ipt_entry_target * +get_snat_target(const char * saddr, unsigned short sport) +{ + struct ipt_entry_target * target; + struct ip_nat_multi_range * mr; + struct ip_nat_range * range; + size_t size; + + size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + + IPT_ALIGN(sizeof(struct ip_nat_multi_range)); + target = calloc(1, size); + target->u.target_size = size; + strncpy(target->u.user.name, "SNAT", sizeof(target->u.user.name)); + /* one ip_nat_range already included in ip_nat_multi_range */ + mr = (struct ip_nat_multi_range *)&target->data[0]; + mr->rangesize = 1; + range = &mr->range[0]; + range->min_ip = range->max_ip = inet_addr(saddr); + range->flags |= IP_NAT_RANGE_MAP_IPS; + range->min.all = range->max.all = htons(sport); + range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + return target; +} + +static struct ipt_entry_target * +get_dscp_target(unsigned char dscp) +{ + struct ipt_entry_target * target; + struct xt_DSCP_info * di; + size_t size; + + size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + + IPT_ALIGN(sizeof(struct xt_DSCP_info)); + target = calloc(1, size); + target->u.target_size = size; + strncpy(target->u.user.name, "DSCP", sizeof(target->u.user.name)); + /* one ip_nat_range already included in ip_nat_multi_range */ + di = (struct xt_DSCP_info *)&target->data[0]; + di->dscp=dscp; + return target; +} + /* iptc_init_verify_and_append() * return 0 on success, -1 on failure */ static int @@ -748,25 +1083,37 @@ addnatrule(int proto, unsigned short eport, { int r = 0; struct ipt_entry * e; + struct ipt_entry * tmp; struct ipt_entry_match *match = NULL; struct ipt_entry_target *target = NULL; e = calloc(1, sizeof(struct ipt_entry)); - e->ip.proto = proto; - if(proto == IPPROTO_TCP) - { - match = get_tcp_match(eport); + if(!e) { + syslog(LOG_ERR, "%s: calloc(%d) error", "addnatrule", + (int)sizeof(struct ipt_entry)); + return -1; } - else - { - match = get_udp_match(eport); + e->ip.proto = proto; + if(proto == IPPROTO_TCP) { + match = get_tcp_match(eport, 0); + } else { + match = get_udp_match(eport, 0); } e->nfcache = NFC_IP_DST_PT; target = get_dnat_target(iaddr, iport); e->nfcache |= NFC_UNKNOWN; - e = realloc(e, sizeof(struct ipt_entry) + tmp = realloc(e, sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size); + if(!tmp) { + syslog(LOG_ERR, "%s: realloc(%d) error", "addnatrule", + (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size)); + free(e); + free(match); + free(target); + return -1; + } + e = tmp; memcpy(e->elems, match, match->u.match_size); memcpy(e->elems + match->u.match_size, target, target->u.target_size); e->target_offset = sizeof(struct ipt_entry) @@ -775,8 +1122,7 @@ addnatrule(int proto, unsigned short eport, + match->u.match_size + target->u.target_size; /* remote host */ - if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) - { + if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) { e->ip.src.s_addr = inet_addr(rhost); e->ip.smsk.s_addr = INADDR_NONE; } @@ -787,6 +1133,150 @@ addnatrule(int proto, unsigned short eport, free(e); return r; } + +/* iptables -t nat -A MINIUPNPD-PCP-PEER -s iaddr -d rhost + * -p proto --sport iport --dport rport -j SNAT + * --to-source ext_ip:eport */ +static int +addpeernatrule(int proto, + const char * eaddr, unsigned short eport, + const char * iaddr, unsigned short iport, + const char * rhost, unsigned short rport) +{ + int r = 0; + struct ipt_entry * e; + struct ipt_entry * tmp; + struct ipt_entry_match *match = NULL; + struct ipt_entry_target *target = NULL; + + e = calloc(1, sizeof(struct ipt_entry)); + if(!e) { + syslog(LOG_ERR, "%s: calloc(%d) error", "addpeernatrule", + (int)sizeof(struct ipt_entry)); + return -1; + } + e->ip.proto = proto; + /* TODO: Fill port matches and SNAT */ + if(proto == IPPROTO_TCP) { + match = get_tcp_match(rport, iport); + } else { + match = get_udp_match(rport, iport); + } + e->nfcache = NFC_IP_DST_PT | NFC_IP_SRC_PT; + target = get_snat_target(eaddr, eport); + e->nfcache |= NFC_UNKNOWN; + tmp = realloc(e, sizeof(struct ipt_entry) + + match->u.match_size + + target->u.target_size); + if(!tmp) { + syslog(LOG_ERR, "%s: realloc(%d) error", "addpeernatrule", + (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size)); + free(e); + free(match); + free(target); + return -1; + } + e = tmp; + memcpy(e->elems, match, match->u.match_size); + memcpy(e->elems + match->u.match_size, target, target->u.target_size); + e->target_offset = sizeof(struct ipt_entry) + + match->u.match_size; + e->next_offset = sizeof(struct ipt_entry) + + match->u.match_size + + target->u.target_size; + + /* internal host */ + if(iaddr && (iaddr[0] != '\0') && (0 != strcmp(iaddr, "*"))) + { + e->ip.src.s_addr = inet_addr(iaddr); + e->ip.smsk.s_addr = INADDR_NONE; + } + /* remote host */ + if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) + { + e->ip.dst.s_addr = inet_addr(rhost); + e->ip.dmsk.s_addr = INADDR_NONE; + } + + r = iptc_init_verify_and_append("nat", miniupnpd_peer_chain, e, "addpeernatrule()"); + free(target); + free(match); + free(e); + return r; +} + +/* iptables -t mangle -A MINIUPNPD -s iaddr -d rhost + * -p proto --sport iport --dport rport -j DSCP + * --set-dscp 0xXXXX */ +static int +addpeerdscprule(int proto, unsigned char dscp, + const char * iaddr, unsigned short iport, + const char * rhost, unsigned short rport) +{ + int r = 0; + struct ipt_entry * e; + struct ipt_entry * tmp; + struct ipt_entry_match *match = NULL; + struct ipt_entry_target *target = NULL; + + e = calloc(1, sizeof(struct ipt_entry)); + if(!e) { + syslog(LOG_ERR, "%s: calloc(%d) error", "addpeerdscprule", + (int)sizeof(struct ipt_entry)); + return -1; + } + e->ip.proto = proto; + /* TODO: Fill port matches and SNAT */ + if(proto == IPPROTO_TCP) { + match = get_tcp_match(rport, iport); + } else { + match = get_udp_match(rport, iport); + } + e->nfcache = NFC_IP_DST_PT | NFC_IP_SRC_PT; + target = get_dscp_target(dscp); + e->nfcache |= NFC_UNKNOWN; + tmp = realloc(e, sizeof(struct ipt_entry) + + match->u.match_size + + target->u.target_size); + if(!tmp) { + syslog(LOG_ERR, "%s: realloc(%d) error", "addpeerdscprule", + (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size)); + free(e); + free(match); + free(target); + return -1; + } + e = tmp; + memcpy(e->elems, match, match->u.match_size); + memcpy(e->elems + match->u.match_size, target, target->u.target_size); + e->target_offset = sizeof(struct ipt_entry) + + match->u.match_size; + e->next_offset = sizeof(struct ipt_entry) + + match->u.match_size + + target->u.target_size; + + /* internal host */ + if(iaddr && (iaddr[0] != '\0') && (0 != strcmp(iaddr, "*"))) + { + e->ip.src.s_addr = inet_addr(iaddr); + e->ip.smsk.s_addr = INADDR_NONE; + } + /* remote host */ + if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) + { + e->ip.dst.s_addr = inet_addr(rhost); + e->ip.dmsk.s_addr = INADDR_NONE; + } + + r = iptc_init_verify_and_append("mangle", miniupnpd_nat_chain, e, + "addpeerDSCPrule()"); + free(target); + free(match); + free(e); + return r; +} + + /* ================================= */ static struct ipt_entry_target * get_accept_target(void) @@ -809,27 +1299,39 @@ add_filter_rule(int proto, const char * rhost, { int r = 0; struct ipt_entry * e; + struct ipt_entry * tmp; struct ipt_entry_match *match = NULL; struct ipt_entry_target *target = NULL; e = calloc(1, sizeof(struct ipt_entry)); - e->ip.proto = proto; - if(proto == IPPROTO_TCP) - { - match = get_tcp_match(iport); + if(!e) { + syslog(LOG_ERR, "%s: calloc(%d) error", "add_filter_rule", + (int)sizeof(struct ipt_entry)); + return -1; } - else - { - match = get_udp_match(iport); + e->ip.proto = proto; + if(proto == IPPROTO_TCP) { + match = get_tcp_match(iport,0); + } else { + match = get_udp_match(iport,0); } e->nfcache = NFC_IP_DST_PT; e->ip.dst.s_addr = inet_addr(iaddr); e->ip.dmsk.s_addr = INADDR_NONE; target = get_accept_target(); e->nfcache |= NFC_UNKNOWN; - e = realloc(e, sizeof(struct ipt_entry) + tmp = realloc(e, sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size); + if(!tmp) { + syslog(LOG_ERR, "%s: realloc(%d) error", "add_filter_rule", + (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size)); + free(e); + free(match); + free(target); + return -1; + } + e = tmp; memcpy(e->elems, match, match->u.match_size); memcpy(e->elems + match->u.match_size, target, target->u.target_size); e->target_offset = sizeof(struct ipt_entry) @@ -919,18 +1421,22 @@ get_portmappings_in_range(unsigned short startport, unsigned short endport, { if(*number >= capacity) { + unsigned short * tmp; /* need to increase the capacity of the array */ - array = realloc(array, sizeof(unsigned short)*capacity); - if(!array) + tmp = realloc(array, sizeof(unsigned short)*capacity); + if(!tmp) { syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%u) error", (unsigned)sizeof(unsigned short)*capacity); *number = 0; + free(array); + array = NULL; break; } - array[*number] = eport; - (*number)++; + array = tmp; } + array[*number] = eport; + (*number)++; } } } diff --git a/release/src/router/miniupnpd/netfilter/iptcrdr.h b/release/src/router/miniupnpd/netfilter/iptcrdr.h index 28142f8224..3ffb06db85 100644 --- a/release/src/router/miniupnpd/netfilter/iptcrdr.h +++ b/release/src/router/miniupnpd/netfilter/iptcrdr.h @@ -17,6 +17,13 @@ add_redirect_rule2(const char * ifname, 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, @@ -26,6 +33,23 @@ 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_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto, + struct sockaddr* ret_ext); +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, diff --git a/release/src/router/miniupnpd/netfilter/iptpinhole.c b/release/src/router/miniupnpd/netfilter/iptpinhole.c index e96a7d3bfa..978e4c0ca0 100644 --- a/release/src/router/miniupnpd/netfilter/iptpinhole.c +++ b/release/src/router/miniupnpd/netfilter/iptpinhole.c @@ -1,7 +1,7 @@ -/* $Id: iptpinhole.c,v 1.11 2014/05/30 14:37:04 nanard Exp $ */ +/* $Id: iptpinhole.c,v 1.14 2015/02/10 15:01:03 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2012 Thomas Bernard + * (c) 2012-2015 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -201,17 +201,23 @@ int add_pinhole(const char * ifname, { int uid; struct ip6t_entry * e; + struct ip6t_entry * tmp; struct ip6t_entry_match *match = NULL; struct ip6t_entry_target *target = NULL; e = calloc(1, sizeof(struct ip6t_entry)); + if(!e) { + syslog(LOG_ERR, "%s: calloc(%d) failed", + "add_pinhole", (int)sizeof(struct ip6t_entry)); + return -1; + } e->ipv6.proto = proto; if (proto) e->ipv6.flags |= IP6T_F_PROTO; if(ifname) strncpy(e->ipv6.iniface, ifname, IFNAMSIZ); - if(rem_host) { + if(rem_host && (rem_host[0] != '\0')) { inet_pton(AF_INET6, rem_host, &e->ipv6.src); memset(&e->ipv6.smsk, 0xff, sizeof(e->ipv6.smsk)); } @@ -223,9 +229,18 @@ int add_pinhole(const char * ifname, match = new_match(proto, rem_port, int_port); target = get_accept_target(); - e = realloc(e, sizeof(struct ip6t_entry) + tmp = realloc(e, sizeof(struct ip6t_entry) + match->u.match_size + target->u.target_size); + if(!tmp) { + syslog(LOG_ERR, "%s: realloc(%d) failed", + "add_pinhole", (int)(sizeof(struct ip6t_entry) + match->u.match_size + target->u.target_size)); + free(e); + free(match); + free(target); + return -1; + } + e = tmp; memcpy(e->elems, match, match->u.match_size); memcpy(e->elems + match->u.match_size, target, target->u.target_size); e->target_offset = sizeof(struct ip6t_entry) @@ -335,7 +350,7 @@ get_pinhole_info(unsigned short uid, p = get_pinhole(uid); if(!p) return -2; /* Not found */ - if(rem_host) { + if(rem_host && (rem_host[0] != '\0')) { if(inet_ntop(AF_INET6, &p->saddr, rem_host, rem_hostlen) == NULL) return -1; } diff --git a/release/src/router/miniupnpd/netfilter_nft/Makefile b/release/src/router/miniupnpd/netfilter_nft/Makefile new file mode 100644 index 0000000000..19846cffe1 --- /dev/null +++ b/release/src/router/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/release/src/router/miniupnpd/netfilter_nft/README.md b/release/src/router/miniupnpd/netfilter_nft/README.md new file mode 100644 index 0000000000..b37d4823fd --- /dev/null +++ b/release/src/router/miniupnpd/netfilter_nft/README.md @@ -0,0 +1,21 @@ +Miniupnpd nftables support by Tomofumi Hayashi (s1061123@gmail.com). + +##Current Status +nftables support is 'alpha' version, not "so much" stable. + +##Supported Features +- IPv4 NAT/Filter add/del. + +##How to build miniupnpd with nftables: +Run 'make' command with 'Makefile.linux_nft', + +`make -f Makefile.linux_nft` + +##How to Run +Please run 'netfilter_nft/scripts/nft_init.sh' to add miniupnpd chain. + +`sudo ./netfilter_nft/scripts/nft_init.sh` + +##FAQ +I will add this section when I get question. +Comments and Questions are welcome ;) diff --git a/release/src/router/miniupnpd/netfilter_nft/nfct_get.c b/release/src/router/miniupnpd/netfilter_nft/nfct_get.c new file mode 100644 index 0000000000..cbffd282be --- /dev/null +++ b/release/src/router/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/release/src/router/miniupnpd/netfilter_nft/nftnlrdr.c b/release/src/router/miniupnpd/netfilter_nft/nftnlrdr.c new file mode 100644 index 0000000000..389273f390 --- /dev/null +++ b/release/src/router/miniupnpd/netfilter_nft/nftnlrdr.c @@ -0,0 +1,499 @@ +/* + * 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" + +#ifdef DEBUG +#define d_printf(x) do { printf x; } while (0) +#else +#define d_printf(x) +#endif + +/* 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(rhost); + UNUSED(timestamp); + 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); +} + +/* + * 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); + + 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); +} + +/* + * 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; + + 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, + 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) ; + + d_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); + break; + } + } + + 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); + break; + } + } + + 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); + + d_printf(("get_peer_rule_by_index()\n")); + reflesh_nft_cache(NFPROTO_IPV4); + if (peer_cache == NULL) { + return -1; + } + + for (i = 0; peer_cache[i] != NULL; i++) { + if (index == i) { + r = peer_cache[i]; + 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); + } + + /* + * 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); + + d_printf(("get_redirect_rule_by_index()\n")); + reflesh_nft_cache(NFPROTO_IPV4); + if (redirect_cache == NULL) { + return -1; + } + + for (i = 0; redirect_cache[i] != NULL; i++) { + if (index == i) { + r = redirect_cache[i]; + 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); + } + + /* + * 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); + + d_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; + + d_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/release/src/router/miniupnpd/netfilter_nft/nftnlrdr.h b/release/src/router/miniupnpd/netfilter_nft/nftnlrdr.h new file mode 100644 index 0000000000..1acf4b3356 --- /dev/null +++ b/release/src/router/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/release/src/router/miniupnpd/netfilter_nft/nftnlrdr_misc.c b/release/src/router/miniupnpd/netfilter_nft/nftnlrdr_misc.c new file mode 100644 index 0000000000..a35c45e7dc --- /dev/null +++ b/release/src/router/miniupnpd/netfilter_nft/nftnlrdr_misc.c @@ -0,0 +1,1145 @@ +/* + * 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" + +#ifdef DEBUG +#define d_printf(x) do { printf x; } while (0) +#else +#define d_printf(x) +#endif + +#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->ingress_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->ingress_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->ingress_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("nftables: 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, reg_len; + + 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 = *(uint32_t *)nft_rule_expr_get(e, + NFT_EXPR_IMM_DATA, + ®_len); + } + + 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 ; + + case NFT_META_OIF: + 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; + uint16_t proto_min_val; + 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"); + } + + proto_min_val = htons((uint16_t)*get_reg_val_ptr(r, proto_min_reg)); + if (r->nat_type == NFT_NAT_DNAT) { + r->iaddr = (in_addr_t)*get_reg_val_ptr(r, addr_min_reg); + r->iport = proto_min_val; + } else if (r->nat_type == NFT_NAT_SNAT) { + r->eaddr = (in_addr_t)*get_reg_val_ptr(r, addr_min_reg); + if (proto_min_reg == NFT_REG_1) { + r->eport = proto_min_val; + } + } + + 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->ingress_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 { + syslog(LOG_ERR, "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; + char *chain; + 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; + } + + chain = (char *)nft_rule_attr_get_data(t, NFT_RULE_ATTR_CHAIN, &len); + if (strcmp(chain, miniupnpd_nft_nat_chain) != 0 && + strcmp(chain, miniupnpd_nft_peer_chain) != 0 && + strcmp(chain, miniupnpd_nft_forward_chain) != 0) { + goto rule_skip; + } + + r->table = strdup( + (char *)nft_rule_attr_get_data(t, NFT_RULE_ATTR_TABLE, &len)); + r->chain = strdup(chain); + 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); + } + + if (r->type == RULE_NONE) { + free(r); + } else { + LIST_INSERT_HEAD(&head, r, entry); + rule_list_length++; + } + +rule_skip: +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; + + if (redirect_cache != NULL) { + free(redirect_cache); + } + len = rule_list_length - rule_list_peer_length; + if (len == 0) { + redirect_cache = NULL; + return; + } + + 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; + + if (peer_cache != NULL) { + free(peer_cache); + } + if (rule_list_peer_length == 0) { + peer_cache = NULL; + 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++; + } + } + + 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); + if (p1->desc != NULL) { + free(p1->desc); + } + if (p1->table != NULL) { + free(p1->table); + } + if (p1->chain != NULL) { + 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_u32(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_val_u16(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_u16(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_u32(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_u16(r, NFT_REG_2, proto_min); + nft_rule_expr_set_u16(e, NFT_EXPR_NAT_REG_PROTO_MIN, NFT_REG_2); + nft_rule_expr_set_u16(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 = htons(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, htons(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_HANDLE, 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/release/src/router/miniupnpd/netfilter_nft/nftnlrdr_misc.h b/release/src/router/miniupnpd/netfilter_nft/nftnlrdr_misc.h new file mode 100644 index 0000000000..e04403e86a --- /dev/null +++ b/release/src/router/miniupnpd/netfilter_nft/nftnlrdr_misc.h @@ -0,0 +1,91 @@ +/* + * 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_OIF, + 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 ingress_ifidx; + uint32_t egress_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/release/src/router/miniupnpd/netfilter_nft/scripts/nft_delete_chain.sh b/release/src/router/miniupnpd/netfilter_nft/scripts/nft_delete_chain.sh new file mode 100644 index 0000000000..97f9fbe802 --- /dev/null +++ b/release/src/router/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/release/src/router/miniupnpd/netfilter_nft/scripts/nft_flush.sh b/release/src/router/miniupnpd/netfilter_nft/scripts/nft_flush.sh new file mode 100644 index 0000000000..bd6c878ee8 --- /dev/null +++ b/release/src/router/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/release/src/router/miniupnpd/netfilter_nft/scripts/nft_init.sh b/release/src/router/miniupnpd/netfilter_nft/scripts/nft_init.sh new file mode 100644 index 0000000000..45b42ea00f --- /dev/null +++ b/release/src/router/miniupnpd/netfilter_nft/scripts/nft_init.sh @@ -0,0 +1,47 @@ +#! /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" +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" +fi +if [ $nft_filter_miniupnpd_exists -eq "1" ]; then + echo "create chain in filter " + nft "add chain filter miniupnpd" +fi +if [ $nft_mangle_miniupnpd_exists -eq "1" ]; then + echo "create chain in mangle" + nft "add chain mangle miniupnpd" +fi diff --git a/release/src/router/miniupnpd/netfilter_nft/scripts/nft_removeall.sh b/release/src/router/miniupnpd/netfilter_nft/scripts/nft_removeall.sh new file mode 100644 index 0000000000..11c38e76f3 --- /dev/null +++ b/release/src/router/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/release/src/router/miniupnpd/netfilter_nft/test_nfct_get.c b/release/src/router/miniupnpd/netfilter_nft/test_nfct_get.c new file mode 100644 index 0000000000..af8c07d76d --- /dev/null +++ b/release/src/router/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/release/src/router/miniupnpd/netfilter_nft/testnftnlrdr.c b/release/src/router/miniupnpd/netfilter_nft/testnftnlrdr.c new file mode 100644 index 0000000000..1491a70fd6 --- /dev/null +++ b/release/src/router/miniupnpd/netfilter_nft/testnftnlrdr.c @@ -0,0 +1,91 @@ +/* $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 + * 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/release/src/router/miniupnpd/netfilter_nft/tiny_nf_nat.h b/release/src/router/miniupnpd/netfilter_nft/tiny_nf_nat.h new file mode 100644 index 0000000000..051e2c13fd --- /dev/null +++ b/release/src/router/miniupnpd/netfilter_nft/tiny_nf_nat.h @@ -0,0 +1,37 @@ +/* $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 +#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*/ diff --git a/release/src/router/miniupnpd/pf/obsdrdr.c b/release/src/router/miniupnpd/pf/obsdrdr.c index 3a38d4a369..7c6799b52b 100644 --- a/release/src/router/miniupnpd/pf/obsdrdr.c +++ b/release/src/router/miniupnpd/pf/obsdrdr.c @@ -1,7 +1,7 @@ -/* $Id: obsdrdr.c,v 1.83 2014/06/27 13:08:49 nanard Exp $ */ +/* $Id: obsdrdr.c,v 1.84 2015/02/08 08:55:55 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2014 Thomas Bernard + * (c) 2006-2015 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -959,14 +959,17 @@ get_portmappings_in_range(unsigned short startport, unsigned short endport, if(*number >= capacity) { /* need to increase the capacity of the array */ + unsigned short * tmp; capacity += 128; - array = realloc(array, sizeof(unsigned short)*capacity); - if(!array) + tmp = realloc(array, sizeof(unsigned short)*capacity); + if(!tmp) { syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); *number = 0; + free(array); return NULL; } + array = tmp; } array[*number] = eport; (*number)++; diff --git a/release/src/router/miniupnpd/upnpglobalvars.c b/release/src/router/miniupnpd/upnpglobalvars.c index 9e50e2d3bc..e515fe193c 100644 --- a/release/src/router/miniupnpd/upnpglobalvars.c +++ b/release/src/router/miniupnpd/upnpglobalvars.c @@ -1,4 +1,4 @@ -/* $Id: upnpglobalvars.c,v 1.37 2014/05/22 07:51:08 nanard Exp $ */ +/* $Id: upnpglobalvars.c,v 1.39 2014/12/10 09:49:22 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard @@ -119,6 +119,28 @@ struct in6_addr ipv6_bind_addr; const char * minissdpdsocketpath = "/var/run/minissdpd.sock"; /* BOOTID.UPNP.ORG and CONFIGID.UPNP.ORG */ -unsigned int upnp_bootid = 1; -unsigned int upnp_configid = 1337; +/* See UPnP Device Architecture v1.1 section 1.2 Advertisement : + * The field value of the BOOTID.UPNP.ORG header field MUST be increased + * each time a device (re)joins the network and sends an initial announce + * (a "reboot" in UPnP terms), or adds a UPnP-enabled interface. + * Unless the device explicitly announces a change in the BOOTID.UPNP.ORG + * field value using an SSDP message, as long as the device remains + * continuously available in the network, the same BOOTID.UPNP.ORG field + * value MUST be used in all repeat announcements, search responses, + * update messages and eventually bye-bye messages. */ +unsigned int upnp_bootid = 1; /* BOOTID.UPNP.ORG */ +/* The field value of the CONFIGID.UPNP.ORG header field identifies the + * current set of device and service descriptions; control points can + * parse this header field to detect whether they need to send new + * description query messages. */ +/* UPnP 1.1 devices MAY freely assign configid numbers from 0 to + * 16777215 (2^24-1). Higher numbers are reserved for future use, and + * can be assigned by the Technical Committee. The configuration of a + * root device consists of the following information: the DDD of the + * root device and all its embedded devices, and the SCPDs of all the + * contained services. If any part of the configuration changes, the + * CONFIGID.UPNP.ORG field value MUST be changed. + * DDD = Device Description Document + * SCPD = Service Control Protocol Description */ +unsigned int upnp_configid = 1337; /* CONFIGID.UPNP.ORG */ diff --git a/release/src/router/miniupnpd/upnphttp.c b/release/src/router/miniupnpd/upnphttp.c index 066606820c..958b404bab 100644 --- a/release/src/router/miniupnpd/upnphttp.c +++ b/release/src/router/miniupnpd/upnphttp.c @@ -1,4 +1,4 @@ -/* $Id: upnphttp.c,v 1.94 2014/12/09 09:46:45 nanard Exp $ */ +/* $Id: upnphttp.c,v 1.99 2014/12/09 17:25:30 nanard Exp $ */ /* Project : miniupnp * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard @@ -220,7 +220,7 @@ ParseHttpHeaders(struct upnphttp * h) } if(colon) { - if(strncasecmp(line, "Content-Length", 14)==0) + if(strncasecmp(line, "Content-Length:", 15)==0) { p = colon; while((*p < '0' || *p > '9') && (*p != '\r') && (*p != '\n')) @@ -234,7 +234,18 @@ ParseHttpHeaders(struct upnphttp * h) printf(" readbufflen=%d contentoff = %d\n", h->req_buflen, h->req_contentoff);*/ } - else if(strncasecmp(line, "SOAPAction", 10)==0) + else if(strncasecmp(line, "Host:", 5)==0) + { + p = colon; + n = 0; + while(*p == ':' || *p == ' ' || *p == '\t') + p++; + while(p[n]>' ') + n++; + h->req_HostOff = p - h->req_buf; + h->req_HostLen = n; + } + else if(strncasecmp(line, "SOAPAction:", 11)==0) { p = colon; n = 0; @@ -250,7 +261,7 @@ ParseHttpHeaders(struct upnphttp * h) h->req_soapActionOff = p - h->req_buf; h->req_soapActionLen = n; } - else if(strncasecmp(line, "accept-language", 15) == 0) + else if(strncasecmp(line, "accept-language:", 16) == 0) { p = colon; n = 0; @@ -268,7 +279,7 @@ ParseHttpHeaders(struct upnphttp * h) memcpy(h->accept_language, p, n); h->accept_language[n] = '\0'; } - else if(strncasecmp(line, "expect", 6) == 0) + else if(strncasecmp(line, "expect:", 7) == 0) { p = colon; n = 0; @@ -282,7 +293,7 @@ ParseHttpHeaders(struct upnphttp * h) } } #ifdef ENABLE_EVENTS - else if(strncasecmp(line, "Callback", 8)==0) + else if(strncasecmp(line, "Callback:", 9)==0) { /* The Callback can contain several urls : * If there is more than one URL, when the service sends @@ -301,10 +312,10 @@ ParseHttpHeaders(struct upnphttp * h) h->req_CallbackOff = p - h->req_buf; h->req_CallbackLen = MAX(0, n + 1); } - else if(strncasecmp(line, "SID", 3)==0) + else if(strncasecmp(line, "SID:", 4)==0) { p = colon + 1; - while(isspace(*p)) + while((*p == ' ') || (*p == '\t')) p++; n = 0; while(!isspace(p[n])) @@ -319,20 +330,20 @@ either number of seconds or infinite. Recommendation by a UPnP Forum working committee. Defined by UPnP vendor. Consists of the keyword "Second-" followed (without an intervening space) by either an integer or the keyword "infinite". */ - else if(strncasecmp(line, "Timeout", 7)==0) + else if(strncasecmp(line, "Timeout:", 8)==0) { p = colon + 1; - while(isspace(*p)) + while((*p == ' ') || (*p == '\t')) p++; if(strncasecmp(p, "Second-", 7)==0) { h->req_Timeout = atoi(p+7); } } #ifdef UPNP_STRICT - else if(strncasecmp(line, "nt", 2)==0) + else if(strncasecmp(line, "nt:", 3)==0) { p = colon + 1; - while(isspace(*p)) + while((*p == ' ') || (*p == '\t')) p++; n = 0; while(!isspace(p[n])) @@ -746,6 +757,35 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h) syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)", HttpCommand, HttpUrl, HttpVer); ParseHttpHeaders(h); + if(h->req_HostOff > 0 && h->req_HostLen > 0) { + syslog(LOG_DEBUG, "Host: %.*s", h->req_HostLen, h->req_buf + h->req_HostOff); + p = h->req_buf + h->req_HostOff; + if(*p == '[') { + /* IPv6 */ + p++; + while(p < h->req_buf + h->req_HostOff + h->req_HostLen) { + if(*p == ']') break; + /* TODO check *p in [0-9a-f:.] */ + p++; + } + if(*p != ']') { + syslog(LOG_NOTICE, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_buf + h->req_HostOff); + Send404(h);/* 403 */ + return; + } + p++; + /* TODO : Check port */ + } else { + for(i = 0; i < h->req_HostLen; i++) { + if(*p != ':' && *p != '.' && (*p > '9' || *p < '0')) { + syslog(LOG_NOTICE, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_buf + h->req_HostOff); + Send404(h);/* 403 */ + return; + } + p++; + } + } + } if(strcmp("POST", HttpCommand) == 0) { h->req_command = EPost; @@ -1092,9 +1132,10 @@ BuildResp2_upnphttp(struct upnphttp * h, int respcode, const char * body, int bodylen) { int r = BuildHeader_upnphttp(h, respcode, respmsg, bodylen); - if(body && (r >= 0)) + if(body && (r >= 0)) { memcpy(h->res_buf + h->res_buflen, body, bodylen); - h->res_buflen += bodylen; + h->res_buflen += bodylen; + } } /* responding 200 OK ! */ diff --git a/release/src/router/miniupnpd/upnphttp.h b/release/src/router/miniupnpd/upnphttp.h index 70fef8fcd2..a060211520 100644 --- a/release/src/router/miniupnpd/upnphttp.h +++ b/release/src/router/miniupnpd/upnphttp.h @@ -1,4 +1,4 @@ -/* $Id: upnphttp.h,v 1.39 2014/12/09 09:46:46 nanard Exp $ */ +/* $Id: upnphttp.h,v 1.40 2014/12/09 16:41:21 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard @@ -72,6 +72,8 @@ struct upnphttp { enum httpCommands req_command; int req_soapActionOff; int req_soapActionLen; + int req_HostOff; /* Host: header */ + int req_HostLen; #ifdef ENABLE_EVENTS int req_CallbackOff; /* For SUBSCRIBE */ int req_CallbackLen; diff --git a/release/src/router/miniupnpd/upnpsoap.c b/release/src/router/miniupnpd/upnpsoap.c index e8db166624..7d126a1dfa 100644 --- a/release/src/router/miniupnpd/upnpsoap.c +++ b/release/src/router/miniupnpd/upnpsoap.c @@ -1,7 +1,7 @@ -/* $Id: upnpsoap.c,v 1.132 2014/12/09 09:46:46 nanard Exp $ */ +/* $Id: upnpsoap.c,v 1.136 2015/03/07 15:52:30 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2014 Thomas Bernard + * (c) 2006-2015 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -681,19 +681,24 @@ DeletePortMapping(struct upnphttp * h, const char * action) ""; struct NameValueParserData data; - const char * r_host, * ext_port, * protocol; + const char * ext_port, * protocol; unsigned short eport; +#ifdef UPNP_STRICT + const char * r_host; +#endif /* UPNP_STRICT */ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); - r_host = GetValueFromNameValueList(&data, "NewRemoteHost"); ext_port = GetValueFromNameValueList(&data, "NewExternalPort"); protocol = GetValueFromNameValueList(&data, "NewProtocol"); +#ifdef UPNP_STRICT + r_host = GetValueFromNameValueList(&data, "NewRemoteHost"); +#endif /* UPNP_STRICT */ #ifdef UPNP_STRICT if(!ext_port || !protocol || !r_host) #else if(!ext_port || !protocol) -#endif +#endif /* UPNP_STRICT */ { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); @@ -707,8 +712,8 @@ DeletePortMapping(struct upnphttp * h, const char * action) SoapError(h, 726, "RemoteHostOnlySupportsWildcard"); return; } -#endif -#endif +#endif /* UPNP_STRICT */ +#endif /* SUPPORT_REMOTEHOST */ eport = (unsigned short)atoi(ext_port); @@ -1145,19 +1150,21 @@ GetDefaultConnectionService(struct upnphttp * h, const char * action) static void SetConnectionType(struct upnphttp * h, const char * action) { +#ifdef UPNP_STRICT const char * connection_type; +#endif /* UPNP_STRICT */ struct NameValueParserData data; UNUSED(action); ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); - connection_type = GetValueFromNameValueList(&data, "NewConnectionType"); #ifdef UPNP_STRICT + connection_type = GetValueFromNameValueList(&data, "NewConnectionType"); if(!connection_type) { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } -#endif +#endif /* UPNP_STRICT */ /* Unconfigured, IP_Routed, IP_Bridged */ ClearNameValueList(&data); /* always return a ReadOnly error */ @@ -1477,7 +1484,7 @@ AddPinhole(struct upnphttp * h, const char * action) } /* I guess it is useless to convert int_ip to literal ipv6 address */ /* rem_host should be converted to literal ipv6 : */ - if(rem_host) + if(rem_host && (rem_host[0] != '\0')) { struct addrinfo *ai, *p; struct addrinfo hints; @@ -2078,7 +2085,7 @@ ExecuteSoapAction(struct upnphttp * h, const char * action, int n) len = strlen(soapMethods[i].methodName); if((len == methodlen) && memcmp(p, soapMethods[i].methodName, len) == 0) { #ifdef DEBUG - syslog(LOG_DEBUG, "Remote Call of SoapMethod '%s'\n", + syslog(LOG_DEBUG, "Remote Call of SoapMethod '%s'", soapMethods[i].methodName); #endif /* DEBUG */ soapMethods[i].methodImpl(h, soapMethods[i].methodName); -- 2.11.4.GIT