Compare commits

..

1 Commits
v3.17 ... demos

Author SHA1 Message Date
Jan Engelhardt
867a920fd7 ipaddr match (from "Writing your own Netfilter module" PDF) 2012-12-27 08:01:22 +01:00
116 changed files with 3689 additions and 2520 deletions

3
.gitignore vendored
View File

@@ -1,10 +1,7 @@
*.gcno
*.la *.la
*.lo *.lo
*.loT *.loT
*.mod
*.o *.o
.cache.mk
.deps/ .deps/
.dirstamp .dirstamp
.libs/ .libs/

View File

@@ -12,17 +12,16 @@ in combination with the kernel's Kbuild system.
Supported configurations for this release Supported configurations for this release
========================================= =========================================
* iptables >= 1.6.0 * iptables >= 1.4.5
* kernel-devel >= 4.15 * kernel-devel >= 3.7
with prepared build/output directory with prepared build/output directory
- CONFIG_NF_CONNTRACK - CONFIG_NF_CONNTRACK
- CONFIG_NF_CONNTRACK_MARK enabled =y or as module (=m) - CONFIG_NF_CONNTRACK_MARK enabled =y or as module (=m)
- CONFIG_CONNECTOR y/m if you wish to receive userspace - CONFIG_CONNECTOR y/m if you wish to receive userspace
notifications from pknock through netlink/connector notifications from pknock through netlink/connector
(Use xtables-addons-1.x if you need support for Linux < 3.7. (Use xtables-addons-1.x if you need support for Linux < 3.7.)
Use xtables-addons-2.x if you need support for Linux < 4.15.)
Selecting extensions Selecting extensions

View File

@@ -1,7 +1,7 @@
# -*- Makefile -*- # -*- Makefile -*-
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = extensions extensions/ACCOUNT extensions/pknock geoip SUBDIRS = extensions geoip
man_MANS := xtables-addons.8 man_MANS := xtables-addons.8
@@ -11,10 +11,8 @@ FORCE:
xtables-addons.8: FORCE xtables-addons.8: FORCE
${MAKE} -f Makefile.mans all; ${MAKE} -f Makefile.mans all;
clean-local-mans: install-exec-hook:
${MAKE} -f Makefile.mans clean; depmod -a || :;
clean-local: clean-local-mans
config.status: Makefile.iptrules.in config.status: Makefile.iptrules.in

View File

@@ -3,8 +3,8 @@
srcdir := @srcdir@ srcdir := @srcdir@
wcman_matches := $(shell find "${srcdir}/extensions" -name 'libxt_[a-z]*.man' -print | sort) wcman_matches := $(shell find "${srcdir}" -name 'libxt_[a-z]*.man' | sort)
wcman_targets := $(shell find "${srcdir}/extensions" -name 'libxt_[A-Z]*.man' -print | sort) wcman_targets := $(shell find "${srcdir}" -name 'libxt_[A-Z]*.man' | sort)
wlist_matches := $(patsubst ${srcdir}/libxt_%.man,%,${wcman_matches}) wlist_matches := $(patsubst ${srcdir}/libxt_%.man,%,${wcman_matches})
wlist_targets := $(patsubst ${srcdir}/libxt_%.man,%,${wcman_targets}) wlist_targets := $(patsubst ${srcdir}/libxt_%.man,%,${wcman_targets})
@@ -38,6 +38,3 @@ matches.man: .manpages.lst ${wcman_matches}
targets.man: .manpages.lst ${wcman_targets} targets.man: .manpages.lst ${wcman_targets}
$(call man_run,${wlist_targets}) $(call man_run,${wlist_targets})
clean:
rm -f xtables-addons.8 matches.man targets.man .manpages.lst

54
README
View File

@@ -1,15 +1,57 @@
Xtables-addons Xtables-addons
============== ==============
Xtables-addons is a set of extensions that were not accepted in the Xtables-addons is the proclaimed successor to patch-o-matic(-ng). It
Linux kernel and/or main Xtables/iptables package. contains extensions that were not accepted in the main Xtables
package.
It superseded the earlier patch-o-matic(-ng) package in that no Xtables-addons is different from patch-o-matic in that you do not
patching and/or recompilation of either the kernel or have to patch or recompile either kernel or Xtables(iptables). But
Xtables/iptables is required. However, do see the INSTALL file for please see the INSTALL file for the minimum requirements of this
the minimum requirements of Xtables-addons. package.
All code imported from patch-o-matic has been reviewed and all
apparent bugs like binary stability across multiarches, missing
sanity checks and incorrect endianess handling have been fixed,
simplified, and sped up.
Included in this package Included in this package
======================== ========================
- xt_ACCOUNT 1.16, libxt_ACCOUNT 1.3 - xt_ACCOUNT 1.16, libxt_ACCOUNT 1.3
Inclusion into a kernel tree
============================
External extensions
===================
The program "xa-download-more" can be used to download more
extensions from 3rd parties into the source tree. The URLs are listed
in the "sources" file. If the "sources" file contains an entry like
http://foobar.org/xa/
xa-download-more will inspect http://foobar.org/xa/xa-index.txt for
files to download. That file may contain
foobar.tar.bz2
and xa-download-more will then retrieve and unpack
http://foobar.org/xa/foobar.tar.bz2.
Files that should be contained in the tarball are an mconfig and
Kbuild files to control building the extension, libxt_foobar.c for
the userspace extension and xt_foobar.c for the kernel extension.
mconfig.foobar
extensions/Kbuild.foobar
extensions/Mbuild.foobar
extensions/libxt_foobar.c
extensions/libxt_foobar.man
extensions/xt_foobar.c
extensions/xt_foobar.h

View File

@@ -1,4 +1,4 @@
AC_INIT([xtables-addons], [3.17]) AC_INIT([xtables-addons], [2.1])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
@@ -26,8 +26,8 @@ fi
AC_CHECK_HEADERS([linux/netfilter/x_tables.h], [], AC_CHECK_HEADERS([linux/netfilter/x_tables.h], [],
[AC_MSG_ERROR([You need to have linux/netfilter/x_tables.h, see INSTALL file for details])]) [AC_MSG_ERROR([You need to have linux/netfilter/x_tables.h, see INSTALL file for details])])
PKG_CHECK_MODULES([libxtables], [xtables >= 1.6.0]) PKG_CHECK_MODULES([libxtables], [xtables >= 1.4.5])
xtlibdir="$($PKG_CONFIG --variable=xtlibdir xtables)" xtlibdir="$(pkg-config --variable=xtlibdir xtables)"
AC_ARG_WITH([xtlibdir], AC_ARG_WITH([xtlibdir],
AS_HELP_STRING([--with-xtlibdir=PATH], AS_HELP_STRING([--with-xtlibdir=PATH],
@@ -44,25 +44,29 @@ regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
if test -n "$kbuilddir"; then if test -n "$kbuilddir"; then
AC_MSG_CHECKING([kernel version that we will build against]) AC_MSG_CHECKING([kernel version that we will build against])
krel="$(make -sC "$kbuilddir" M=$PWD kernelrelease | $AWK -v 'FS=[[^0-9.]]' '{print $1; exit}')" krel="$(make -sC "$kbuilddir" M=$PWD kernelrelease)";
save_IFS="$IFS" kmajor="${krel%%[[^0-9]]*}";
IFS='.' kmajor="$(($kmajor+0))";
set x $krel krel="${krel:${#kmajor}}";
IFS="$save_IFS" krel="${krel#.}";
kmajor="$(($2+0))" kminor="${krel%%[[^0-9]]*}";
kminor="$(($3+0))" kminor="$(($kminor+0))";
kmicro="$(($4+0))" krel="${krel:${#kminor}}";
kstable="$(($5+0))" krel="${krel#.}";
kmicro="${krel%%[[^0-9]]*}";
kmicro="$(($kmicro+0))";
krel="${krel:${#kmicro}}";
krel="${krel#.}";
kstable="${krel%%[[^0-9]]*}";
kstable="$(($kstable+0))";
if test -z "$kmajor" -o -z "$kminor" -o -z "$kmicro"; then if test -z "$kmajor" -o -z "$kminor" -o -z "$kmicro"; then
echo "WARNING: Version detection did not succeed. Continue at own luck."; echo "WARNING: Version detection did not succeed. Continue at own luck.";
else else
echo "$kmajor.$kminor.$kmicro.$kstable in $kbuilddir"; echo "$kmajor.$kminor.$kmicro.$kstable in $kbuilddir";
if test "$kmajor" -gt 5 -o "$kmajor" -eq 5 -a "$kminor" -gt 11; then if test "$kmajor" -gt 3 -o "$kmajor" -eq 3 -a "$kminor" -gt 8; then
echo "WARNING: That kernel version is not officially supported yet. Continue at own luck."; echo "WARNING: That kernel version is not officially supported.";
elif test "$kmajor" -eq 5 -a "$kminor" -ge 0; then elif test "$kmajor" -eq 3 -a "$kminor" -ge 7; then
: :;
elif test "$kmajor" -eq 4 -a "$kminor" -ge 15; then
:
else else
echo "WARNING: That kernel version is not officially supported."; echo "WARNING: That kernel version is not officially supported.";
fi; fi;

View File

@@ -1,219 +1,6 @@
v3.17 (2021-02-28)
==================
- xt_pknock: cure a NULL deref
HEAD
v3.16 (2021-02-24) ====
==================
- xt_pknock: build fix for ILP32 targets
v3.15 (2021-02-05)
==================
- xt_ECHO: support new function signature of security_skb_classify_flow
- xt_lscan: add --mirai option
- Support for Linux 5.11
v3.14 (2020-11-24)
==================
- DELUDE, ECHO, TARPIT: use actual tunnel socket (ip_route_me_harder).
- geoip: scripts for use with MaxMind DB have been brought back,
partly under new names.
- Gave xt_geoip_fetch a more fitting name, xt_geoip_query.
v3.13 (2020-11-20)
==================
- Support for Linux 4.19.158 and 5.4.78 (ip_route_me_harder)
v3.12 (2020-11-19)
==================
- Support for Linux 5.10 and 5.9.9 API
(changes to ip_route_me_harder there)
v3.11 (2020-09-06)
==================
- Support for up to Linux 5.9
v3.10 (2020-07-28)
==================
- Support for up to Linux 5.8
v3.9 (2020-02-25)
=================
- Support for Linux 5.6 procfs changes
v3.8 (2020-02-03)
=================
- Support for Linux 5.5
- xt_geoip_build now expects the DBIP format as input,
Maxmind is thrown out.
v3.7 (2019-12-01)
=================
Fixes:
- xt_geoip: fix in6_addr little-endian byte swapping
v3.6 (2019-11-20)
=================
Enhancements:
- support for up to Linux 5.4
v3.5 (2019-09-10)
=================
Enhancements:
- xt_DELUDE, xt_TARPIT: added additional code needed to work with
bridges from Linux 5.0 onwards.
v3.4 (2019-09-06)
=================
Enhancements:
- support for up to Linux 5.3
- xt_PROTO module
v3.3 (2019-03-07)
=================
Enhancements:
- support for Linux 5.0
v3.2 (2018-09-07)
=================
Changes:
- rework xt_geoip_build to scan the immediate directory for .csv,
not to scan for GeoLite2-Country-CSV_\d+.
v3.1 (2018-08-14)
=================
Enhancements:
- support for Linux 4.17, 4.18
v3.0 (2018-02-12)
=================
Enhancements:
- support for Linux 4.15, 4.16
Changes:
- remove support for Linux 3.7--4.14
v2.14 (2017-11-22)
==================
Enhancements:
- support for Linux up to 4.14
Fixes:
- xt_DNETMAP: fix some reports from PVSStudio (a static checker)
v2.13 (2017-06-29)
==================
Enhancements:
- support for Linux up to 4.12
- xt_condition: namespace support
Fixes:
- xt_geoip: check for allocation overflow
- xt_DNETMAP: fix a buffer overflow
v2.12 (2017-01-11)
==================
Enhancements:
- support for Linux up to 4.10
v2.11 (2016-05-20)
==================
Enhancements:
- support for Linux 4.5, 4.6
- xt_ECHO: tentatively support responding to fragments
v2.10 (2015-11-20)
==================
Enhancements:
- Support for Linux 4.4
Fixes:
- xt_ACCOUNT: call free_page with the right amount of pages
v2.9 (2015-10-12)
=================
Enhancements:
- Support for Linux 4.3
v2.8 (2015-08-19)
=================
Enhancements:
- Support for Linux 4.2
- Enable xt_ECHO for Linux 4.0+
v2.7 (2015-07-06)
=================
Enhancements:
- Support for Linux up to 4.1
v2.6 (2014-09-29)
=================
Enhancements:
- Support for Linux up to 3.17
Fixes:
- xt_pknock: UDP SPA mode erroneously returned an error saying
crypto was unavailable
v2.5 (2014-04-18)
=================
Enhancements:
- Support for Linux up to 3.15
- xt_quota2: introduce support for network namespaces
v2.4 (2014-01-09)
=================
Enhancements:
- Support for Linux up to 3.13
Changes:
- remove unmaintained RAWSNAT/RAWDNAT code
- remove unused parts of compat_xtables that served Linux <3.7
Fixes:
- xt_quota2: --no-change should not alter quota to zero ever
- xt_quota2: --packet should not be set to zero based on skb->len
v2.3 (2013-06-18)
=================
Enhancements:
- Support for Linux 3.10
Fixes:
- xt_DNETMAP, xt_condition, xt_quota2: resolve compile error when
CONFIG_UIDGID_STRICT_TYPE_CHECKS=y
- xt_RAWNAT: ensure correct operation in the presence of IPv4 options
- xt_geoip: do not throw a warnings when country database is size 0
- xt_quota2: print "!" at the correct position during iptables-save
Changes:
- Make print (iptables -L) output the same as save (-S)
v2.2 (2013-03-31)
=================
Enhancements:
- Support for Linux 3.9
- iptaccount: fix entire program being erroneously optimized away on PPC
v2.1 (2012-11-27) v2.1 (2012-11-27)
@@ -233,5 +20,514 @@ Changes:
Enhancements: Enhancements:
- Support for Linux 3.7 - Support for Linux 3.7
If you want to use Xtables-addons with kernels older than 4.15, If you want to use Xtables-addons with kernels older than 3.7,
use the addons 2.x series. use the addons 1.x series, which continues to be maintained for
the time being.
v1.47.1 (2010-10-15)
====================
Enhancements:
- xt_psd gained IPv6 support
Notes for this release:
- Linux 3.7+ is expressly unsupported by this release.
v1.46 (2012-08-23)
==================
Fixes:
- length2, SYSRQ, RAWNAT: preinitialize values for ipv6_find_hdr
- TARPIT: fix memory leak when tarpit_generic() fails
- build: remove extraneous closing bracket in configure.ac
- doc: update xt_SYSRQ.man to reflect that the full IPv6 address is needed
Enhancements:
- Support for Linux 3.6
v1.45 (2012-07-16)
==================
Fixes:
- build: export missing functions
(fixes: "WARNING 'xtnu_ipv6_find_hdr' [xt_TARPIT.ko] not found")
- build: avoid use of unexported functions
(fixes: "WARNING 'ipv6_find_hdr' [xt_TARPIT.ko] not found"
in <= linux-2.6.37)
v1.44 (2012-07-15)
==================
Fixes:
- SYSRQ: fix double target initialization at module load
- build: do not attempt to build IPv6 parts if CONFIG_IP6_NF_IPTABLES=n
Enhancements:
- TARPIT gained IPv6 support
v1.43 (2012-06-30)
==================
Fixes:
- xt_psd: avoid crash due to curr->next corruption
Changes:
- xt_psd: reject invalid match options
Enhancements:
- Support for Linux 3.5
- DNETMAP: new type: static binding
- DNETMAP: new persistent flag option for prefix
- DNETMAP: add write support to procfs interface
v1.42 (2012-04-05)
==================
Fixes:
- compat_xtables: fixed mistranslation of checkentry return values
(affected kernels < 2.6.23)
- xt_SYSRQ: fix compile error when crypto is turned off
Changes:
- ipset6-genl has been dropped from the tree;
the libmnl build-time dependency is thus no longer needed
Enhancements:
- Support for Linux 3.3, 3.4
v1.41 (2012-01-04)
==================
Changes:
- Deactivate build of ipset-genl by default.
I think the original ipset package can now take over, given there are
a handful of kernels (2.6.39 onwards) that do not need patching.
Enhancements:
- Support for Linux 3.2
v1.40 (2011-11-30)
==================
Fixes:
- build: the code actually requires at least iptables 1.4.5 (would yield a
compile error otherwise), make sure configure checks for it; update INSTALL
- xt_ECHO: fix kernel warning about RTAX_HOPLIMIT being used
- xt_ipv4options: fix an infinite loop
Changes:
- xt_ECHO: now calculates UDP checksum
- Linux kernel versions below 2.6.32 are no longer officially
supported, and will not be part of compilation testing.
- update to ipset 6.10
Enhancements:
- xt_ECHO: IPv6 support
v1.39 (2011-09-21)
==================
Fixes:
- libxt_ACCOUNT: fix compilation after missing libxtables_CFLAGS
- build: fix compilation after missing libxtables_CFLAGS in submodules
- build: add missing linux/version.h includes where needed
Changes:
- Remove unsupported ipset 4.x from the Xtables-addons distribution
- ipset: move ipset_errcode from src to library to avoid undefined reference
- update to ipset 6.9.1
v1.38 (2011-08-20)
==================
- xt_CHECKSUM: abort build when the feature is already provided by mainline
- xt_SYSRQ: fix UDPLITE header lookup in IPv6
- xt_TARPIT: fix kernel warning about RTAX_HOPLIMIT being used
- xt_TEE: abort build when the feature is already provided by mainline
- xt_ipp2p: support UDPLITE
- xt_pknock: support UDPLITE
- xt_psd: restore functionality with UDP
- xt_psd: support UDPLITE
- update to ipset 6.8
- support for Linux 3.1
v1.37 (2011-06-25)
==================
Fixes:
- xt_SYSRQ: make IPv6 trigger work again
- xt_SYSRQ: improve security: include host address in digest
- xt_TARPIT: fix a kernel oops in --reset mode
v1.36 (2011-06-03)
==================
Changes:
- xt_geoip: avoid recursive function calls
- xt_TARPIT: unlock for use in all tables
- xt_TARPIT: honeypot and reset modes
- update to ipset 6.7
- support for Linux 3.0
v1.35 (2011-04-11)
==================
Enhancements:
- update to ipset 6.3
* allow "new" as a commad alias to "create"
* resolving IP addresses did not work at listing/saving sets, fixed
* check ICMP and ICMPv6 with the set match and target in the testsuite
* avoid possible syntax clashing at saving hostnames
* fix linking with CONFIG_IPV6=n
* sctp, udplite support for the hash:*port* types
- ipset-genl: handle EAGAIN return value emitted from autoloader
- ipset-genl: resolve nfgenmsg remains and fix spurious protocol abort
v1.34 (2011-04-07)
==================
Fixes:
- xt_pknock: avoid crash when hash TFM could not be allocated
- xt_pknock: avoid inversion of rule lookup that led to warnings
- xt_DNETMAP: add missing module alias
- xt_DNETMAP: support for kernels below 2.6.34
Changes:
- Linux kernel versions below 2.6.29 are no longer officially
supported, and will not be part of compilation testing.
v1.33 (2011-02-02)
==================
Fixes:
- build: restore functionality of `configure --without-kbuild`
- build: fix objdir builds for ipset-5 (xt-a specific)
- build: fix missing inclusion of dependency rules
- xt_LOGMARK: fix detection of untracked connection for Linux >= 2.6.36
Enhancements:
- IPv6 support for xt_geoip
- Update to ipset 5.3
* make IPv4 and IPv6 address handling similar
* show correct line numbers in restore output for parser errors
- Update to ipset 5.4
* fixed ICMP and ICMPv6 handling
* fixed trailing whitespaces and pr_* messages
* fixed module loading at create/header commands
- build: support for Linux up to 2.6.38
- build: preliminary support for iptables 1.4.11
v1.32 (2011-01-04)
==================
Fixes:
- Update to ipset 4.5
* the iptreemap type used wrong gfp flags when deleting entries
- Include ipset 5.2 with genetlink patch (beta)
* no kernel patch needed, but requires Linux >= 2.6.35
and thus needs to be manually enabled in mconfig
v1.31 (2010-11-05)
==================
Fixes:
- build: improve detection of kernel version and error handling
Changes:
- build: automatically derive Xtables module directory, thus
--with-xtlibdir is no longer needed for ./configure in most cases
(If I still see a distro using it, I will scold you for not
reading this changelog.)
Enhancements:
- LOGMARK: print remaining lifetime of cts
- xt_iface: allow matching against incoming/outgoing interface
- libxt_gradm: match packets based on status of grsecurity RBAC
(userspace part only - xt_gradm is in the grsec patch)
v1.30 (2010-010-02)
===================
Fixes:
- update to ipset 4.4
* ipport{,ip,net}hash did not work with mixed "src" and "dst"
destination parameters
Changes:
- deactivate building xt_TEE and xt_CHECKSUM by default, as these have been
merged upstream in Linux 2.6.35 and 2.6.36, respectively.
Distros still wishing to build this need to enable it in their build
script, e.g. perl -i -pe 's{^build_TEE=.*}{build_TEE=m}' mconfig;
v1.29 (2010-09-29)
==================
- compat_xtables: return bool for match_check and target_check in 2.6.23..34
- ipset: enable building of ip_set_ipport{ip,net}hash.ko
- support for Linux 2.6.36
- SYSRQ: resolve compile error with Linux 2.6.36
- TEE: resolve compile error with Linux 2.6.36
- add workaround for broken linux-glibc-devel 2.6.34 userspace headers
("implicit declaration of function 'ALIGN'")
v1.28 (2010-07-24)
==================
- RAWNAT: IPv6 variants erroneously rejected masks /33-/128
- new target xt_CHECKSUM
- xt_length2: add support for IPv6 jumbograms
- xt_geoip: fix possible out-of-bounds access
- import xt_geoip database scripts
v1.27 (2010-05-16)
==================
- further updates for the upcoming 2.6.35 changes
v1.26 (2010-04-30)
==================
- compat_xtables: fix 2.6.34 compile error due to a typo
v1.25 (2010-04-26)
==================
- TEE: do rechecksumming in PREROUTING too
- TEE: decrease TTL on cloned packet
- TEE: set dont-fragment on cloned packets
- TEE: free skb when route lookup failed
- TEE: do not limit use to mangle table
- TEE: do not retain iif and mark on cloned packet
- TEE: new loop detection logic
- TEE: use less expensive pskb_copy
- condition: remove unnecessary RCU protection
v1.24 (2010-03-17)
==================
- build: fix build of userspace modules against old (pre-2.6.25)
headers from linux-glibc-devel (/usr/include/linux)
- ipp2p: updated bittorent command recognition
- SYSRQ: let module load when crypto is unavailable
- SYSRQ: allow processing of UDP-Lite
v1.23 (2010-02-24)
==================
- build: support for Linux 2.6.34
- build: remove unused --with-ksource option
- build: remove unneeded --with-xtables option
- build: fix compilations in RAWNAT, SYSRQ and length2 when CONFIG_IPV6=n
- ipset: update to 4.2
- ECHO: fix compilation w.r.t. skb_dst
v1.22 (2010-01-22)
==================
- compat_xtables: support for 2.6.33 skb_iif changes
- geoip: for FHS compliance use /usr/share/xt_geoip instead of /var/geoip
- ipset: enable build of ip_set_setlist.ko
- quota2: add the --no-change mode
v1.21 (2009-12-09)
==================
- ACCOUNT: avoid collision with arp_tables setsockopt numbers
- doc: fix option mismatch --gw/--gateway in libxt_TEE.man
v1.20 (2009-11-19)
==================
- ipp2p: add more boundary checks
- ipp2p: fix Gnutelle line ending detection
- LOGMARK: remove unknown options from manpage
- ACCOUNT: endianess-correctness
- ipset: install manpage
- ipset: fast forward to v4.1
v1.19 (2009-10-12)
==================
- build: compile fixes for 2.6.31-rt
- build: support for Linux 2.6.32
- ipp2p: try to address underflows
- psd: avoid potential crash when dealing with non-linear skbs
- merge xt_ACCOUNT userspace utilities
- added reworked xt_pknock module
Changes from pknock v0.5:
- pknock: "strict" and "checkip" flags were not displayed in `iptables -L`
- pknock: the GC expire time's lower bound is now the default gc time
(65000 msec) to avoid rendering anti-spoof protection in SPA mode useless
- pknock: avoid crash on memory allocation failure and fix memleak
- pknock: avoid fillup of peer table during DDoS
- pknock: automatic closing of ports
- pknock: make non-zero time mandatory for TCP mode
- pknock: display only pknock mode and state relevant information in procfs
- pknock: check interknock time only for !ST_ALLOWED peers
- pknock: preserve time/autoclose values for rules added in
reverse/arbitrary order
- pknock: add a manpage
v1.18 (2009-09-09)
==================
- build: support for Linux 2.6.31
- ipset: fast forward to v3.2
- quota2: support anonymous counters
- quota2: reduce memory footprint for anonymous counters
- quota2: extend locked period during cleanup (locking bugfix)
- quota2: use strtoull instead of strtoul
- merged xt_ACCOUNT module
- merged xt_psd module
v1.17 (2009-06-16)
==================
- IPMARK: print missing --shift parameter
- build: use readlink -f in extensions/ipset/
- build: support for Linux 2.6.30
v1.16 (2009-05-27)
==================
- RAWNAT: make iptable_rawpost compile with 2.6.30-rc5
- ipset: fast forward to 3.0
v1.15 (2009-04-30)
==================
- build: add kernel version check to configure
- condition: compile fix for 2.6.30-rc
- condition: fix intrapositional negation sign
- fuzzy: fix bogus comparison logic leftover from move to new 1.4.3 API
- ipp2p: fix bogus varargs call
- ipp2p: fix typo in error message
- added "iface" match
- added rawpost table (for use with RAWNAT)
- added RAWSNAT/RAWDNAT targets
v1.14 (2009-03-31)
==================
- fuzzy: need to account for kernel-level modified variables in .userspacesize
- geoip: remove XT_ALIGN from .userspacesize when used with offsetof
- SYSRQ: ignore non-UDP packets
- SYSRQ: do proper L4 header access in IPv6 code
(must not use tcp/udp_hdr in input path)
- add "STEAL" target
- dhcpmac: rename from dhcpaddr
v1.13 (2009-03-23)
==================
- added a reworked ipv4options match
- upgrade to iptables 1.4.3 API
v1.12 (2009-03-07)
==================
- ipset: fix for compilation with 2.6.29-rt
- ipset: fast forward to 2.5.0
- rename xt_portscan to xt_lscan ("low-level scan") because
"portscan" as a word caused confusion
- xt_LOGMARK: print incoming interface index
- revert "TEE: do not use TOS for routing"
- xt_TEE: resolve unknown symbol error with CONFIG_IPV6=n
- xt_TEE: enable routing by iif, nfmark and flowlabel
v1.10 (2009-02-18)
==================
- compat: compile fixes for 2.6.29
- ipset: upgrade to ipset 2.4.9
v1.9 (2009-01-30)
=================
- add the xt_length2 extension
- xt_TEE: remove intrapositional '!' support
- ipset: upgrade to ipset 2.4.7
v1.8 (2009-01-10)
=================
- xt_TEE: IPv6 support
- xt_TEE: do not include TOS value in routing decision
- xt_TEE: fix switch-case inversion for name/IP display
- xt_ipp2p: update manpages and help text
- xt_ipp2p: remove log flooding
- xt_portscan: update manpage about --grscan option caveats
v1.7 (2008-12-25)
=================
- xt_ECHO: compile fix
- avoid the use of "_init" which led to compile errors on some installations
- build: do not unconditionally install ipset
- doc: add manpages for xt_ECHO and xt_TEE
- xt_ipp2p: kazaa detection code cleanup
- xt_ipp2p: fix newline inspection in kazaa detection
- xt_ipp2p: ensure better array bounds checking
- xt_SYSRQ: improve security by hashing password
v1.6 (2008-11-18)
=================
- build: support for Linux 2.6.17
- build: compile fixes for 2.6.18 and 2.6.19
- xt_ECHO: resolve compile errors in xt_ECHO
- xt_ipp2p: parenthesize unaligned-access macros
v1.5.7 (2008-09-01)
===================
- API layer: fix use of uninitialized 'hotdrop' variable
- API layer: move to pskb-based signatures
- xt_SYSRQ: compile fixes for Linux <= 2.6.19
- ipset: adjust semaphore.h include for Linux >= 2.6.27
- build: automatically run `depmod -a` on installation
- add reworked xt_fuzzy module
- add DHCP address match and mangle module
- xt_portscan: IPv6 support
- xt_SYSRQ: add missing module aliases
v1.5.5 (2008-08-03)
===================
- manpage updates for xt_CHAOS, xt_IPMARK; README updates
- build: properly recognize external Kbuild/Mbuild files
- build: remove dependency on CONFIG_NETWORK_SECMARK
- add the xt_SYSRQ target
- add the xt_quota2 extension
- import ipset extension group
v1.5.4.1 (2008-04-26)
=====================
- build: fix compile error for 2.6.18-stable
v1.5.4 (2008-04-09)
===================
- build: support building multiple files with one config option
- API layer: add check for pskb relocation
- doc: generate manpages
- xt_ECHO: catch skb_linearize out-of-memory condition
- xt_LOGMARK: add hook= and ctdir= fields in dump
- xt_LOGMARK: fix comma output in ctstatus= list
- xt_TEE: fix address copying bug
- xt_TEE: make skb writable before attempting checksum update
- add reworked xt_condition match
- add reworked xt_ipp2p match
- add reworked xt_IPMARK target
v1.5.3 (2008-03-22)
===================
- support for Linux 2.6.18
- add xt_ECHO sample target
- add reworked xt_geoip match
v1.5.2 (2008-03-04)
===================
- build: support for GNU make < 3.81 which does not have $(realpath)
v1.5.1 (2008-02-21)
===================
- build: allow user to select what extensions to compile and install
- build: allow external proejcts to be downloaded into the tree
- xt_LOGMARK: dump classify mark, ctstate and ctstatus
- add xt_CHAOS, xt_DELUDE and xt_portscan from Chaostables
v1.5.0 (2008-02-11)
===================
Initial release with:
- extensions: xt_LOGMARK, xt_TARPIT, xt_TEE
- support for Linux >= 2.6.19

View File

@@ -64,7 +64,7 @@ int main(int argc, char *argv[])
struct ipt_ACCOUNT_context ctx; struct ipt_ACCOUNT_context ctx;
struct ipt_acc_handle_ip *entry; struct ipt_acc_handle_ip *entry;
int i; int i;
int optchar; char optchar;
bool doHandleUsage = false, doHandleFree = false, doTableNames = false; bool doHandleUsage = false, doHandleFree = false, doTableNames = false;
bool doFlush = false, doContinue = false, doCSV = false; bool doFlush = false, doContinue = false, doCSV = false;
@@ -200,19 +200,13 @@ int main(int argc, char *argv[])
while ((entry = ipt_ACCOUNT_get_next_entry(&ctx)) != NULL) while ((entry = ipt_ACCOUNT_get_next_entry(&ctx)) != NULL)
{ {
if (doCSV) if (doCSV)
printf("%s;%llu;%llu;%llu;%llu\n", printf("%s;%u;%u;%u;%u\n",
addr_to_dotted(entry->ip), addr_to_dotted(entry->ip), entry->src_packets, entry->src_bytes,
(unsigned long long)entry->src_packets, entry->dst_packets, entry->dst_bytes);
(unsigned long long)entry->src_bytes,
(unsigned long long)entry->dst_packets,
(unsigned long long)entry->dst_bytes);
else else
printf("IP: %s SRC packets: %llu bytes: %llu DST packets: %llu bytes: %llu\n", printf("IP: %s SRC packets: %u bytes: %u DST packets: %u bytes: %u\n",
addr_to_dotted(entry->ip), addr_to_dotted(entry->ip), entry->src_packets, entry->src_bytes,
(unsigned long long)entry->src_packets, entry->dst_packets, entry->dst_bytes);
(unsigned long long)entry->src_bytes,
(unsigned long long)entry->dst_packets,
(unsigned long long)entry->dst_bytes);
} }
if (doContinue) if (doContinue)

View File

@@ -34,8 +34,7 @@ int ipt_ACCOUNT_init(struct ipt_ACCOUNT_context *ctx)
// 4096 bytes default buffer should save us from reallocations // 4096 bytes default buffer should save us from reallocations
// as it fits 200 concurrent active clients // as it fits 200 concurrent active clients
ctx->data = malloc(IPT_ACCOUNT_MIN_BUFSIZE); if ((ctx->data = malloc(IPT_ACCOUNT_MIN_BUFSIZE)) == NULL) {
if (ctx->data == NULL) {
close(ctx->sockfd); close(ctx->sockfd);
ctx->sockfd = -1; ctx->sockfd = -1;
ctx->error_str = "Out of memory for data buffer"; ctx->error_str = "Out of memory for data buffer";

View File

@@ -15,7 +15,6 @@
//#define DEBUG 1 //#define DEBUG 1
#include <linux/module.h> #include <linux/module.h>
#include <linux/version.h> #include <linux/version.h>
#include <net/net_namespace.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <net/icmp.h> #include <net/icmp.h>
@@ -28,12 +27,8 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/string.h> #include <linux/string.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
#include <linux/sockptr.h>
#endif
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <net/netns/generic.h>
#include <net/route.h> #include <net/route.h>
#include "xt_ACCOUNT.h" #include "xt_ACCOUNT.h"
@@ -43,9 +38,6 @@
#error "ipt_ACCOUNT needs at least a PAGE_SIZE of 4096" #error "ipt_ACCOUNT needs at least a PAGE_SIZE of 4096"
#endif #endif
static unsigned int max_tables_limit = 128;
module_param(max_tables_limit, uint, 0);
/** /**
* Internal table structure, generated by check_entry() * Internal table structure, generated by check_entry()
* @name: name of the table * @name: name of the table
@@ -81,13 +73,13 @@ struct ipt_acc_handle {
}; };
/* Used for every IP entry /* Used for every IP entry
Size is 32 bytes so that 256 (class C network) * 16 Size is 16 bytes so that 256 (class C network) * 16
fits in a double kernel (zero) page (two consecutive kernel pages)*/ fits in one kernel (zero) page */
struct ipt_acc_ip { struct ipt_acc_ip {
uint64_t src_packets; uint32_t src_packets;
uint64_t src_bytes; uint32_t src_bytes;
uint64_t dst_packets; uint32_t dst_packets;
uint64_t dst_bytes; uint32_t dst_bytes;
}; };
/* /*
@@ -108,28 +100,25 @@ struct ipt_acc_mask_8 {
struct ipt_acc_mask_16 *mask_16[256]; struct ipt_acc_mask_16 *mask_16[256];
}; };
static int ipt_acc_net_id __read_mostly; static struct ipt_acc_table *ipt_acc_tables;
static struct ipt_acc_handle *ipt_acc_handles;
static void *ipt_acc_tmpbuf;
struct ipt_acc_net {
/* Spinlock used for manipulating the current accounting tables/data */ /* Spinlock used for manipulating the current accounting tables/data */
spinlock_t ipt_acc_lock; static DEFINE_SPINLOCK(ipt_acc_lock);
/* Mutex (semaphore) used for manipulating userspace handles/snapshot data */ /* Mutex (semaphore) used for manipulating userspace handles/snapshot data */
struct semaphore ipt_acc_userspace_mutex; static struct semaphore ipt_acc_userspace_mutex;
struct ipt_acc_table *ipt_acc_tables; /* Allocates a page and clears it */
struct ipt_acc_handle *ipt_acc_handles;
void *ipt_acc_tmpbuf;
};
/* Allocates a page pair and clears it */
static void *ipt_acc_zalloc_page(void) static void *ipt_acc_zalloc_page(void)
{ {
// Don't use get_zeroed_page until it's fixed in the kernel. // Don't use get_zeroed_page until it's fixed in the kernel.
// get_zeroed_page(GFP_ATOMIC) // get_zeroed_page(GFP_ATOMIC)
void *mem = (void *)__get_free_pages(GFP_ATOMIC, 2); void *mem = (void *)__get_free_page(GFP_ATOMIC);
if (mem != NULL) if (mem) {
memset(mem, 0, 2 *PAGE_SIZE); memset (mem, 0, PAGE_SIZE);
}
return mem; return mem;
} }
@@ -142,7 +131,7 @@ static void ipt_acc_data_free(void *data, uint8_t depth)
/* Free for 8 bit network */ /* Free for 8 bit network */
if (depth == 0) { if (depth == 0) {
free_pages((unsigned long)data, 2); free_page((unsigned long)data);
return; return;
} }
@@ -150,10 +139,12 @@ static void ipt_acc_data_free(void *data, uint8_t depth)
if (depth == 1) { if (depth == 1) {
struct ipt_acc_mask_16 *mask_16 = data; struct ipt_acc_mask_16 *mask_16 = data;
unsigned int b; unsigned int b;
for (b = 0; b <= 255; ++b) for (b = 0; b <= 255; b++) {
if (mask_16->mask_24[b]) if (mask_16->mask_24[b]) {
free_pages((unsigned long)mask_16->mask_24[b], 2); free_page((unsigned long)mask_16->mask_24[b]);
free_pages((unsigned long)data, 2); }
}
free_page((unsigned long)data);
return; return;
} }
@@ -165,13 +156,15 @@ static void ipt_acc_data_free(void *data, uint8_t depth)
struct ipt_acc_mask_16 *mask_16 = struct ipt_acc_mask_16 *mask_16 =
((struct ipt_acc_mask_8 *)data)->mask_16[a]; ((struct ipt_acc_mask_8 *)data)->mask_16[a];
for (b = 0; b <= 255; ++b) for (b = 0; b <= 255; b++) {
if (mask_16->mask_24[b]) if (mask_16->mask_24[b]) {
free_pages((unsigned long)mask_16->mask_24[b], 2); free_page((unsigned long)mask_16->mask_24[b]);
free_pages((unsigned long)mask_16, 2);
} }
} }
free_pages((unsigned long)data, 2); free_page((unsigned long)mask_16);
}
}
free_page((unsigned long)data);
return; return;
} }
@@ -182,27 +175,28 @@ static void ipt_acc_data_free(void *data, uint8_t depth)
/* Look for existing table / insert new one. /* Look for existing table / insert new one.
Return internal ID or -1 on error */ Return internal ID or -1 on error */
static int ipt_acc_table_insert(struct ipt_acc_table *ipt_acc_tables, static int ipt_acc_table_insert(const char *name, __be32 ip, __be32 netmask)
const char *name, __be32 ip, __be32 netmask)
{ {
unsigned int i; unsigned int i;
pr_debug("ACCOUNT: ipt_acc_table_insert: %s, %pI4/%pI4\n", pr_debug("ACCOUNT: ipt_acc_table_insert: %s, %u.%u.%u.%u/%u.%u.%u.%u\n",
name, &ip, &netmask); name, NIPQUAD(ip), NIPQUAD(netmask));
/* Look for existing table */ /* Look for existing table */
for (i = 0; i < max_tables_limit; i++) { for (i = 0; i < ACCOUNT_MAX_TABLES; i++) {
if (strncmp(ipt_acc_tables[i].name, name, if (strncmp(ipt_acc_tables[i].name, name,
ACCOUNT_TABLE_NAME_LEN) == 0) { ACCOUNT_TABLE_NAME_LEN) == 0) {
pr_debug("ACCOUNT: Found existing slot: %d - %pI4/%pI4\n", pr_debug("ACCOUNT: Found existing slot: %d - "
i, &ipt_acc_tables[i].ip, &ipt_acc_tables[i].netmask); "%u.%u.%u.%u/%u.%u.%u.%u\n", i,
NIPQUAD(ipt_acc_tables[i].ip),
NIPQUAD(ipt_acc_tables[i].netmask));
if (ipt_acc_tables[i].ip != ip if (ipt_acc_tables[i].ip != ip
|| ipt_acc_tables[i].netmask != netmask) { || ipt_acc_tables[i].netmask != netmask) {
printk("ACCOUNT: Table %s found, but IP/netmask mismatch. " printk("ACCOUNT: Table %s found, but IP/netmask mismatch. "
"IP/netmask found: %pI4/%pI4\n", "IP/netmask found: %u.%u.%u.%u/%u.%u.%u.%u\n",
name, &ipt_acc_tables[i].ip, name, NIPQUAD(ipt_acc_tables[i].ip),
&ipt_acc_tables[i].netmask); NIPQUAD(ipt_acc_tables[i].netmask));
return -1; return -1;
} }
@@ -213,7 +207,7 @@ static int ipt_acc_table_insert(struct ipt_acc_table *ipt_acc_tables,
} }
/* Insert new table */ /* Insert new table */
for (i = 0; i < max_tables_limit; i++) { for (i = 0; i < ACCOUNT_MAX_TABLES; i++) {
/* Found free slot */ /* Found free slot */
if (ipt_acc_tables[i].name[0] == 0) { if (ipt_acc_tables[i].name[0] == 0) {
unsigned int netsize = 0; unsigned int netsize = 0;
@@ -262,21 +256,19 @@ static int ipt_acc_table_insert(struct ipt_acc_table *ipt_acc_tables,
/* No free slot found */ /* No free slot found */
printk("ACCOUNT: No free table slot found (max: %d). " printk("ACCOUNT: No free table slot found (max: %d). "
"Please increase the \"max_tables_limit\" module parameter.\n", max_tables_limit); "Please increase ACCOUNT_MAX_TABLES.\n", ACCOUNT_MAX_TABLES);
return -1; return -1;
} }
static int ipt_acc_checkentry(const struct xt_tgchk_param *par) static int ipt_acc_checkentry(const struct xt_tgchk_param *par)
{ {
struct ipt_acc_net *ian = net_generic(par->net, ipt_acc_net_id);
struct ipt_acc_info *info = par->targinfo; struct ipt_acc_info *info = par->targinfo;
int table_nr; int table_nr;
spin_lock_bh(&ian->ipt_acc_lock); spin_lock_bh(&ipt_acc_lock);
table_nr = ipt_acc_table_insert(ian->ipt_acc_tables, table_nr = ipt_acc_table_insert(info->table_name, info->net_ip,
info->table_name, info->net_ip,
info->net_mask); info->net_mask);
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
if (table_nr == -1) { if (table_nr == -1) {
printk("ACCOUNT: Table insert problem. Aborting\n"); printk("ACCOUNT: Table insert problem. Aborting\n");
@@ -291,11 +283,10 @@ static int ipt_acc_checkentry(const struct xt_tgchk_param *par)
static void ipt_acc_destroy(const struct xt_tgdtor_param *par) static void ipt_acc_destroy(const struct xt_tgdtor_param *par)
{ {
struct ipt_acc_net *ian = net_generic(par->net, ipt_acc_net_id);
unsigned int i; unsigned int i;
struct ipt_acc_info *info = par->targinfo; struct ipt_acc_info *info = par->targinfo;
spin_lock_bh(&ian->ipt_acc_lock); spin_lock_bh(&ipt_acc_lock);
pr_debug("ACCOUNT: ipt_acc_deleteentry called for table: %s (#%d)\n", pr_debug("ACCOUNT: ipt_acc_deleteentry called for table: %s (#%d)\n",
info->table_name, info->table_nr); info->table_name, info->table_nr);
@@ -303,32 +294,32 @@ static void ipt_acc_destroy(const struct xt_tgdtor_param *par)
info->table_nr = -1; /* Set back to original state */ info->table_nr = -1; /* Set back to original state */
/* Look for table */ /* Look for table */
for (i = 0; i < max_tables_limit; i++) { for (i = 0; i < ACCOUNT_MAX_TABLES; i++) {
if (strncmp(ian->ipt_acc_tables[i].name, info->table_name, if (strncmp(ipt_acc_tables[i].name, info->table_name,
ACCOUNT_TABLE_NAME_LEN) == 0) { ACCOUNT_TABLE_NAME_LEN) == 0) {
pr_debug("ACCOUNT: Found table at slot: %d\n", i); pr_debug("ACCOUNT: Found table at slot: %d\n", i);
ian->ipt_acc_tables[i].refcount--; ipt_acc_tables[i].refcount--;
pr_debug("ACCOUNT: Refcount left: %d\n", pr_debug("ACCOUNT: Refcount left: %d\n",
ian->ipt_acc_tables[i].refcount); ipt_acc_tables[i].refcount);
/* Table not needed anymore? */ /* Table not needed anymore? */
if (ian->ipt_acc_tables[i].refcount == 0) { if (ipt_acc_tables[i].refcount == 0) {
pr_debug("ACCOUNT: Destroying table at slot: %d\n", i); pr_debug("ACCOUNT: Destroying table at slot: %d\n", i);
ipt_acc_data_free(ian->ipt_acc_tables[i].data, ipt_acc_data_free(ipt_acc_tables[i].data,
ian->ipt_acc_tables[i].depth); ipt_acc_tables[i].depth);
memset(&ian->ipt_acc_tables[i], 0, memset(&ipt_acc_tables[i], 0,
sizeof(struct ipt_acc_table)); sizeof(struct ipt_acc_table));
} }
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
return; return;
} }
} }
/* Table not found */ /* Table not found */
printk("ACCOUNT: Table %s not found for destroy\n", info->table_name); printk("ACCOUNT: Table %s not found for destroy\n", info->table_name);
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
} }
static void ipt_acc_depth0_insert(struct ipt_acc_mask_24 *mask_24, static void ipt_acc_depth0_insert(struct ipt_acc_mask_24 *mask_24,
@@ -341,8 +332,9 @@ static void ipt_acc_depth0_insert(struct ipt_acc_mask_24 *mask_24,
/* Check if this entry is new */ /* Check if this entry is new */
bool is_src_new_ip = false, is_dst_new_ip = false; bool is_src_new_ip = false, is_dst_new_ip = false;
pr_debug("ACCOUNT: ipt_acc_depth0_insert: %pI4/%pI4 for net %pI4/%pI4," pr_debug("ACCOUNT: ipt_acc_depth0_insert: %u.%u.%u.%u/%u.%u.%u.%u "
" size: %u\n", &src_ip, &dst_ip, &net_ip, &netmask, size); "for net %u.%u.%u.%u/%u.%u.%u.%u, size: %u\n", NIPQUAD(src_ip),
NIPQUAD(dst_ip), NIPQUAD(net_ip), NIPQUAD(netmask), size);
/* Check if src/dst is inside our network. */ /* Check if src/dst is inside our network. */
/* Special: net_ip = 0.0.0.0/0 gets stored as src in slot 0 */ /* Special: net_ip = 0.0.0.0/0 gets stored as src in slot 0 */
@@ -354,8 +346,9 @@ static void ipt_acc_depth0_insert(struct ipt_acc_mask_24 *mask_24,
is_dst = true; is_dst = true;
if (!is_src && !is_dst) { if (!is_src && !is_dst) {
pr_debug("ACCOUNT: Skipping packet %pI4/%pI4 for net %pI4/%pI4\n", pr_debug("ACCOUNT: Skipping packet %u.%u.%u.%u/%u.%u.%u.%u "
&src_ip, &dst_ip, &net_ip, &netmask); "for net %u.%u.%u.%u/%u.%u.%u.%u\n", NIPQUAD(src_ip),
NIPQUAD(dst_ip), NIPQUAD(net_ip), NIPQUAD(netmask));
return; return;
} }
@@ -394,11 +387,11 @@ static void ipt_acc_depth0_insert(struct ipt_acc_mask_24 *mask_24,
} }
} else { } else {
if (is_src_new_ip) { if (is_src_new_ip) {
pr_debug("ACCOUNT: New src_ip: %pI4\n", &src_ip); pr_debug("ACCOUNT: New src_ip: %u.%u.%u.%u\n", NIPQUAD(src_ip));
++*itemcount; ++*itemcount;
} }
if (is_dst_new_ip) { if (is_dst_new_ip) {
pr_debug("ACCOUNT: New dst_ip: %pI4\n", &dst_ip); pr_debug("ACCOUNT: New dst_ip: %u.%u.%u.%u\n", NIPQUAD(dst_ip));
++*itemcount; ++*itemcount;
} }
} }
@@ -481,24 +474,22 @@ static void ipt_acc_depth2_insert(struct ipt_acc_mask_8 *mask_8,
} }
} }
static unsigned int static unsigned int ipt_acc_target(struct sk_buff **pskb, const struct xt_action_param *par)
ipt_acc_target(struct sk_buff *skb, const struct xt_action_param *par)
{ {
struct ipt_acc_net *ian = net_generic(par->state->net, ipt_acc_net_id);
struct ipt_acc_table *ipt_acc_tables = ian->ipt_acc_tables;
const struct ipt_acc_info *info = const struct ipt_acc_info *info =
par->targinfo; par->targinfo;
__be32 src_ip = ip_hdr(skb)->saddr; __be32 src_ip = ip_hdr(*pskb)->saddr;
__be32 dst_ip = ip_hdr(skb)->daddr; __be32 dst_ip = ip_hdr(*pskb)->daddr;
uint32_t size = ntohs(ip_hdr(skb)->tot_len); uint32_t size = ntohs(ip_hdr(*pskb)->tot_len);
spin_lock_bh(&ian->ipt_acc_lock); spin_lock_bh(&ipt_acc_lock);
if (ipt_acc_tables[info->table_nr].name[0] == 0) { if (ipt_acc_tables[info->table_nr].name[0] == 0) {
printk("ACCOUNT: ipt_acc_target: Invalid table id %u. " printk("ACCOUNT: ipt_acc_target: Invalid table id %u. "
"IPs %pI4/%pI4\n", info->table_nr, &src_ip, &dst_ip); "IPs %u.%u.%u.%u/%u.%u.%u.%u\n", info->table_nr,
spin_unlock_bh(&ian->ipt_acc_lock); NIPQUAD(src_ip), NIPQUAD(dst_ip));
spin_unlock_bh(&ipt_acc_lock);
return XT_CONTINUE; return XT_CONTINUE;
} }
@@ -510,7 +501,7 @@ ipt_acc_target(struct sk_buff *skb, const struct xt_action_param *par)
ipt_acc_tables[info->table_nr].ip, ipt_acc_tables[info->table_nr].ip,
ipt_acc_tables[info->table_nr].netmask, ipt_acc_tables[info->table_nr].netmask,
src_ip, dst_ip, size, &ipt_acc_tables[info->table_nr].itemcount); src_ip, dst_ip, size, &ipt_acc_tables[info->table_nr].itemcount);
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
return XT_CONTINUE; return XT_CONTINUE;
} }
@@ -521,7 +512,7 @@ ipt_acc_target(struct sk_buff *skb, const struct xt_action_param *par)
ipt_acc_tables[info->table_nr].ip, ipt_acc_tables[info->table_nr].ip,
ipt_acc_tables[info->table_nr].netmask, ipt_acc_tables[info->table_nr].netmask,
src_ip, dst_ip, size, &ipt_acc_tables[info->table_nr].itemcount); src_ip, dst_ip, size, &ipt_acc_tables[info->table_nr].itemcount);
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
return XT_CONTINUE; return XT_CONTINUE;
} }
@@ -532,19 +523,21 @@ ipt_acc_target(struct sk_buff *skb, const struct xt_action_param *par)
ipt_acc_tables[info->table_nr].ip, ipt_acc_tables[info->table_nr].ip,
ipt_acc_tables[info->table_nr].netmask, ipt_acc_tables[info->table_nr].netmask,
src_ip, dst_ip, size, &ipt_acc_tables[info->table_nr].itemcount); src_ip, dst_ip, size, &ipt_acc_tables[info->table_nr].itemcount);
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
return XT_CONTINUE; return XT_CONTINUE;
} }
printk("ACCOUNT: ipt_acc_target: Unable to process packet. Table id " printk("ACCOUNT: ipt_acc_target: Unable to process packet. "
"%u. IPs %pI4/%pI4\n", info->table_nr, &src_ip, &dst_ip); "Table id %u. IPs %u.%u.%u.%u/%u.%u.%u.%u\n",
spin_unlock_bh(&ian->ipt_acc_lock); info->table_nr, NIPQUAD(src_ip), NIPQUAD(dst_ip));
spin_unlock_bh(&ipt_acc_lock);
return XT_CONTINUE; return XT_CONTINUE;
} }
/* /*
Functions dealing with "handles": Functions dealing with "handles":
Handles are snapshots of an accounting state. Handles are snapshots of a accounting state.
read snapshots are only for debugging the code read snapshots are only for debugging the code
and are very expensive concerning speed/memory and are very expensive concerning speed/memory
@@ -559,7 +552,7 @@ ipt_acc_target(struct sk_buff *skb, const struct xt_action_param *par)
but there could be two or more applications accessing the data but there could be two or more applications accessing the data
at the same time. at the same time.
*/ */
static int ipt_acc_handle_find_slot(struct ipt_acc_handle *ipt_acc_handles) static int ipt_acc_handle_find_slot(void)
{ {
unsigned int i; unsigned int i;
/* Insert new table */ /* Insert new table */
@@ -579,8 +572,7 @@ static int ipt_acc_handle_find_slot(struct ipt_acc_handle *ipt_acc_handles)
return -1; return -1;
} }
static int ipt_acc_handle_free(struct ipt_acc_handle *ipt_acc_handles, static int ipt_acc_handle_free(unsigned int handle)
unsigned int handle)
{ {
if (handle >= ACCOUNT_MAX_HANDLES) { if (handle >= ACCOUNT_MAX_HANDLES) {
printk("ACCOUNT: Invalid handle for ipt_acc_handle_free() specified:" printk("ACCOUNT: Invalid handle for ipt_acc_handle_free() specified:"
@@ -596,19 +588,18 @@ static int ipt_acc_handle_free(struct ipt_acc_handle *ipt_acc_handles,
/* Prepare data for read without flush. Use only for debugging! /* Prepare data for read without flush. Use only for debugging!
Real applications should use read&flush as it's way more efficent */ Real applications should use read&flush as it's way more efficent */
static int ipt_acc_handle_prepare_read(struct ipt_acc_table *ipt_acc_tables, static int ipt_acc_handle_prepare_read(char *tablename,
char *tablename,
struct ipt_acc_handle *dest, uint32_t *count) struct ipt_acc_handle *dest, uint32_t *count)
{ {
int table_nr = -1; int table_nr = -1;
uint8_t depth; uint8_t depth;
for (table_nr = 0; table_nr < max_tables_limit; table_nr++) for (table_nr = 0; table_nr < ACCOUNT_MAX_TABLES; table_nr++)
if (strncmp(ipt_acc_tables[table_nr].name, tablename, if (strncmp(ipt_acc_tables[table_nr].name, tablename,
ACCOUNT_TABLE_NAME_LEN) == 0) ACCOUNT_TABLE_NAME_LEN) == 0)
break; break;
if (table_nr == max_tables_limit) { if (table_nr == ACCOUNT_MAX_TABLES) {
printk("ACCOUNT: ipt_acc_handle_prepare_read(): " printk("ACCOUNT: ipt_acc_handle_prepare_read(): "
"Table %s not found\n", tablename); "Table %s not found\n", tablename);
return -1; return -1;
@@ -620,8 +611,7 @@ static int ipt_acc_handle_prepare_read(struct ipt_acc_table *ipt_acc_tables,
dest->itemcount = ipt_acc_tables[table_nr].itemcount; dest->itemcount = ipt_acc_tables[table_nr].itemcount;
/* allocate "root" table */ /* allocate "root" table */
dest->data = ipt_acc_zalloc_page(); if ((dest->data = ipt_acc_zalloc_page()) == NULL) {
if (dest->data == NULL) {
printk("ACCOUNT: out of memory for root table " printk("ACCOUNT: out of memory for root table "
"in ipt_acc_handle_prepare_read()\n"); "in ipt_acc_handle_prepare_read()\n");
return -1; return -1;
@@ -640,8 +630,7 @@ static int ipt_acc_handle_prepare_read(struct ipt_acc_table *ipt_acc_tables,
unsigned int b; unsigned int b;
for (b = 0; b <= 255; b++) { for (b = 0; b <= 255; b++) {
if (src_16->mask_24[b] == NULL) if (src_16->mask_24[b]) {
continue;
if ((network_16->mask_24[b] = if ((network_16->mask_24[b] =
ipt_acc_zalloc_page()) == NULL) { ipt_acc_zalloc_page()) == NULL) {
printk("ACCOUNT: out of memory during copy of 16 bit " printk("ACCOUNT: out of memory during copy of 16 bit "
@@ -653,6 +642,7 @@ static int ipt_acc_handle_prepare_read(struct ipt_acc_table *ipt_acc_tables,
memcpy(network_16->mask_24[b], src_16->mask_24[b], memcpy(network_16->mask_24[b], src_16->mask_24[b],
sizeof(struct ipt_acc_mask_24)); sizeof(struct ipt_acc_mask_24));
} }
}
} else if (depth == 2) { } else if (depth == 2) {
struct ipt_acc_mask_8 *src_8 = struct ipt_acc_mask_8 *src_8 =
ipt_acc_tables[table_nr].data; ipt_acc_tables[table_nr].data;
@@ -661,8 +651,7 @@ static int ipt_acc_handle_prepare_read(struct ipt_acc_table *ipt_acc_tables,
unsigned int a, b; unsigned int a, b;
for (a = 0; a <= 255; a++) { for (a = 0; a <= 255; a++) {
if (src_8->mask_16[a] == NULL) if (src_8->mask_16[a]) {
continue;
if ((network_8->mask_16[a] = if ((network_8->mask_16[a] =
ipt_acc_zalloc_page()) == NULL) { ipt_acc_zalloc_page()) == NULL) {
printk("ACCOUNT: out of memory during copy of 24 bit network" printk("ACCOUNT: out of memory during copy of 24 bit network"
@@ -678,8 +667,7 @@ static int ipt_acc_handle_prepare_read(struct ipt_acc_table *ipt_acc_tables,
network_16 = network_8->mask_16[a]; network_16 = network_8->mask_16[a];
for (b = 0; b <= 255; b++) { for (b = 0; b <= 255; b++) {
if (src_16->mask_24[b] == NULL) if (src_16->mask_24[b]) {
continue;
if ((network_16->mask_24[b] = if ((network_16->mask_24[b] =
ipt_acc_zalloc_page()) == NULL) { ipt_acc_zalloc_page()) == NULL) {
printk("ACCOUNT: out of memory during copy of 16 bit" printk("ACCOUNT: out of memory during copy of 16 bit"
@@ -693,6 +681,8 @@ static int ipt_acc_handle_prepare_read(struct ipt_acc_table *ipt_acc_tables,
} }
} }
} }
}
}
*count = ipt_acc_tables[table_nr].itemcount; *count = ipt_acc_tables[table_nr].itemcount;
@@ -700,27 +690,25 @@ static int ipt_acc_handle_prepare_read(struct ipt_acc_table *ipt_acc_tables,
} }
/* Prepare data for read and flush it */ /* Prepare data for read and flush it */
static int ipt_acc_handle_prepare_read_flush(struct ipt_acc_table *ipt_acc_tables, static int ipt_acc_handle_prepare_read_flush(char *tablename,
char *tablename,
struct ipt_acc_handle *dest, uint32_t *count) struct ipt_acc_handle *dest, uint32_t *count)
{ {
int table_nr; int table_nr;
void *new_data_page; void *new_data_page;
for (table_nr = 0; table_nr < max_tables_limit; table_nr++) for (table_nr = 0; table_nr < ACCOUNT_MAX_TABLES; table_nr++)
if (strncmp(ipt_acc_tables[table_nr].name, tablename, if (strncmp(ipt_acc_tables[table_nr].name, tablename,
ACCOUNT_TABLE_NAME_LEN) == 0) ACCOUNT_TABLE_NAME_LEN) == 0)
break; break;
if (table_nr == max_tables_limit) { if (table_nr == ACCOUNT_MAX_TABLES) {
printk("ACCOUNT: ipt_acc_handle_prepare_read_flush(): " printk("ACCOUNT: ipt_acc_handle_prepare_read_flush(): "
"Table %s not found\n", tablename); "Table %s not found\n", tablename);
return -1; return -1;
} }
/* Try to allocate memory */ /* Try to allocate memory */
new_data_page = ipt_acc_zalloc_page(); if (!(new_data_page = ipt_acc_zalloc_page())) {
if (new_data_page == NULL) {
printk("ACCOUNT: ipt_acc_handle_prepare_read_flush(): " printk("ACCOUNT: ipt_acc_handle_prepare_read_flush(): "
"Out of memory!\n"); "Out of memory!\n");
return -1; return -1;
@@ -743,8 +731,7 @@ static int ipt_acc_handle_prepare_read_flush(struct ipt_acc_table *ipt_acc_table
/* Copy 8 bit network data into a prepared buffer. /* Copy 8 bit network data into a prepared buffer.
We only copy entries != 0 to increase performance. We only copy entries != 0 to increase performance.
*/ */
static int ipt_acc_handle_copy_data(struct ipt_acc_net *ian, static int ipt_acc_handle_copy_data(void *to_user, unsigned long *to_user_pos,
void *to_user, unsigned long *to_user_pos,
unsigned long *tmpbuf_pos, unsigned long *tmpbuf_pos,
struct ipt_acc_mask_24 *data, struct ipt_acc_mask_24 *data,
uint32_t net_ip, uint32_t net_OR_mask) uint32_t net_ip, uint32_t net_OR_mask)
@@ -754,11 +741,9 @@ static int ipt_acc_handle_copy_data(struct ipt_acc_net *ian,
unsigned int i; unsigned int i;
for (i = 0; i <= 255; i++) { for (i = 0; i <= 255; i++) {
if (data->ip[i].src_packets == 0 && if (data->ip[i].src_packets || data->ip[i].dst_packets) {
data->ip[i].dst_packets == 0)
continue;
handle_ip.ip = net_ip | net_OR_mask | i; handle_ip.ip = net_ip | net_OR_mask | i;
handle_ip.src_packets = data->ip[i].src_packets; handle_ip.src_packets = data->ip[i].src_packets;
handle_ip.src_bytes = data->ip[i].src_bytes; handle_ip.src_bytes = data->ip[i].src_bytes;
handle_ip.dst_packets = data->ip[i].dst_packets; handle_ip.dst_packets = data->ip[i].dst_packets;
@@ -766,15 +751,16 @@ static int ipt_acc_handle_copy_data(struct ipt_acc_net *ian,
/* Temporary buffer full? Flush to userspace */ /* Temporary buffer full? Flush to userspace */
if (*tmpbuf_pos + handle_ip_size >= PAGE_SIZE) { if (*tmpbuf_pos + handle_ip_size >= PAGE_SIZE) {
if (copy_to_user(to_user + *to_user_pos, ian->ipt_acc_tmpbuf, if (copy_to_user(to_user + *to_user_pos, ipt_acc_tmpbuf,
*tmpbuf_pos)) *tmpbuf_pos))
return -EFAULT; return -EFAULT;
*to_user_pos = *to_user_pos + *tmpbuf_pos; *to_user_pos = *to_user_pos + *tmpbuf_pos;
*tmpbuf_pos = 0; *tmpbuf_pos = 0;
} }
memcpy(ian->ipt_acc_tmpbuf + *tmpbuf_pos, &handle_ip, handle_ip_size); memcpy(ipt_acc_tmpbuf + *tmpbuf_pos, &handle_ip, handle_ip_size);
*tmpbuf_pos += handle_ip_size; *tmpbuf_pos += handle_ip_size;
} }
}
return 0; return 0;
} }
@@ -783,8 +769,7 @@ static int ipt_acc_handle_copy_data(struct ipt_acc_net *ian,
We only copy entries != 0 to increase performance. We only copy entries != 0 to increase performance.
Overwrites ipt_acc_tmpbuf. Overwrites ipt_acc_tmpbuf.
*/ */
static int ipt_acc_handle_get_data(struct ipt_acc_net *ian, static int ipt_acc_handle_get_data(uint32_t handle, void *to_user)
uint32_t handle, void *to_user)
{ {
unsigned long to_user_pos = 0, tmpbuf_pos = 0; unsigned long to_user_pos = 0, tmpbuf_pos = 0;
uint32_t net_ip; uint32_t net_ip;
@@ -796,25 +781,25 @@ static int ipt_acc_handle_get_data(struct ipt_acc_net *ian,
return -1; return -1;
} }
if (ian->ipt_acc_handles[handle].data == NULL) { if (ipt_acc_handles[handle].data == NULL) {
printk("ACCOUNT: handle %u is BROKEN: Contains no data\n", handle); printk("ACCOUNT: handle %u is BROKEN: Contains no data\n", handle);
return -1; return -1;
} }
net_ip = ntohl(ian->ipt_acc_handles[handle].ip); net_ip = ntohl(ipt_acc_handles[handle].ip);
depth = ian->ipt_acc_handles[handle].depth; depth = ipt_acc_handles[handle].depth;
/* 8 bit network */ /* 8 bit network */
if (depth == 0) { if (depth == 0) {
struct ipt_acc_mask_24 *network = struct ipt_acc_mask_24 *network =
ian->ipt_acc_handles[handle].data; ipt_acc_handles[handle].data;
if (ipt_acc_handle_copy_data(ian, to_user, &to_user_pos, &tmpbuf_pos, if (ipt_acc_handle_copy_data(to_user, &to_user_pos, &tmpbuf_pos,
network, net_ip, 0)) network, net_ip, 0))
return -1; return -1;
/* Flush remaining data to userspace */ /* Flush remaining data to userspace */
if (tmpbuf_pos) if (tmpbuf_pos)
if (copy_to_user(to_user + to_user_pos, ian->ipt_acc_tmpbuf, tmpbuf_pos)) if (copy_to_user(to_user + to_user_pos, ipt_acc_tmpbuf, tmpbuf_pos))
return -1; return -1;
return 0; return 0;
@@ -823,13 +808,13 @@ static int ipt_acc_handle_get_data(struct ipt_acc_net *ian,
/* 16 bit network */ /* 16 bit network */
if (depth == 1) { if (depth == 1) {
struct ipt_acc_mask_16 *network_16 = struct ipt_acc_mask_16 *network_16 =
ian->ipt_acc_handles[handle].data; ipt_acc_handles[handle].data;
unsigned int b; unsigned int b;
for (b = 0; b <= 255; b++) { for (b = 0; b <= 255; b++) {
if (network_16->mask_24[b]) { if (network_16->mask_24[b]) {
struct ipt_acc_mask_24 *network = struct ipt_acc_mask_24 *network =
network_16->mask_24[b]; network_16->mask_24[b];
if (ipt_acc_handle_copy_data(ian, to_user, &to_user_pos, if (ipt_acc_handle_copy_data(to_user, &to_user_pos,
&tmpbuf_pos, network, net_ip, (b << 8))) &tmpbuf_pos, network, net_ip, (b << 8)))
return -1; return -1;
} }
@@ -837,7 +822,7 @@ static int ipt_acc_handle_get_data(struct ipt_acc_net *ian,
/* Flush remaining data to userspace */ /* Flush remaining data to userspace */
if (tmpbuf_pos) if (tmpbuf_pos)
if (copy_to_user(to_user + to_user_pos, ian->ipt_acc_tmpbuf, tmpbuf_pos)) if (copy_to_user(to_user + to_user_pos, ipt_acc_tmpbuf, tmpbuf_pos))
return -1; return -1;
return 0; return 0;
@@ -846,7 +831,7 @@ static int ipt_acc_handle_get_data(struct ipt_acc_net *ian,
/* 24 bit network */ /* 24 bit network */
if (depth == 2) { if (depth == 2) {
struct ipt_acc_mask_8 *network_8 = struct ipt_acc_mask_8 *network_8 =
ian->ipt_acc_handles[handle].data; ipt_acc_handles[handle].data;
unsigned int a, b; unsigned int a, b;
for (a = 0; a <= 255; a++) { for (a = 0; a <= 255; a++) {
if (network_8->mask_16[a]) { if (network_8->mask_16[a]) {
@@ -856,7 +841,7 @@ static int ipt_acc_handle_get_data(struct ipt_acc_net *ian,
if (network_16->mask_24[b]) { if (network_16->mask_24[b]) {
struct ipt_acc_mask_24 *network = struct ipt_acc_mask_24 *network =
network_16->mask_24[b]; network_16->mask_24[b];
if (ipt_acc_handle_copy_data(ian, to_user, if (ipt_acc_handle_copy_data(to_user,
&to_user_pos, &tmpbuf_pos, &to_user_pos, &tmpbuf_pos,
network, net_ip, (a << 16) | (b << 8))) network, net_ip, (a << 16) | (b << 8)))
return -1; return -1;
@@ -867,7 +852,7 @@ static int ipt_acc_handle_get_data(struct ipt_acc_net *ian,
/* Flush remaining data to userspace */ /* Flush remaining data to userspace */
if (tmpbuf_pos) if (tmpbuf_pos)
if (copy_to_user(to_user + to_user_pos, ian->ipt_acc_tmpbuf, tmpbuf_pos)) if (copy_to_user(to_user + to_user_pos, ipt_acc_tmpbuf, tmpbuf_pos))
return -1; return -1;
return 0; return 0;
@@ -877,15 +862,8 @@ static int ipt_acc_handle_get_data(struct ipt_acc_net *ian,
} }
static int ipt_acc_set_ctl(struct sock *sk, int cmd, static int ipt_acc_set_ctl(struct sock *sk, int cmd,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) void *user, unsigned int len)
void *user,
#else
sockptr_t arg,
#endif
unsigned int len)
{ {
struct net *net = sock_net(sk);
struct ipt_acc_net *ian = net_generic(net, ipt_acc_net_id);
struct ipt_acc_handle_sockopt handle; struct ipt_acc_handle_sockopt handle;
int ret = -EINVAL; int ret = -EINVAL;
@@ -901,27 +879,22 @@ static int ipt_acc_set_ctl(struct sock *sk, int cmd,
break; break;
} }
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) if (copy_from_user(&handle, user, len)) {
if (copy_from_user(&handle, user, len))
#else
if (copy_from_sockptr(&handle, arg, len))
#endif
{
printk("ACCOUNT: ipt_acc_set_ctl: copy_from_user failed for " printk("ACCOUNT: ipt_acc_set_ctl: copy_from_user failed for "
"IPT_SO_SET_HANDLE_FREE\n"); "IPT_SO_SET_HANDLE_FREE\n");
break; break;
} }
down(&ian->ipt_acc_userspace_mutex); down(&ipt_acc_userspace_mutex);
ret = ipt_acc_handle_free(ian->ipt_acc_handles, handle.handle_nr); ret = ipt_acc_handle_free(handle.handle_nr);
up(&ian->ipt_acc_userspace_mutex); up(&ipt_acc_userspace_mutex);
break; break;
case IPT_SO_SET_ACCOUNT_HANDLE_FREE_ALL: { case IPT_SO_SET_ACCOUNT_HANDLE_FREE_ALL: {
unsigned int i; unsigned int i;
down(&ian->ipt_acc_userspace_mutex); down(&ipt_acc_userspace_mutex);
for (i = 0; i < ACCOUNT_MAX_HANDLES; i++) for (i = 0; i < ACCOUNT_MAX_HANDLES; i++)
ipt_acc_handle_free(ian->ipt_acc_handles, i); ipt_acc_handle_free(i);
up(&ian->ipt_acc_userspace_mutex); up(&ipt_acc_userspace_mutex);
ret = 0; ret = 0;
break; break;
} }
@@ -934,8 +907,6 @@ static int ipt_acc_set_ctl(struct sock *sk, int cmd,
static int ipt_acc_get_ctl(struct sock *sk, int cmd, void *user, int *len) static int ipt_acc_get_ctl(struct sock *sk, int cmd, void *user, int *len)
{ {
struct net *net = sock_net(sk);
struct ipt_acc_net *ian = net_generic(net, ipt_acc_net_id);
struct ipt_acc_handle_sockopt handle; struct ipt_acc_handle_sockopt handle;
int ret = -EINVAL; int ret = -EINVAL;
@@ -960,29 +931,28 @@ static int ipt_acc_get_ctl(struct sock *sk, int cmd, void *user, int *len)
break; break;
} }
spin_lock_bh(&ian->ipt_acc_lock); spin_lock_bh(&ipt_acc_lock);
if (cmd == IPT_SO_GET_ACCOUNT_PREPARE_READ_FLUSH) if (cmd == IPT_SO_GET_ACCOUNT_PREPARE_READ_FLUSH)
ret = ipt_acc_handle_prepare_read_flush( ret = ipt_acc_handle_prepare_read_flush(
ian->ipt_acc_tables, handle.name, &dest, &handle.itemcount); handle.name, &dest, &handle.itemcount);
else else
ret = ipt_acc_handle_prepare_read( ret = ipt_acc_handle_prepare_read(
ian->ipt_acc_tables, handle.name, &dest, &handle.itemcount); handle.name, &dest, &handle.itemcount);
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
// Error occured during prepare_read? // Error occured during prepare_read?
if (ret == -1) if (ret == -1)
return -EINVAL; return -EINVAL;
/* Allocate a userspace handle */ /* Allocate a userspace handle */
down(&ian->ipt_acc_userspace_mutex); down(&ipt_acc_userspace_mutex);
handle.handle_nr = ipt_acc_handle_find_slot(ian->ipt_acc_handles); if ((handle.handle_nr = ipt_acc_handle_find_slot()) == -1) {
if (handle.handle_nr == -1) {
ipt_acc_data_free(dest.data, dest.depth); ipt_acc_data_free(dest.data, dest.depth);
up(&ian->ipt_acc_userspace_mutex); up(&ipt_acc_userspace_mutex);
return -EINVAL; return -EINVAL;
} }
memcpy(&ian->ipt_acc_handles[handle.handle_nr], &dest, memcpy(&ipt_acc_handles[handle.handle_nr], &dest,
sizeof(struct ipt_acc_handle)); sizeof(struct ipt_acc_handle));
up(&ian->ipt_acc_userspace_mutex); up(&ipt_acc_userspace_mutex);
if (copy_to_user(user, &handle, if (copy_to_user(user, &handle,
sizeof(struct ipt_acc_handle_sockopt))) { sizeof(struct ipt_acc_handle_sockopt))) {
@@ -1011,19 +981,19 @@ static int ipt_acc_get_ctl(struct sock *sk, int cmd, void *user, int *len)
break; break;
} }
if (*len < ian->ipt_acc_handles[handle.handle_nr].itemcount if (*len < ipt_acc_handles[handle.handle_nr].itemcount
* sizeof(struct ipt_acc_handle_ip)) { * sizeof(struct ipt_acc_handle_ip)) {
printk("ACCOUNT: ipt_acc_get_ctl: not enough space (%u < %zu)" printk("ACCOUNT: ipt_acc_get_ctl: not enough space (%u < %zu)"
" to store data from IPT_SO_GET_ACCOUNT_GET_DATA\n", " to store data from IPT_SO_GET_ACCOUNT_GET_DATA\n",
*len, ian->ipt_acc_handles[handle.handle_nr].itemcount *len, ipt_acc_handles[handle.handle_nr].itemcount
* sizeof(struct ipt_acc_handle_ip)); * sizeof(struct ipt_acc_handle_ip));
ret = -ENOMEM; ret = -ENOMEM;
break; break;
} }
down(&ian->ipt_acc_userspace_mutex); down(&ipt_acc_userspace_mutex);
ret = ipt_acc_handle_get_data(ian, handle.handle_nr, user); ret = ipt_acc_handle_get_data(handle.handle_nr, user);
up(&ian->ipt_acc_userspace_mutex); up(&ipt_acc_userspace_mutex);
if (ret) { if (ret) {
printk("ACCOUNT: ipt_acc_get_ctl: ipt_acc_handle_get_data" printk("ACCOUNT: ipt_acc_get_ctl: ipt_acc_handle_get_data"
" failed for handle %u\n", handle.handle_nr); " failed for handle %u\n", handle.handle_nr);
@@ -1043,11 +1013,11 @@ static int ipt_acc_get_ctl(struct sock *sk, int cmd, void *user, int *len)
/* Find out how many handles are in use */ /* Find out how many handles are in use */
handle.itemcount = 0; handle.itemcount = 0;
down(&ian->ipt_acc_userspace_mutex); down(&ipt_acc_userspace_mutex);
for (i = 0; i < ACCOUNT_MAX_HANDLES; i++) for (i = 0; i < ACCOUNT_MAX_HANDLES; i++)
if (ian->ipt_acc_handles[i].data) if (ipt_acc_handles[i].data)
handle.itemcount++; handle.itemcount++;
up(&ian->ipt_acc_userspace_mutex); up(&ipt_acc_userspace_mutex);
if (copy_to_user(user, &handle, if (copy_to_user(user, &handle,
sizeof(struct ipt_acc_handle_sockopt))) { sizeof(struct ipt_acc_handle_sockopt))) {
@@ -1061,38 +1031,38 @@ static int ipt_acc_get_ctl(struct sock *sk, int cmd, void *user, int *len)
uint32_t size = 0, i, name_len; uint32_t size = 0, i, name_len;
char *tnames; char *tnames;
spin_lock_bh(&ian->ipt_acc_lock); spin_lock_bh(&ipt_acc_lock);
/* Determine size of table names */ /* Determine size of table names */
for (i = 0; i < max_tables_limit; i++) { for (i = 0; i < ACCOUNT_MAX_TABLES; i++) {
if (ian->ipt_acc_tables[i].name[0] != 0) if (ipt_acc_tables[i].name[0] != 0)
size += strlen(ian->ipt_acc_tables[i].name) + 1; size += strlen(ipt_acc_tables[i].name) + 1;
} }
size += 1; /* Terminating NULL character */ size += 1; /* Terminating NULL character */
if (*len < size || size > PAGE_SIZE) { if (*len < size || size > PAGE_SIZE) {
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
printk("ACCOUNT: ipt_acc_get_ctl: not enough space (%u < %u < %lu)" printk("ACCOUNT: ipt_acc_get_ctl: not enough space (%u < %u < %lu)"
" to store table names\n", *len, size, PAGE_SIZE); " to store table names\n", *len, size, PAGE_SIZE);
ret = -ENOMEM; ret = -ENOMEM;
break; break;
} }
/* Copy table names to userspace */ /* Copy table names to userspace */
tnames = ian->ipt_acc_tmpbuf; tnames = ipt_acc_tmpbuf;
for (i = 0; i < max_tables_limit; i++) { for (i = 0; i < ACCOUNT_MAX_TABLES; i++) {
if (ian->ipt_acc_tables[i].name[0] != 0) { if (ipt_acc_tables[i].name[0] != 0) {
name_len = strlen(ian->ipt_acc_tables[i].name) + 1; name_len = strlen(ipt_acc_tables[i].name) + 1;
memcpy(tnames, ian->ipt_acc_tables[i].name, name_len); memcpy(tnames, ipt_acc_tables[i].name, name_len);
tnames += name_len; tnames += name_len;
} }
} }
spin_unlock_bh(&ian->ipt_acc_lock); spin_unlock_bh(&ipt_acc_lock);
/* Terminating NULL character */ /* Terminating NULL character */
*tnames = 0; *tnames = 0;
/* Transfer to userspace */ /* Transfer to userspace */
if (copy_to_user(user, ian->ipt_acc_tmpbuf, size)) if (copy_to_user(user, ipt_acc_tmpbuf, size))
return -EFAULT; return -EFAULT;
ret = 0; ret = 0;
@@ -1105,59 +1075,6 @@ static int ipt_acc_get_ctl(struct sock *sk, int cmd, void *user, int *len)
return ret; return ret;
} }
static int __net_init ipt_acc_net_init(struct net *net)
{
struct ipt_acc_net *ian = net_generic(net, ipt_acc_net_id);
memset(ian, 0, sizeof(*ian));
sema_init(&ian->ipt_acc_userspace_mutex, 1);
ian->ipt_acc_tables = kcalloc(max_tables_limit,
sizeof(struct ipt_acc_table), GFP_KERNEL);
if (ian->ipt_acc_tables == NULL) {
printk("ACCOUNT: Out of memory allocating account_tables structure");
goto error_cleanup;
}
ian->ipt_acc_handles = kcalloc(ACCOUNT_MAX_HANDLES,
sizeof(struct ipt_acc_handle), GFP_KERNEL);
if (ian->ipt_acc_handles == NULL) {
printk("ACCOUNT: Out of memory allocating account_handles structure");
goto error_cleanup;
}
/* Allocate one page as temporary storage */
ian->ipt_acc_tmpbuf = (void *)__get_free_pages(GFP_KERNEL, 2);
if (ian->ipt_acc_tmpbuf == NULL) {
printk("ACCOUNT: Out of memory for temporary buffer page\n");
goto error_cleanup;
}
return 0;
error_cleanup:
kfree(ian->ipt_acc_tables);
kfree(ian->ipt_acc_handles);
free_pages((unsigned long)ian->ipt_acc_tmpbuf, 2);
return -ENOMEM;
}
static void __net_exit ipt_acc_net_exit(struct net *net)
{
struct ipt_acc_net *ian = net_generic(net, ipt_acc_net_id);
kfree(ian->ipt_acc_tables);
kfree(ian->ipt_acc_handles);
free_pages((unsigned long)ian->ipt_acc_tmpbuf, 2);
}
static struct pernet_operations ipt_acc_net_ops = {
.init = ipt_acc_net_init,
.exit = ipt_acc_net_exit,
.id = &ipt_acc_net_id,
.size = sizeof(struct ipt_acc_net),
};
static struct xt_target xt_acc_reg __read_mostly = { static struct xt_target xt_acc_reg __read_mostly = {
.name = "ACCOUNT", .name = "ACCOUNT",
.revision = 1, .revision = 1,
@@ -1181,41 +1098,63 @@ static struct nf_sockopt_ops ipt_acc_sockopts = {
static int __init account_tg_init(void) static int __init account_tg_init(void)
{ {
int ret; sema_init(&ipt_acc_userspace_mutex, 1);
ret = register_pernet_subsys(&ipt_acc_net_ops); if ((ipt_acc_tables =
if (ret < 0) { kmalloc(ACCOUNT_MAX_TABLES *
pr_err("ACCOUNT: cannot register per net operations.\n"); sizeof(struct ipt_acc_table), GFP_KERNEL)) == NULL) {
goto error_out; printk("ACCOUNT: Out of memory allocating account_tables structure");
goto error_cleanup;
}
memset(ipt_acc_tables, 0,
ACCOUNT_MAX_TABLES * sizeof(struct ipt_acc_table));
if ((ipt_acc_handles =
kmalloc(ACCOUNT_MAX_HANDLES *
sizeof(struct ipt_acc_handle), GFP_KERNEL)) == NULL) {
printk("ACCOUNT: Out of memory allocating account_handles structure");
goto error_cleanup;
}
memset(ipt_acc_handles, 0,
ACCOUNT_MAX_HANDLES * sizeof(struct ipt_acc_handle));
/* Allocate one page as temporary storage */
if ((ipt_acc_tmpbuf = (void*)__get_free_page(GFP_KERNEL)) == NULL) {
printk("ACCOUNT: Out of memory for temporary buffer page\n");
goto error_cleanup;
} }
/* Register setsockopt */ /* Register setsockopt */
ret = nf_register_sockopt(&ipt_acc_sockopts); if (nf_register_sockopt(&ipt_acc_sockopts) < 0) {
if (ret < 0) { printk("ACCOUNT: Can't register sockopts. Aborting\n");
pr_err("ACCOUNT: cannot register sockopts.\n"); goto error_cleanup;
goto unreg_pernet;
} }
ret = xt_register_target(&xt_acc_reg); if (xt_register_target(&xt_acc_reg))
if (ret < 0) { goto error_cleanup;
pr_err("ACCOUNT: cannot register sockopts.\n");
goto unreg_sockopt;
}
return 0; return 0;
unreg_sockopt: error_cleanup:
nf_unregister_sockopt(&ipt_acc_sockopts); if (ipt_acc_tables)
unreg_pernet: kfree(ipt_acc_tables);
unregister_pernet_subsys(&ipt_acc_net_ops); if (ipt_acc_handles)
error_out: kfree(ipt_acc_handles);
return ret; if (ipt_acc_tmpbuf)
free_page((unsigned long)ipt_acc_tmpbuf);
return -EINVAL;
} }
static void __exit account_tg_exit(void) static void __exit account_tg_exit(void)
{ {
xt_unregister_target(&xt_acc_reg); xt_unregister_target(&xt_acc_reg);
nf_unregister_sockopt(&ipt_acc_sockopts); nf_unregister_sockopt(&ipt_acc_sockopts);
unregister_pernet_subsys(&ipt_acc_net_ops);
kfree(ipt_acc_tables);
kfree(ipt_acc_handles);
free_page((unsigned long)ipt_acc_tmpbuf);
} }
module_init(account_tg_init); module_init(account_tg_init);

View File

@@ -34,6 +34,7 @@
#define IPT_SO_GET_ACCOUNT_GET_TABLE_NAMES (SO_ACCOUNT_BASE_CTL + 8) #define IPT_SO_GET_ACCOUNT_GET_TABLE_NAMES (SO_ACCOUNT_BASE_CTL + 8)
#define IPT_SO_GET_ACCOUNT_MAX IPT_SO_GET_ACCOUNT_GET_TABLE_NAMES #define IPT_SO_GET_ACCOUNT_MAX IPT_SO_GET_ACCOUNT_GET_TABLE_NAMES
#define ACCOUNT_MAX_TABLES 128
#define ACCOUNT_TABLE_NAME_LEN 32 #define ACCOUNT_TABLE_NAME_LEN 32
#define ACCOUNT_MAX_HANDLES 10 #define ACCOUNT_MAX_HANDLES 10
@@ -58,11 +59,11 @@ struct ipt_acc_handle_sockopt {
Used for every IP when returning data Used for every IP when returning data
*/ */
struct ipt_acc_handle_ip { struct ipt_acc_handle_ip {
__be32 ip, __dummy; __be32 ip;
uint64_t src_packets; uint32_t src_packets;
uint64_t src_bytes; uint32_t src_bytes;
uint64_t dst_packets; uint32_t dst_packets;
uint64_t dst_bytes; uint32_t dst_bytes;
}; };
#endif /* _IPT_ACCOUNT_H */ #endif /* _IPT_ACCOUNT_H */

View File

@@ -10,11 +10,17 @@ obj-${build_CHAOS} += xt_CHAOS.o
obj-${build_DELUDE} += xt_DELUDE.o obj-${build_DELUDE} += xt_DELUDE.o
obj-${build_DHCPMAC} += xt_DHCPMAC.o obj-${build_DHCPMAC} += xt_DHCPMAC.o
obj-${build_DNETMAP} += xt_DNETMAP.o obj-${build_DNETMAP} += xt_DNETMAP.o
ifeq (${VERSION},3)
obj-${build_ECHO} += xt_ECHO.o obj-${build_ECHO} += xt_ECHO.o
endif
obj-${build_IPMARK} += xt_IPMARK.o obj-${build_IPMARK} += xt_IPMARK.o
obj-${build_LOGMARK} += xt_LOGMARK.o obj-${build_LOGMARK} += xt_LOGMARK.o
obj-${build_PROTO} += xt_PROTO.o obj-${build_RAWNAT} += xt_RAWNAT.o iptable_rawpost.o
ifneq (${CONFIG_IP6_NF_IPTABLES},)
obj-${build_RAWNAT} += ip6table_rawpost.o
endif
obj-${build_SYSRQ} += xt_SYSRQ.o obj-${build_SYSRQ} += xt_SYSRQ.o
obj-${build_STEAL} += xt_STEAL.o
obj-${build_TARPIT} += xt_TARPIT.o obj-${build_TARPIT} += xt_TARPIT.o
obj-${build_condition} += xt_condition.o obj-${build_condition} += xt_condition.o
obj-${build_fuzzy} += xt_fuzzy.o obj-${build_fuzzy} += xt_fuzzy.o
@@ -28,5 +34,7 @@ obj-${build_pknock} += pknock/
obj-${build_psd} += xt_psd.o obj-${build_psd} += xt_psd.o
obj-${build_quota2} += xt_quota2.o obj-${build_quota2} += xt_quota2.o
obj-${build_ipaddr} += xt_ipaddr.o
-include ${M}/*.Kbuild -include ${M}/*.Kbuild
-include ${M}/Kbuild.* -include ${M}/Kbuild.*

View File

@@ -8,13 +8,15 @@ obj-${build_DNETMAP} += libxt_DNETMAP.so
obj-${build_ECHO} += libxt_ECHO.so obj-${build_ECHO} += libxt_ECHO.so
obj-${build_IPMARK} += libxt_IPMARK.so obj-${build_IPMARK} += libxt_IPMARK.so
obj-${build_LOGMARK} += libxt_LOGMARK.so obj-${build_LOGMARK} += libxt_LOGMARK.so
obj-${build_PROTO} += libxt_PROTO.so obj-${build_RAWNAT} += libxt_RAWDNAT.so libxt_RAWSNAT.so
obj-${build_STEAL} += libxt_STEAL.so
obj-${build_SYSRQ} += libxt_SYSRQ.so obj-${build_SYSRQ} += libxt_SYSRQ.so
obj-${build_TARPIT} += libxt_TARPIT.so obj-${build_TARPIT} += libxt_TARPIT.so
obj-${build_condition} += libxt_condition.so obj-${build_condition} += libxt_condition.so
obj-${build_fuzzy} += libxt_fuzzy.so obj-${build_fuzzy} += libxt_fuzzy.so
obj-${build_geoip} += libxt_geoip.so obj-${build_geoip} += libxt_geoip.so
obj-${build_iface} += libxt_iface.so obj-${build_iface} += libxt_iface.so
obj-${build_ipaddr} += libxt_ipaddr.so
obj-${build_ipp2p} += libxt_ipp2p.so obj-${build_ipp2p} += libxt_ipp2p.so
obj-${build_ipv4options} += libxt_ipv4options.so obj-${build_ipv4options} += libxt_ipv4options.so
obj-${build_length2} += libxt_length2.so obj-${build_length2} += libxt_length2.so

View File

@@ -0,0 +1,14 @@
#ifndef _COMPAT_NFINETADDR_H
#define _COMPAT_NFINETADDR_H 1
#include <linux/in.h>
#include <linux/in6.h>
union nf_inet_addr {
__be32 ip;
__be32 ip6[4];
struct in_addr in;
struct in6_addr in6;
};
#endif /* _COMPAT_NFINETADDR_H */

View File

@@ -0,0 +1,6 @@
#ifndef XTA_COMPAT_RAWPOST_H
#define XTA_COMPAT_RAWPOST_H 1
typedef struct sk_buff sk_buff_t;
#endif /* XTA_COMPAT_RAWPOST_H */

View File

@@ -28,6 +28,118 @@
# define WITH_IPV6 1 # define WITH_IPV6 1
#endif #endif
static unsigned int
xtnu_target_run(struct sk_buff *skb, const struct xt_action_param *par)
{
struct xtnu_target *nt = xtcompat_nutarget(par->target);
return nt->target(&skb, par);
}
int xtnu_register_target(struct xtnu_target *nt)
{
struct xt_target *ct;
char *tmp;
int ret;
ct = kzalloc(sizeof(struct xt_target), GFP_KERNEL);
if (ct == NULL)
return -ENOMEM;
tmp = (char *)ct->name;
memcpy(tmp, nt->name, sizeof(nt->name));
tmp = (char *)(ct->name + sizeof(ct->name) - sizeof(void *));
*(tmp-1) = '\0';
memcpy(tmp, &nt, sizeof(void *));
ct->revision = nt->revision;
ct->family = nt->family;
ct->table = (char *)nt->table;
ct->hooks = nt->hooks;
ct->proto = nt->proto;
ct->target = xtnu_target_run;
ct->checkentry = nt->checkentry;
ct->destroy = nt->destroy;
ct->targetsize = nt->targetsize;
ct->me = nt->me;
nt->__compat_target = ct;
ret = xt_register_target(ct);
if (ret != 0)
kfree(ct);
return ret;
}
EXPORT_SYMBOL_GPL(xtnu_register_target);
int xtnu_register_targets(struct xtnu_target *nt, unsigned int num)
{
unsigned int i;
int ret;
for (i = 0; i < num; ++i) {
ret = xtnu_register_target(&nt[i]);
if (ret < 0) {
if (i > 0)
xtnu_unregister_targets(nt, i);
return ret;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(xtnu_register_targets);
void xtnu_unregister_target(struct xtnu_target *nt)
{
xt_unregister_target(nt->__compat_target);
kfree(nt->__compat_target);
}
EXPORT_SYMBOL_GPL(xtnu_unregister_target);
void xtnu_unregister_targets(struct xtnu_target *nt, unsigned int num)
{
unsigned int i;
for (i = 0; i < num; ++i)
xtnu_unregister_target(&nt[i]);
}
EXPORT_SYMBOL_GPL(xtnu_unregister_targets);
struct xt_match *xtnu_request_find_match(unsigned int af, const char *name,
uint8_t revision)
{
static const char *const xt_prefix[] = {
[AF_UNSPEC] = "x",
[AF_INET] = "ip",
[AF_INET6] = "ip6",
#ifdef AF_ARP
[AF_ARP] = "arp",
#elif defined(NF_ARP) && NF_ARP != AF_UNSPEC
[NF_ARP] = "arp",
#endif
};
struct xt_match *match;
match = try_then_request_module(xt_find_match(af, name, revision),
"%st_%s", xt_prefix[af], name);
if (IS_ERR(match) || match == NULL)
return NULL;
return match;
}
EXPORT_SYMBOL_GPL(xtnu_request_find_match);
int xtnu_ip_route_me_harder(struct sk_buff **pskb, unsigned int addr_type)
{
return ip_route_me_harder(*pskb, addr_type);
}
EXPORT_SYMBOL_GPL(xtnu_ip_route_me_harder);
int xtnu_skb_make_writable(struct sk_buff **pskb, unsigned int len)
{
return skb_make_writable(*pskb, len);
}
EXPORT_SYMBOL_GPL(xtnu_skb_make_writable);
void *HX_memmem(const void *space, size_t spacesize, void *HX_memmem(const void *space, size_t spacesize,
const void *point, size_t pointsize) const void *point, size_t pointsize)
{ {

View File

@@ -8,8 +8,8 @@
#define DEBUGP Use__pr_debug__instead #define DEBUGP Use__pr_debug__instead
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
# warning Kernels below 4.15 not supported. # warning Kernels below 3.7 not supported.
#endif #endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
@@ -21,31 +21,40 @@
# warning You need CONFIG_NF_CONNTRACK. # warning You need CONFIG_NF_CONNTRACK.
#endif #endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) || \ #define ipt_unregister_table(tbl) ipt_unregister_table(&init_net, (tbl))
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 9) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) || \ #define ip6t_unregister_table(tbl) ip6t_unregister_table(&init_net, (tbl))
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 78) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) || \
LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 158) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0) #define rt_dst(rt) (&(rt)->dst)
#else
# define ip_route_me_harder(xnet, xsk, xskb, xaddrtype) ip_route_me_harder((xnet), (xskb), (xaddrtype)) #if !defined(NIP6) && !defined(NIP6_FMT)
# define ip6_route_me_harder(xnet, xsk, xskb) ip6_route_me_harder((xnet), (xskb)) # define NIP6(addr) \
ntohs((addr).s6_addr16[0]), \
ntohs((addr).s6_addr16[1]), \
ntohs((addr).s6_addr16[2]), \
ntohs((addr).s6_addr16[3]), \
ntohs((addr).s6_addr16[4]), \
ntohs((addr).s6_addr16[5]), \
ntohs((addr).s6_addr16[6]), \
ntohs((addr).s6_addr16[7])
# define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
#endif
#if !defined(NIPQUAD) && !defined(NIPQUAD_FMT)
# define NIPQUAD(addr) \
((const unsigned char *)&addr)[0], \
((const unsigned char *)&addr)[1], \
((const unsigned char *)&addr)[2], \
((const unsigned char *)&addr)[3]
# define NIPQUAD_FMT "%u.%u.%u.%u"
#endif #endif
static inline struct net *par_net(const struct xt_action_param *par) #define ip_route_me_harder xtnu_ip_route_me_harder
{ #define skb_make_writable xtnu_skb_make_writable
return par->state->net; #define xt_target xtnu_target
} #define xt_register_target xtnu_register_target
#define xt_unregister_target xtnu_unregister_target
#define xt_register_targets xtnu_register_targets
#define xt_unregister_targets xtnu_unregister_targets
#ifndef NF_CT_ASSERT #define xt_request_find_match xtnu_request_find_match
# define NF_CT_ASSERT(x) WARN_ON(!(x))
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
# define proc_ops file_operations
# define proc_open open
# define proc_read read
# define proc_write write
# define proc_lseek llseek
# define proc_release release
#endif
#endif /* _XTABLES_COMPAT_H */ #endif /* _XTABLES_COMPAT_H */

View File

@@ -1,9 +1,14 @@
#ifndef _COMPAT_XTNU_H #ifndef _COMPAT_XTNU_H
#define _COMPAT_XTNU_H 1 #define _COMPAT_XTNU_H 1
#include <linux/list.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <linux/spinlock.h>
struct flowi;
struct module; struct module;
struct net_device;
struct rtable;
struct sk_buff; struct sk_buff;
struct xtnu_match { struct xtnu_match {
@@ -53,7 +58,11 @@ static inline struct xtnu_target *xtcompat_nutarget(const struct xt_target *t)
return q; return q;
} }
extern int xtnu_ip_local_out(struct sk_buff *);
extern int xtnu_ip_route_me_harder(struct sk_buff **, unsigned int);
extern int xtnu_skb_make_writable(struct sk_buff **, unsigned int);
extern int xtnu_register_match(struct xtnu_match *); extern int xtnu_register_match(struct xtnu_match *);
extern int xtnu_ip_route_output_key(void *, struct rtable **, struct flowi *);
extern void xtnu_unregister_match(struct xtnu_match *); extern void xtnu_unregister_match(struct xtnu_match *);
extern int xtnu_register_matches(struct xtnu_match *, unsigned int); extern int xtnu_register_matches(struct xtnu_match *, unsigned int);
extern void xtnu_unregister_matches(struct xtnu_match *, unsigned int); extern void xtnu_unregister_matches(struct xtnu_match *, unsigned int);
@@ -61,6 +70,14 @@ extern int xtnu_register_target(struct xtnu_target *);
extern void xtnu_unregister_target(struct xtnu_target *); extern void xtnu_unregister_target(struct xtnu_target *);
extern int xtnu_register_targets(struct xtnu_target *, unsigned int); extern int xtnu_register_targets(struct xtnu_target *, unsigned int);
extern void xtnu_unregister_targets(struct xtnu_target *, unsigned int); extern void xtnu_unregister_targets(struct xtnu_target *, unsigned int);
extern struct xt_match *xtnu_request_find_match(unsigned int,
const char *, uint8_t);
extern void xtnu_proto_csum_replace4(__u16 __bitwise *, struct sk_buff *,
__be32, __be32, bool);
extern int xtnu_ipv6_skip_exthdr(const struct sk_buff *, int,
uint8_t *, __be16 *);
extern int xtnu_ipv6_find_hdr(const struct sk_buff *, unsigned int *,
int, unsigned short *, int *);
extern void *HX_memmem(const void *, size_t, const void *, size_t); extern void *HX_memmem(const void *, size_t, const void *, size_t);

View File

@@ -0,0 +1,93 @@
/*
* rawpost table for ip6_tables
* written by Jan Engelhardt, 2008 - 2009
* placed in the Public Domain
*/
#include <linux/module.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/ip.h>
#include "compat_xtables.h"
#include "compat_rawpost.h"
enum {
RAWPOST_VALID_HOOKS = 1 << NF_INET_POST_ROUTING,
};
static struct {
struct ip6t_replace repl;
struct ip6t_standard entries[1];
struct ip6t_error term;
} rawpost6_initial __initdata = {
.repl = {
.name = "rawpost",
.valid_hooks = RAWPOST_VALID_HOOKS,
.num_entries = 2,
.size = sizeof(struct ip6t_standard) +
sizeof(struct ip6t_error),
.hook_entry = {
[NF_INET_POST_ROUTING] = 0,
},
.underflow = {
[NF_INET_POST_ROUTING] = 0,
},
},
.entries = {
IP6T_STANDARD_INIT(NF_ACCEPT), /* POST_ROUTING */
},
.term = IP6T_ERROR_INIT, /* ERROR */
};
static struct xt_table *rawpost6_ptable;
static struct xt_table rawpost6_itable = {
.name = "rawpost",
.af = NFPROTO_IPV6,
.valid_hooks = RAWPOST_VALID_HOOKS,
.me = THIS_MODULE,
};
static unsigned int rawpost6_hook_fn(unsigned int hook, sk_buff_t *skb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return ip6t_do_table(skb, hook, in, out, rawpost6_ptable);
}
static struct nf_hook_ops rawpost6_hook_ops __read_mostly = {
.hook = rawpost6_hook_fn,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_LAST,
.owner = THIS_MODULE,
};
static int __init rawpost6_table_init(void)
{
int ret;
rawpost6_ptable = ip6t_register_table(&init_net, &rawpost6_itable,
&rawpost6_initial.repl);
if (IS_ERR(rawpost6_ptable))
return PTR_ERR(rawpost6_ptable);
ret = nf_register_hook(&rawpost6_hook_ops);
if (ret < 0)
goto out;
return ret;
out:
ip6t_unregister_table(rawpost6_ptable);
return ret;
}
static void __exit rawpost6_table_exit(void)
{
nf_unregister_hook(&rawpost6_hook_ops);
ip6t_unregister_table(rawpost6_ptable);
}
module_init(rawpost6_table_init);
module_exit(rawpost6_table_exit);
MODULE_AUTHOR("Jan Engelhardt ");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,95 @@
/*
* rawpost table for ip_tables
* written by Jan Engelhardt, 2008 - 2009
* placed in the Public Domain
*/
#include <linux/module.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/version.h>
#include <net/ip.h>
#include "compat_xtables.h"
#include "compat_rawpost.h"
enum {
RAWPOST_VALID_HOOKS = 1 << NF_INET_POST_ROUTING,
};
static struct {
struct ipt_replace repl;
struct ipt_standard entries[1];
struct ipt_error term;
} rawpost4_initial __initdata = {
.repl = {
.name = "rawpost",
.valid_hooks = RAWPOST_VALID_HOOKS,
.num_entries = 2,
.size = sizeof(struct ipt_standard) +
sizeof(struct ipt_error),
.hook_entry = {
[NF_INET_POST_ROUTING] = 0,
},
.underflow = {
[NF_INET_POST_ROUTING] = 0,
},
},
.entries = {
IPT_STANDARD_INIT(NF_ACCEPT), /* POST_ROUTING */
},
.term = IPT_ERROR_INIT, /* ERROR */
};
static struct xt_table *rawpost4_ptable;
static struct xt_table rawpost4_itable = {
.name = "rawpost",
.af = NFPROTO_IPV4,
.valid_hooks = RAWPOST_VALID_HOOKS,
.me = THIS_MODULE,
};
static unsigned int rawpost4_hook_fn(unsigned int hook, sk_buff_t *skb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return ipt_do_table(skb, hook, in, out, rawpost4_ptable);
}
static struct nf_hook_ops rawpost4_hook_ops __read_mostly = {
.hook = rawpost4_hook_fn,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_LAST,
.owner = THIS_MODULE,
};
static int __init rawpost4_table_init(void)
{
int ret;
rawpost4_ptable = ipt_register_table(&init_net, &rawpost4_itable,
&rawpost4_initial.repl);
if (IS_ERR(rawpost4_ptable))
return PTR_ERR(rawpost4_ptable);
ret = nf_register_hook(&rawpost4_hook_ops);
if (ret < 0)
goto out;
return ret;
out:
ipt_unregister_table(rawpost4_ptable);
return ret;
}
static void __exit rawpost4_table_exit(void)
{
nf_unregister_hook(&rawpost4_hook_ops);
ipt_unregister_table(rawpost4_ptable);
}
module_init(rawpost4_table_init);
module_exit(rawpost4_table_exit);
MODULE_DESCRIPTION("Xtables: rawpost table for use with RAWNAT");
MODULE_AUTHOR("Jan Engelhardt ");
MODULE_LICENSE("GPL");

View File

@@ -64,6 +64,21 @@ static void chaos_tg_check(unsigned int flags)
"may be specified"); "may be specified");
} }
static void chaos_tg_print(const void *ip,
const struct xt_entry_target *target, int numeric)
{
const struct xt_chaos_tginfo *info = (const void *)target->data;
switch (info->variant) {
case XTCHAOS_DELUDE:
printf(" DELUDE ");
break;
case XTCHAOS_TARPIT:
printf(" TARPIT ");
break;
}
}
static void chaos_tg_save(const void *ip, const struct xt_entry_target *target) static void chaos_tg_save(const void *ip, const struct xt_entry_target *target)
{ {
const struct xt_chaos_tginfo *info = (const void *)target->data; const struct xt_chaos_tginfo *info = (const void *)target->data;
@@ -78,13 +93,6 @@ static void chaos_tg_save(const void *ip, const struct xt_entry_target *target)
} }
} }
static void chaos_tg_print(const void *ip,
const struct xt_entry_target *target, int numeric)
{
printf(" -j CHAOS");
chaos_tg_save(ip, target);
}
static struct xtables_target chaos_tg_reg = { static struct xtables_target chaos_tg_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "CHAOS", .name = "CHAOS",

View File

@@ -1,4 +1,3 @@
.PP
Causes confusion on the other end by doing odd things with incoming packets. Causes confusion on the other end by doing odd things with incoming packets.
CHAOS will randomly reply (or not) with one of its configurable subtargets: CHAOS will randomly reply (or not) with one of its configurable subtargets:
.TP .TP

View File

@@ -1,4 +1,3 @@
.PP
The DELUDE target will reply to a SYN packet with SYN-ACK, and to all other The DELUDE target will reply to a SYN packet with SYN-ACK, and to all other
packets with an RST. This will terminate the connection much like REJECT, but packets with an RST. This will terminate the connection much like REJECT, but
network scanners doing TCP half-open discovery can be spoofed to make them network scanners doing TCP half-open discovery can be spoofed to make them

View File

@@ -61,6 +61,15 @@ static void dhcpmac_tg_check(unsigned int flags)
"--set-mac parameter required"); "--set-mac parameter required");
} }
static void dhcpmac_tg_print(const void *ip,
const struct xt_entry_target *target, int numeric)
{
const struct dhcpmac_info *info = (void *)target->data;
printf(" DHCPMAC %s" DH_MAC_FMT "/%u ",
info->invert ? "!" : "", DH_MAC_HEX(info->addr), info->mask);
}
static void dhcpmac_tg_save(const void *ip, static void dhcpmac_tg_save(const void *ip,
const struct xt_entry_target *target) const struct xt_entry_target *target)
{ {
@@ -72,13 +81,6 @@ static void dhcpmac_tg_save(const void *ip,
DH_MAC_HEX(info->addr), info->mask); DH_MAC_HEX(info->addr), info->mask);
} }
static void dhcpmac_tg_print(const void *ip,
const struct xt_entry_target *target, int numeric)
{
printf(" -j DHCPMAC");
dhcpmac_tg_save(ip, target);
}
static struct xtables_target dhcpmac_tg_reg = { static struct xtables_target dhcpmac_tg_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "DHCPMAC", .name = "DHCPMAC",

View File

@@ -1,4 +1,3 @@
.PP
In conjunction with ebtables, DHCPMAC can be used to completely change all MAC In conjunction with ebtables, DHCPMAC can be used to completely change all MAC
addresses from and to a VMware-based virtual machine. This is needed because addresses from and to a VMware-based virtual machine. This is needed because
VMware does not allow to set a non-VMware MAC address before an operating VMware does not allow to set a non-VMware MAC address before an operating

View File

@@ -195,6 +195,33 @@ static void DNETMAP_print_addr(const void *ip,
printf("/%d", bits); printf("/%d", bits);
} }
static void DNETMAP_print(const void *ip, const struct xt_entry_target *target,
int numeric)
{
struct xt_DNETMAP_tginfo *tginfo = (void *)&target->data;
const __u8 *flags = &tginfo->flags;
printf(" prefix ");
if (*flags & XT_DNETMAP_PREFIX)
DNETMAP_print_addr(ip, target, numeric);
else
printf("any");
if (*flags & XT_DNETMAP_REUSE)
printf(" reuse");
if (*flags & XT_DNETMAP_STATIC)
printf(" static");
if (*flags & XT_DNETMAP_PERSISTENT)
printf(" persistent");
if (*flags & XT_DNETMAP_TTL)
printf(" ttl %i", tginfo->ttl);
else
printf(" ttl default");
}
static void DNETMAP_save(const void *ip, const struct xt_entry_target *target) static void DNETMAP_save(const void *ip, const struct xt_entry_target *target)
{ {
struct xt_DNETMAP_tginfo *tginfo = (void *)&target->data; struct xt_DNETMAP_tginfo *tginfo = (void *)&target->data;
@@ -219,13 +246,6 @@ static void DNETMAP_save(const void *ip, const struct xt_entry_target *target)
printf(" --ttl %i ", tginfo->ttl); printf(" --ttl %i ", tginfo->ttl);
} }
static void DNETMAP_print(const void *ip, const struct xt_entry_target *target,
int numeric)
{
printf(" -j DNETMAP");
DNETMAP_save(ip, target);
}
static struct xtables_target dnetmap_tg_reg = { static struct xtables_target dnetmap_tg_reg = {
.name = MODULENAME, .name = MODULENAME,
.version = XTABLES_VERSION, .version = XTABLES_VERSION,

View File

@@ -1,179 +1,172 @@
.PP The \fBDNETMAP\fR target allows dynamic two-way 1:1 mapping of IPv4 subnets.
The \fBDNETMAP\fR target allows dynamic two-way 1:1 mapping of IPv4 subnets. A Single rule can map private subnet to shorter public subnet creating and
single rule can map a private subnet to a shorter public subnet, creating and maintaining unambigeous private-public ip bindings. Second rule can be used to
maintaining unambiguous private-public IP address bindings. The second rule can map new flows to private subnet according to maintained bindings. Target allows
be used to map new flows to a private subnet according to maintained bindings. efficient public IPv4 space usage and unambigeous NAT at the same time.
The target allows efficient public IPv4 space usage and unambiguous NAT at the
same time. Target can be used only in \fBnat\fR table in \fBPOSTROUTING\fR or \fBOUTPUT\fR
.PP chains for SNAT and in \fBPREROUTING\fR for DNAT. Only flows directed to bound
The target can be used only in the \fBnat\fR table in \fBPOSTROUTING\fR or IPs will be DNATed. Packet continues chain traversal if there is no free
\fBOUTPUT\fR chains for SNAT, and in \fBPREROUTING\fR for DNAT. Only flows postnat-ip to be assigned to prenat-ip. Default binding \fBttl\fR is \fI10
directed to bound addresses will be DNATed. The packet continues chain minutes\fR and can be changed using \fBdefault_ttl\fR module option. Default ip
traversal if there is no free postnat address to be assigned to the prenat hash size is 256 and can be changed using \fBhash_size\fR module option.
address. The default binding \fBTTL\fR is \fI10 minutes\fR and can be changed
using the \fBdefault_ttl\fR module option. The default address hash size is 256
and can be changed using the \fBhash_size\fR module option.
.TP .TP
\fB\-\-prefix\fR \fIaddr\fR\fB/\fR\fImask\fR \fB\-\-prefix\fR \fIaddr\fR\fB/\fR\fImask\fR
The network subnet to map to. If not specified, all existing prefixes are used. Network subnet to map to. If not specified, all existing prefixes are used.
.TP .TP
\fB\-\-reuse\fR \fB\-\-reuse\fR
Reuse the entry for a given prenat address from any prefix even if the Reuse entry for given prenat-ip from any prefix despite bindings ttl < 0.
binding's TTL is < 0.
.TP .TP
\fB\-\-persistent\fR \fB\-\-persistent\fR
Set the prefix to be persistent. It will not be removed after deleting the last Set prefix persistent. It won't be removed after deleting last iptables rule.
iptables rule. The option is effective only in the first rule for a given Option is effective only in the first rule for a given prefix. If you
prefix. If you need to change persistency for an existing prefix, please use need to change persistency for existing prefix, please use proc interface
the procfs interface described below. described below.
.TP .TP
\fB\-\-static\fR \fB\-\-static\fR
Do not create dynamic mappings using this rule. Use static mappings only. Note Don't create dynamic mappings using this rule. Use static mappings only. Note
that you need to create static mappings via the procfs interface for this rule that you need to create static mappings via proc interface for this rule with
for this option to have any effect. this option to have any effect.
.TP .TP
\fB\-\-ttl\fR \fIseconds\fR \fB\-\-ttl\fR \fIseconds\fR
Reset the binding's TTL value to \fIseconds\fR. If a negative value is Regenerate bindings ttl value to \fIseconds\fR. If negative value is specified,
specified, the binding's TTL is kept unchanged. If this option is not bindings ttl is kept unchanged. If not specified then default ttl value (600s)
specified, then the default TTL value (600s) is used. is used.
.PP .PP
\fB* /proc interface\fR \fB* /proc interface\fR
.PP
The module creates the following entries for each new specified subnet: Module creates following entries for each new specified subnet:
.TP .TP
\fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR \fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR
Contains the binding table for the given \fIsubnet/mask\fP. Each line contains Contains binding table for subnet/mask. Each line contains \fBprenat-ip\fR,
\fBprenat address\fR, \fBpostnat address\fR, \fBttl\fR (seconds until the entry \fBpostnat-ip\fR,\fBttl\fR (seconds till entry times out), \fBlasthit\fR (last
times out), \fBlasthit\fR (last hit to the entry in seconds relative to system entry hit in seconds relative to system boot time). Please note that \fBttl\fR
boot time). Please note that the \fBttl\fR and \fBlasthit\fR entries contain an and \fBlasthit\fR entries contain \fBS\fR in case of static binding.
\(oq\fBS\fR\(cq in case of a static binding.
.TP .TP
\fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR\fB_stat\fR \fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR\fB_stat\fR
Contains statistics for a given \fIsubnet/mask\fP. The line contains four Contains statistics for given subnet/mask. Line contains contains four
numerical values separated by spaces. The first one is the number of currently numerical values separated by spaces. First one is number of currently used
used dynamic addresses (bindings with negative TTL excluded), the second one is dynamic addresses (bindings with negative ttl excluded), second one is number
the number of static assignments, the third one is the number of all usable static assignments, third one is number of all usable addresses in subnet and
addresses in the subnet, and the fourth one is the mean \fBTTL\fR value for all the fourth one is mean \fBttl\fR value for all active entries. If prefix has
active entries. If the prefix has the persistent flag set, it will be noted as persistent flag set it'll be noted as fifth entry.
fifth entry.
.PP .PP
The following write operations are supported via the procfs interface: Following write operations are supported via proc interface:
.TP .TP
echo "+\fIprenat-address\fR:\fIpostnat-address\fR" >\fB/proc/net/xt_DNETMAP/subnet_mask\fR echo "+\fIprenatIP\fR:\fIpostnatIP\fR" > \fB/proc/net/xt_DNETMAP/subnet_mask\fR
Adds a static binding between the prenat and postnap address. If Adds static binding between prenatIP nad postnatIP. If postnatIP is already
postnat_address is already bound, any previous binding will be timed out bound, previous binding will be timedout immediatelly. Static binding is never
immediately. A static binding is never timed out. timedout.
.TP .TP
echo "\-\fIaddress\fR" >\fB/proc/net/xt_DNETMAP/subnet_mask\fR echo "-\fIIP\fR" > \fB/proc/net/xt_DNETMAP/subnet_mask\fR
Removes the binding with \fIaddress\fR as prenat or postnat address. If the Removes binding with \fIIP\fR as prenat or postnat address. If removed binding
removed binding is currently static, it will make the entry available for is currently static, it'll make entry available for dynamic allocation.
dynamic allocation.
.TP .TP
echo "+persistent" > \fB/proc/net/xt_DNETMAP/subnet_mask\fR echo "+persistent" > \fB/proc/net/xt_DNETMAP/subnet_mask\fR
Sets the persistent flag for the prefix. It is useful if you do not want Sets persistent flag for prefix. It's usefull if you don't want bindings to get
bindings to get flushed when the firewall is restarted. You can check if the flushed when firewall is restarted. You can check if prefix is persistent by
prefix is persistent by printing the contents of printing \fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR\fB_stat\fR
\fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR\fB_stat\fR. contents.
.TP .TP
echo "\-persistent" >\fB/proc/net/xt_DNETMAP/subnet_mask\fR echo "-persistent" > \fB/proc/net/xt_DNETMAP/subnet_mask\fR
Unsets the persistent flag for the prefix. In this mode, the prefix will be Unsets persistent flag for prefix. In this mode prefix will be deleted if the
deleted if the last iptables rule for that prefix is removed. last iptables rule for that prefix is removed.
.TP .TP
echo "flush" > \fB/proc/net/xt_DNETMAP/subnet_mask\fR echo "flush" > \fB/proc/net/xt_DNETMAP/subnet_mask\fR
Flushes all bindings for the specific prefix. All static entries are also Flushes all bindings for specific prefix. All static entries are also flushed
flushed and become available for dynamic bindings. and are available for dynamic bindings.
.PP .PP
Note! Entries are removed if the last iptables rule for a specific prefix is Note! Entries are removed if the last iptables rule for a specific prefix is
deleted unless the persistent flag is set. deleted unless there's persistent flag set.
.PP .PP
\fB* Logging\fR \fB* Logging\fR
.PP
The module logs binding add/timeout events to klog. This behaviour can be Module logs binding add/timeout events to klog. This behaviour can be disabled
disabled using the \fBdisable_log\fR module parameter. using \fBdisable_log\fR module parameter.
.PP
\fB* Examples\fR \fB* Examples\fR
.PP
\fB1.\fR Map subnet 192.168.0.0/24 to subnets 20.0.0.0/26. SNAT only: \fB1.\fR Map subnet 192.168.0.0/24 to subnets 20.0.0.0/26. SNAT only:
.PP
iptables \-t nat \-A POSTROUTING \-s 192.168.0.0/24 \-j DNETMAP \-\-prefix 20.0.0.0/26 iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
.PP
Active hosts from the 192.168.0.0/24 subnet are mapped to 20.0.0.0/26. If the Active hosts from 192.168.0.0/24 subnet are mapped to 20.0.0.0/26. If packet
packet from a not yet bound prenat address hits the rule and there are no free from not yet bound prenat-ip hits the rule and there are no free or timed-out
or timed-out (TTL<0) entries in prefix 20.0.0.0/28, then a notice is logged to (ttl<0) entries in prefix 20.0.0.0/28, then notice is logged to klog and chain
klog and chain traversal continues. If packet from an already-bound prenat traversal continues. If packet from already bound prenat-ip hits the rule,
address hits the rule, the binding's TTL value is reset to default_ttl and SNAT bindings ttl value is regenerated to default_ttl and SNAT is performed.
is performed.
.PP
\fB2.\fR Use of \fB\-\-reuse\fR and \fB\-\-ttl\fR switches, multiple rule \fB2.\fR Use of \fB\-\-reuse\fR and \fB\-\-ttl\fR switches, multiple rule
interaction: interaction:
.PP
iptables \-t nat \-A POSTROUTING \-s 192.168.0.0/24 \-j DNETMAP \-\-prefix iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix
20.0.0.0/26 \-\-reuse \-\-ttl 200 20.0.0.0/26 --reuse --ttl 200
.PP
iptables \-t nat \-A POSTROUTING \-s 192.168.0.0/24 \-j DNETMAP \-\-prefix 30.0.0.0/26 iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 30.0.0.0/26
.PP
Active hosts from 192.168.0.0/24 subnet are mapped to 20.0.0.0/26 with TTL = Active hosts from 192.168.0.0/24 subnet are mapped to 20.0.0.0/26 with ttl =
200 seconds. If there are no free addresses in first prefix, the next one 200 seconds. If there are no free addresses in first prefix the next one
(30.0.0.0/26) is used with the default TTL. It is important to note that the (30.0.0.0/26) is used with default ttl. It's important to note that the first
first rule SNATs all flows whose source address is already actively bound rule SNATs all flows whose source IP is already actively (ttl>0) bound to ANY
(TTL>0) to ANY prefix. The \fB\-\-reuse\fR parameter makes this functionality prefix. Parameter \fB\-\-reuse\fR makes this functionality work even for
work even for inactive (TTL<0) entries. inactive (ttl<0) entries.
.PP
If both subnets are exhausted, then chain traversal continues. If both subnets are exhaused, then chain traversal continues.
.PP
\fB3.\fR Map 192.168.0.0/24 to subnets 20.0.0.0/26 in a bidirectional way: \fB3.\fR Map 192.168.0.0/24 to subnets 20.0.0.0/26 bidirectional way:
.PP
iptables \-t nat \-A POSTROUTING \-s 192.168.0.0/24 \-j DNETMAP \-\-prefix 20.0.0.0/26 iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
.PP
iptables \-t nat \-A PREROUTING \-j DNETMAP iptables -t nat -A PREROUTING -j DNETMAP
.PP
If the host 192.168.0.10 generates some traffic, it gets bound to first free If host 192.168.0.10 generates some traffic, it gets bound to first free IP in
address in the subnet \(em 20.0.0.0. Now, any traffic directed to 20.0.0.0 gets subnet - 20.0.0.0. Now any traffic directed to 20.0.0.0 gets DNATed to
DNATed to 192.168.0.10 as long as there is an active (TTL>0) binding. There is 192.168.0.10 as long as there's an active (ttl>0) binding. There's no need to
no need to specify \fB\-\-prefix\fR parameter in a PREROUTING rule, because specify \fB\-\-prefix\fR parameter in PREROUTING rule, because this way it DNATs
this way, it DNATs traffic to all active prefixes. You could specify the prefix traffic to all active prefixes. You could specify prefix it you'd like to make
you would like to make DNAT work for a specific prefix only. DNAT work for specific prefix only.
.PP
\fB4.\fR Map 192.168.0.0/24 to subnets 20.0.0.0/26 with static assignments \fB4.\fR Map 192.168.0.0/24 to subnets 20.0.0.0/26 with static assignments only:
only:
.PP iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
iptables \-t nat \-A POSTROUTING \-s 192.168.0.0/24 \-j DNETMAP \-\-prefix 20.0.0.0/26 --static
\-\-static
.PP
echo "+192.168.0.10:20.0.0.1" > /proc/net/xt_DNETMAP/20.0.0.0_26 echo "+192.168.0.10:20.0.0.1" > /proc/net/xt_DNETMAP/20.0.0.0_26
.br .br
echo "+192.168.0.11:20.0.0.2" > /proc/net/xt_DNETMAP/20.0.0.0_26 echo "+192.168.0.11:20.0.0.2" > /proc/net/xt_DNETMAP/20.0.0.0_26
.br .br
echo "+192.168.0.51:20.0.0.3" > /proc/net/xt_DNETMAP/20.0.0.0_26 echo "+192.168.0.51:20.0.0.3" > /proc/net/xt_DNETMAP/20.0.0.0_26
.PP
This configuration will allow only preconfigured static bindings to work due to This configuration will allow only preconfigured static bindings to work due to
the \fBstatic\fR rule option. Without this flag, dynamic bindings would be \fBstatic\fR rule option. Without this flag dynamic bindings would be created
created using non-static entries. using non-static entries.
.PP
\fB5.\fR Persistent prefix: \fB5.\fR Persistent prefix:
.PP
iptables \-t nat \-A POSTROUTING \-s 192.168.0.0/24 \-j DNETMAP \-\-prefix 20.0.0.0/26 iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
\-\-persistent --persistent
.br .br
\fBor\fR \fBor\fR
.br .br
iptables \-t nat \-A POSTROUTING \-s 192.168.0.0/24 \-j DNETMAP \-\-prefix 20.0.0.0/26 iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
.br .br
echo "+persistent" > /proc/net/xt_DNETMAP/20.0.0.0_26 echo "+persistent" > /proc/net/xt_DNETMAP/20.0.0.0_26
.PP
Now, we can check the persistent flag of the prefix: Now we can check persistent flag of the prefix:
.br .br
cat /proc/net/xt_DNETMAP/20.0.0.0_26 cat /proc/net/xt_DNETMAP/20.0.0.0_26
.br .br
0 0 64 0 \fBpersistent\fR 0 0 64 0 \fBpersistent\fR
.PP
Flush the iptables nat table and see that prefix is still in existence: Flush iptables nat table and see that prefix is still in existence:
.br .br
iptables \-F \-t nat iptables -F -t nat
.br .br
ls \-l /proc/net/xt_DNETMAP ls -l /proc/net/xt_DNETMAP
.br .br
\-rw\-r\-\-r\-\- 1 root root 0 06\-10 09:01 20.0.0.0_26 -rw-r--r-- 1 root root 0 06-10 09:01 20.0.0.0_26
.br .br
\-rw\-r\-\-r\-\- 1 root root 0 06\-10 09:01 20.0.0.0_26_stat -rw-r--r-- 1 root root 0 06-10 09:01 20.0.0.0_26_stat
. .

View File

@@ -1,4 +1,3 @@
.PP
The \fBECHO\fP target will send back all packets it received. It serves as an The \fBECHO\fP target will send back all packets it received. It serves as an
examples for an Xtables target. examples for an Xtables target.
.PP .PP

View File

@@ -112,6 +112,25 @@ static void ipmark_tg_check(unsigned int flags)
"IPMARK target: Parameter --addr is required"); "IPMARK target: Parameter --addr is required");
} }
static void
ipmark_tg_print(const void *entry, const struct xt_entry_target *target,
int numeric)
{
const struct xt_ipmark_tginfo *info = (const void *)target->data;
if (info->selector == XT_IPMARK_SRC)
printf(" IPMARK src ip ");
else
printf(" IPMARK dst ip ");
if (info->shift != 0)
printf(" shift %u ", (unsigned int)info->shift);
if (info->andmask != ~0U)
printf(" and 0x%x ", (unsigned int)info->andmask);
if (info->ormask != 0)
printf(" or 0x%x ", (unsigned int)info->ormask);
}
static void static void
ipmark_tg_save(const void *entry, const struct xt_entry_target *target) ipmark_tg_save(const void *entry, const struct xt_entry_target *target)
{ {
@@ -130,14 +149,6 @@ ipmark_tg_save(const void *entry, const struct xt_entry_target *target)
printf(" --or-mask 0x%x ", (unsigned int)info->ormask); printf(" --or-mask 0x%x ", (unsigned int)info->ormask);
} }
static void
ipmark_tg_print(const void *entry, const struct xt_entry_target *target,
int numeric)
{
printf(" -j IPMARK");
ipmark_tg_save(entry, target);
}
static struct xtables_target ipmark_tg_reg = { static struct xtables_target ipmark_tg_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "IPMARK", .name = "IPMARK",

View File

@@ -1,8 +1,7 @@
.PP
Allows you to mark a received packet basing on its IP address. This Allows you to mark a received packet basing on its IP address. This
can replace many mangle/mark entries with only one, if you use can replace many mangle/mark entries with only one, if you use
firewall based classifier. firewall based classifier.
.PP
This target is to be used inside the \fBmangle\fP table. This target is to be used inside the \fBmangle\fP table.
.TP .TP
\fB\-\-addr\fP {\fBsrc\fP|\fBdst\fP} \fB\-\-addr\fP {\fBsrc\fP|\fBdst\fP}

View File

@@ -77,6 +77,15 @@ logmark_tg_parse(int c, char **argv, int invert, unsigned int *flags,
return false; return false;
} }
static void
logmark_tg_print(const void *ip, const struct xt_entry_target *target,
int numeric)
{
const struct xt_logmark_tginfo *info = (void *)target->data;
printf(" LOGMARK level %u prefix \"%s\" ", info->level, info->prefix);
}
static void static void
logmark_tg_save(const void *ip, const struct xt_entry_target *target) logmark_tg_save(const void *ip, const struct xt_entry_target *target)
{ {
@@ -88,14 +97,6 @@ logmark_tg_save(const void *ip, const struct xt_entry_target *target)
printf(" --log-prefix \"%s\" ", info->prefix); printf(" --log-prefix \"%s\" ", info->prefix);
} }
static void
logmark_tg_print(const void *ip, const struct xt_entry_target *target,
int numeric)
{
printf(" -j LOGMARK");
logmark_tg_save(ip, target);
}
static struct xtables_target logmark_tg_reg = { static struct xtables_target logmark_tg_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "LOGMARK", .name = "LOGMARK",

View File

@@ -1,4 +1,3 @@
.PP
The LOGMARK target will log packet and connection marks to syslog. The LOGMARK target will log packet and connection marks to syslog.
.TP .TP
\fB\-\-log\-level\fR \fIlevel\fR \fB\-\-log\-level\fR \fIlevel\fR

View File

@@ -1,105 +0,0 @@
/*
* PROTO Target module
* This program is distributed under the terms of GNU GPL
*/
#include <stdio.h>
#include <xtables.h>
#include "xt_PROTO.h"
enum {
O_PROTO_SET = 0,
O_PROTO_STOP_AT_FRAG = 1,
O_PROTO_STOP_AT_AUTH = 2,
F_PROTO_SET = 1 << O_PROTO_SET,
F_PROTO_STOP_AT_FRAG = 1 << O_PROTO_STOP_AT_FRAG,
F_PROTO_STOP_AT_AUTH = 1 << O_PROTO_STOP_AT_AUTH,
};
#define s struct xt_PROTO_info
static const struct xt_option_entry PROTO_opts[] = {
{.name = "proto-set", .type = XTTYPE_UINT8, .id = O_PROTO_SET,
.flags = XTOPT_PUT | XTOPT_MAND, XTOPT_POINTER(s, proto)},
{.name = "stop-at-frag", .type = XTTYPE_NONE, .id = O_PROTO_STOP_AT_FRAG},
{.name = "stop-at-auth", .type = XTTYPE_NONE, .id = O_PROTO_STOP_AT_AUTH},
XTOPT_TABLEEND,
};
#undef s
static void PROTO_help(void)
{
printf(
"PROTO target options\n"
" --proto-set value Set protocol to <value 0-255>\n"
);
}
static void PROTO_parse(struct xt_option_call *cb)
{
struct xt_PROTO_info *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_PROTO_SET:
info->mode |= 1 << XT_PROTO_SET;
break;
case O_PROTO_STOP_AT_FRAG:
info->mode |= 1 << XT_PROTO_STOP_AT_FRAG;
break;
case O_PROTO_STOP_AT_AUTH:
info->mode |= 1 << XT_PROTO_STOP_AT_AUTH;
break;
}
}
static void PROTO_check(struct xt_fcheck_call *cb)
{
if (!(cb->xflags & F_PROTO_SET))
xtables_error(PARAMETER_PROBLEM,
"PROTO: You must specify the proto to be set");
}
static void PROTO_save(const void *ip, const struct xt_entry_target *target)
{
const struct xt_PROTO_info *info = (void *)target->data;
if (info->mode & (1 << XT_PROTO_SET))
printf(" --proto-set %u", info->proto);
if (info->mode & (1 << XT_PROTO_STOP_AT_FRAG))
printf(" --stop-at-frag");
if (info->mode & (1 << XT_PROTO_STOP_AT_AUTH))
printf(" --stop-at-auth");
}
static void PROTO_print(const void *ip, const struct xt_entry_target *target,
int numeric)
{
const struct xt_PROTO_info *info = (void *)target->data;
printf(" PROTO ");
if (info->mode & (1 << XT_PROTO_SET))
printf("set to %u", info->proto);
if (info->mode & (1 << XT_PROTO_STOP_AT_FRAG))
printf(" stop-at-frag");
if (info->mode & (1 << XT_PROTO_STOP_AT_AUTH))
printf(" stop-at-auth");
}
static struct xtables_target proto_tg_reg = {
.name = "PROTO",
.version = XTABLES_VERSION,
.family = NFPROTO_UNSPEC,
.size = XT_ALIGN(sizeof(struct xt_PROTO_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_PROTO_info)),
.help = PROTO_help,
.print = PROTO_print,
.save = PROTO_save,
.x6_parse = PROTO_parse,
.x6_fcheck = PROTO_check,
.x6_options = PROTO_opts,
};
static __attribute__((constructor)) void _init(void)
{
xtables_register_target(&proto_tg_reg);
}

View File

@@ -1,30 +0,0 @@
.PP
The PROTO target modifies the protocol number in IP packet header.
.TP
\fB\-\-proto-set\fP \fIproto_num\fP
This option is mandatory. \fIproto_num\fP is the protocol number to which you want to
modify the packets.
.TP
\fB\-\-stop-at-frag\fP
This option is only valid for IPv6 rules. When specifying this option, the
fragment extension header will be seen as a non-extension header.
.TP
\fB\-\-stop-at-auth\fP
This option is only valid for IPv6 rules. When specifying this option, the
authentication extension header will be seen as a non-extension header.
.PP
For IPv4 packets, the \fBProtocol\fP field is modified and the checksum is
re-calculated.
.PP
For IPv6 packets, the scenario can be more complex due to the introduction of
the extension headers mechanism. By default, the PROTO target will scan the IPv6
packet, finding the last extension header and modify its \fBNext-header\fP field.
Normally, the following headers will be seen as an extension header:
\fINEXTHDR_HOP\fP,
\fINEXTHDR_ROUTING\fP,
\fINEXTHDR_FRAGMENT\fP,
\fINEXTHDR_AUTH\fP,
\fINEXTHDR_DEST\fP.
.PP
For fragmented packets, only the first fragment is processed and other fragments
are not touched.

189
extensions/libxt_RAWDNAT.c Normal file
View File

@@ -0,0 +1,189 @@
/*
* "RAWNAT" target extension for iptables
* Copyright © Jan Engelhardt, 2008 - 2009
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License; either
* version 2 of the License, or any later version, as published by the
* Free Software Foundation.
*/
#include <netinet/in.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter.h>
#include "xt_RAWNAT.h"
#include "compat_user.h"
enum {
FLAGS_TO = 1 << 0,
};
static const struct option rawdnat_tg_opts[] = {
{.name = "to-destination", .has_arg = true, .val = 't'},
{},
};
static void rawdnat_tg_help(void)
{
printf(
"RAWDNAT target options:\n"
" --to-destination addr[/mask] Address or network to map to\n"
);
}
static int
rawdnat_tg4_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_rawnat_tginfo *info = (void *)(*target)->data;
struct in_addr *a;
unsigned int mask;
char *end;
switch (c) {
case 't':
info->mask = 32;
end = strchr(optarg, '/');
if (end != NULL) {
*end++ = '\0';
if (!xtables_strtoui(end, NULL, &mask, 0, 32))
xtables_param_act(XTF_BAD_VALUE, "RAWDNAT",
"--to-destination", optarg);
info->mask = mask;
}
a = xtables_numeric_to_ipaddr(optarg);
if (a == NULL)
xtables_param_act(XTF_BAD_VALUE, "RAWDNAT",
"--to-destination", optarg);
memcpy(&info->addr.in, a, sizeof(*a));
*flags |= FLAGS_TO;
return true;
}
return false;
}
static int
rawdnat_tg6_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_rawnat_tginfo *info = (void *)(*target)->data;
struct in6_addr *a;
unsigned int mask;
char *end;
switch (c) {
case 't':
info->mask = 128;
end = strchr(optarg, '/');
if (end != NULL) {
*end++ = '\0';
if (!xtables_strtoui(end, NULL, &mask, 0, 128))
xtables_param_act(XTF_BAD_VALUE, "RAWDNAT",
"--to-destination", optarg);
info->mask = mask;
}
a = xtables_numeric_to_ip6addr(optarg);
if (a == NULL)
xtables_param_act(XTF_BAD_VALUE, "RAWDNAT",
"--to-destination", optarg);
memcpy(&info->addr.in6, a, sizeof(*a));
*flags |= FLAGS_TO;
return true;
}
return false;
}
static void rawdnat_tg_check(unsigned int flags)
{
if (!(flags & FLAGS_TO))
xtables_error(PARAMETER_PROBLEM, "RAWDNAT: "
"\"--to-destination\" is required.");
}
static void
rawdnat_tg4_print(const void *entry, const struct xt_entry_target *target,
int numeric)
{
const struct xt_rawnat_tginfo *info = (const void *)target->data;
if (!numeric && info->mask == 32)
printf(" to-destination %s ",
xtables_ipaddr_to_anyname(&info->addr.in));
else
printf(" to-destination %s/%u ",
xtables_ipaddr_to_numeric(&info->addr.in), info->mask);
}
static void
rawdnat_tg6_print(const void *entry, const struct xt_entry_target *target,
int numeric)
{
const struct xt_rawnat_tginfo *info = (const void *)target->data;
if (!numeric && info->mask == 128)
printf(" to-destination %s ",
xtables_ip6addr_to_anyname(&info->addr.in6));
else
printf(" to-destination %s/%u ",
xtables_ip6addr_to_numeric(&info->addr.in6), info->mask);
}
static void
rawdnat_tg4_save(const void *entry, const struct xt_entry_target *target)
{
const struct xt_rawnat_tginfo *info = (const void *)target->data;
printf(" --to-destination %s/%u ",
xtables_ipaddr_to_numeric(&info->addr.in),
info->mask);
}
static void
rawdnat_tg6_save(const void *entry, const struct xt_entry_target *target)
{
const struct xt_rawnat_tginfo *info = (const void *)target->data;
printf(" --to-destination %s/%u ",
xtables_ip6addr_to_numeric(&info->addr.in6),
info->mask);
}
static struct xtables_target rawdnat_tg_reg[] = {
{
.version = XTABLES_VERSION,
.name = "RAWDNAT",
.revision = 0,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_rawnat_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_rawnat_tginfo)),
.help = rawdnat_tg_help,
.parse = rawdnat_tg4_parse,
.final_check = rawdnat_tg_check,
.print = rawdnat_tg4_print,
.save = rawdnat_tg4_save,
.extra_opts = rawdnat_tg_opts,
},
{
.version = XTABLES_VERSION,
.name = "RAWDNAT",
.revision = 0,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_rawnat_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_rawnat_tginfo)),
.help = rawdnat_tg_help,
.parse = rawdnat_tg6_parse,
.final_check = rawdnat_tg_check,
.print = rawdnat_tg6_print,
.save = rawdnat_tg6_save,
.extra_opts = rawdnat_tg_opts,
},
};
static void _init(void)
{
xtables_register_targets(rawdnat_tg_reg,
sizeof(rawdnat_tg_reg) / sizeof(*rawdnat_tg_reg));
}

View File

@@ -0,0 +1,10 @@
The \fBRAWDNAT\fR target will rewrite the destination address in the IP header,
much like the \fBNETMAP\fR target.
.TP
\fB\-\-to\-destination\fR \fIaddr\fR[\fB/\fR\fImask\fR]
Network address to map to. The resulting address will be constructed the
following way: All 'one' bits in the \fImask\fR are filled in from the new
\fIaddress\fR. All bits that are zero in the mask are filled in from the
original address.
.PP
See the \fBRAWSNAT\fR help entry for examples and constraints.

189
extensions/libxt_RAWSNAT.c Normal file
View File

@@ -0,0 +1,189 @@
/*
* "RAWNAT" target extension for iptables
* Copyright © Jan Engelhardt, 2008 - 2009
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License; either
* version 2 of the License, or any later version, as published by the
* Free Software Foundation.
*/
#include <netinet/in.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter.h>
#include "xt_RAWNAT.h"
#include "compat_user.h"
enum {
FLAGS_TO = 1 << 0,
};
static const struct option rawsnat_tg_opts[] = {
{.name = "to-source", .has_arg = true, .val = 't'},
{},
};
static void rawsnat_tg_help(void)
{
printf(
"RAWSNAT target options:\n"
" --to-source addr[/mask] Address or network to map to\n"
);
}
static int
rawsnat_tg4_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_rawnat_tginfo *info = (void *)(*target)->data;
struct in_addr *a;
unsigned int mask;
char *end;
switch (c) {
case 't':
info->mask = 32;
end = strchr(optarg, '/');
if (end != NULL) {
*end++ = '\0';
if (!xtables_strtoui(end, NULL, &mask, 0, 32))
xtables_param_act(XTF_BAD_VALUE, "RAWSNAT",
"--to-source", optarg);
info->mask = mask;
}
a = xtables_numeric_to_ipaddr(optarg);
if (a == NULL)
xtables_param_act(XTF_BAD_VALUE, "RAWSNAT",
"--to-source", optarg);
memcpy(&info->addr.in, a, sizeof(*a));
*flags |= FLAGS_TO;
return true;
}
return false;
}
static int
rawsnat_tg6_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_rawnat_tginfo *info = (void *)(*target)->data;
struct in6_addr *a;
unsigned int mask;
char *end;
switch (c) {
case 't':
info->mask = 128;
end = strchr(optarg, '/');
if (end != NULL) {
*end++ = '\0';
if (!xtables_strtoui(end, NULL, &mask, 0, 128))
xtables_param_act(XTF_BAD_VALUE, "RAWSNAT",
"--to-source", optarg);
info->mask = mask;
}
a = xtables_numeric_to_ip6addr(optarg);
if (a == NULL)
xtables_param_act(XTF_BAD_VALUE, "RAWSNAT",
"--to-source", optarg);
memcpy(&info->addr.in6, a, sizeof(*a));
*flags |= FLAGS_TO;
return true;
}
return false;
}
static void rawsnat_tg_check(unsigned int flags)
{
if (!(flags & FLAGS_TO))
xtables_error(PARAMETER_PROBLEM, "RAWSNAT: "
"\"--to-source\" is required.");
}
static void
rawsnat_tg4_print(const void *entry, const struct xt_entry_target *target,
int numeric)
{
const struct xt_rawnat_tginfo *info = (const void *)target->data;
if (!numeric && info->mask == 32)
printf(" to-source %s ",
xtables_ipaddr_to_anyname(&info->addr.in));
else
printf(" to-source %s/%u ",
xtables_ipaddr_to_numeric(&info->addr.in), info->mask);
}
static void
rawsnat_tg6_print(const void *entry, const struct xt_entry_target *target,
int numeric)
{
const struct xt_rawnat_tginfo *info = (const void *)target->data;
if (!numeric && info->mask == 128)
printf(" to-source %s ",
xtables_ip6addr_to_anyname(&info->addr.in6));
else
printf(" to-source %s/%u ",
xtables_ip6addr_to_numeric(&info->addr.in6), info->mask);
}
static void
rawsnat_tg4_save(const void *entry, const struct xt_entry_target *target)
{
const struct xt_rawnat_tginfo *info = (const void *)target->data;
printf(" --to-source %s/%u ",
xtables_ipaddr_to_numeric(&info->addr.in),
info->mask);
}
static void
rawsnat_tg6_save(const void *entry, const struct xt_entry_target *target)
{
const struct xt_rawnat_tginfo *info = (const void *)target->data;
printf(" --to-source %s/%u ",
xtables_ip6addr_to_numeric(&info->addr.in6),
info->mask);
}
static struct xtables_target rawsnat_tg_reg[] = {
{
.version = XTABLES_VERSION,
.name = "RAWSNAT",
.revision = 0,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_rawnat_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_rawnat_tginfo)),
.help = rawsnat_tg_help,
.parse = rawsnat_tg4_parse,
.final_check = rawsnat_tg_check,
.print = rawsnat_tg4_print,
.save = rawsnat_tg4_save,
.extra_opts = rawsnat_tg_opts,
},
{
.version = XTABLES_VERSION,
.name = "RAWSNAT",
.revision = 0,
.family = NFPROTO_IPV6,
.size = XT_ALIGN(sizeof(struct xt_rawnat_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_rawnat_tginfo)),
.help = rawsnat_tg_help,
.parse = rawsnat_tg6_parse,
.final_check = rawsnat_tg_check,
.print = rawsnat_tg6_print,
.save = rawsnat_tg6_save,
.extra_opts = rawsnat_tg_opts,
},
};
static void _init(void)
{
xtables_register_targets(rawsnat_tg_reg,
sizeof(rawsnat_tg_reg) / sizeof(*rawsnat_tg_reg));
}

View File

@@ -0,0 +1,38 @@
The \fBRAWSNAT\fR and \fBRAWDNAT\fP targets provide stateless network address
translation.
.PP
The \fBRAWSNAT\fR target will rewrite the source address in the IP header, much
like the \fBNETMAP\fP target. \fBRAWSNAT\fP (and \fBRAWDNAT\fP) may only be
used in the \fBraw\fP or \fBrawpost\fP tables, but can be used in all chains,
which makes it possible to change the source address either when the packet
enters the machine or when it leaves it. The reason for this table constraint
is that RAWNAT must happen outside of connection tracking.
.TP
\fB\-\-to\-source\fR \fIaddr\fR[\fB/\fR\fImask\fR]
Network address to map to. The resulting address will be constructed the
following way: All 'one' bits in the \fImask\fR are filled in from the new
\fIaddress\fR. All bits that are zero in the mask are filled in from the
original address.
.PP
As an example, changing the destination for packets forwarded from an internal
LAN to the internet:
.IP
\-t raw \-A PREROUTING \-i lan0 \-d 212.201.100.135 \-j RAWDNAT \-\-to\-destination 199.181.132.250;
\-t rawpost \-A POSTROUTING \-o lan0 \-s 199.181.132.250 \-j RAWSNAT \-\-to\-source 212.201.100.135;
.PP
Note that changing addresses may influence the route selection! Specifically,
it statically NATs packets, not connections, like the normal DNAT/SNAT targets
would do. Also note that it can transform already-NATed connections \(em as
said, it is completely external to Netfilter's connection tracking/NAT.
.PP
If the machine itself generates packets that are to be rawnat'ed, you need a
rule in the OUTPUT chain instead, just like you would with the stateful NAT
targets.
.PP
It may be necessary that in doing so, you also need an extra RAWSNAT rule, to
override the automatic source address selection that the routing code does
before passing packets to iptables. If the connecting socket has not been
explicitly bound to an address, as is the common mode of operation, the address
that will be chosen is the primary address of the device through which the
packet would be routed with its initial destination address - the address as
seen before any RAWNAT takes place.

32
extensions/libxt_STEAL.c Normal file
View File

@@ -0,0 +1,32 @@
#include <stdio.h>
#include <xtables.h>
#include "compat_user.h"
static void steal_tg_help(void)
{
printf("STEAL takes no options\n\n");
}
static int steal_tg_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
return 0;
}
static void steal_tg_check(unsigned int flags)
{
}
static struct xtables_target steal_tg_reg = {
.version = XTABLES_VERSION,
.name = "STEAL",
.family = NFPROTO_UNSPEC,
.help = steal_tg_help,
.parse = steal_tg_parse,
.final_check = steal_tg_check,
};
static void _init(void)
{
xtables_register_target(&steal_tg_reg);
}

View File

@@ -0,0 +1,2 @@
Like the DROP target, but does not throw an error like DROP when used in the
\fBOUTPUT\fP chain.

View File

@@ -1,4 +1,3 @@
.PP
The SYSRQ target allows to remotely trigger sysrq on the local machine over the The SYSRQ target allows to remotely trigger sysrq on the local machine over the
network. This can be useful when vital parts of the machine hang, for example network. This can be useful when vital parts of the machine hang, for example
an oops in a filesystem causing locks to be not released and processes to get an oops in a filesystem causing locks to be not released and processes to get

View File

@@ -67,6 +67,24 @@ static void tarpit_tg_check(unsigned int flags)
"TARPIT: only one action can be used at a time"); "TARPIT: only one action can be used at a time");
} }
static void tarpit_tg_print(const void *ip,
const struct xt_entry_target *target, int numeric)
{
const struct xt_tarpit_tginfo *info = (void *)target->data;
switch (info->variant) {
case XTTARPIT_HONEYPOT:
printf(" honeypot mode ");
break;
case XTTARPIT_RESET:
printf(" reset mode ");
break;
default:
printf(" tarpit mode ");
break;
}
}
static void tarpit_tg_save(const void *ip, static void tarpit_tg_save(const void *ip,
const struct xt_entry_target *target) const struct xt_entry_target *target)
{ {
@@ -85,13 +103,6 @@ static void tarpit_tg_save(const void *ip,
} }
} }
static void tarpit_tg_print(const void *ip,
const struct xt_entry_target *target, int numeric)
{
printf(" -j TARPIT");
tarpit_tg_save(ip, target);
}
static struct xtables_target tarpit_tg_reg = { static struct xtables_target tarpit_tg_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "TARPIT", .name = "TARPIT",

View File

@@ -1,4 +1,3 @@
.PP
Captures and holds incoming TCP connections using no local per-connection Captures and holds incoming TCP connections using no local per-connection
resources. resources.
.PP .PP

View File

@@ -62,6 +62,15 @@ static void condition_check(unsigned int flags)
"Condition match: must specify --condition"); "Condition match: must specify --condition");
} }
static void condition_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_condition_mtinfo *info = (const void *)match->data;
printf(" condition %s%s ", (info->invert) ? "!" : "", info->name);
}
static void condition_save(const void *ip, const struct xt_entry_match *match) static void condition_save(const void *ip, const struct xt_entry_match *match)
{ {
const struct xt_condition_mtinfo *info = (const void *)match->data; const struct xt_condition_mtinfo *info = (const void *)match->data;
@@ -69,13 +78,6 @@ static void condition_save(const void *ip, const struct xt_entry_match *match)
printf("%s --condition \"%s\" ", info->invert ? " !" : "", info->name); printf("%s --condition \"%s\" ", info->invert ? " !" : "", info->name);
} }
static void condition_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
printf(" -m condition");
condition_save(ip, match);
}
static struct xtables_match condition_mt_reg = { static struct xtables_match condition_mt_reg = {
.name = "condition", .name = "condition",
.revision = 1, .revision = 1,

View File

@@ -1,4 +1,3 @@
.PP
This matches if a specific condition variable is (un)set. This matches if a specific condition variable is (un)set.
.TP .TP
[\fB!\fP] \fB\-\-condition\fP \fIname\fP [\fB!\fP] \fB\-\-condition\fP \fIname\fP

View File

@@ -62,6 +62,15 @@ static void dhcpmac_mt_check(unsigned int flags)
"--mac parameter required"); "--mac parameter required");
} }
static void dhcpmac_mt_print(const void *ip,
const struct xt_entry_match *match, int numeric)
{
const struct dhcpmac_info *info = (void *)match->data;
printf(" dhcpmac %s" DH_MAC_FMT "/%u ",
info->invert ? "!" : "", DH_MAC_HEX(info->addr), info->mask);
}
static void dhcpmac_mt_save(const void *ip, static void dhcpmac_mt_save(const void *ip,
const struct xt_entry_match *match) const struct xt_entry_match *match)
{ {
@@ -73,13 +82,6 @@ static void dhcpmac_mt_save(const void *ip,
DH_MAC_HEX(info->addr), info->mask); DH_MAC_HEX(info->addr), info->mask);
} }
static void dhcpmac_mt_print(const void *ip,
const struct xt_entry_match *match, int numeric)
{
printf(" -m dhcpmac");
dhcpmac_mt_save(ip, match);
}
static struct xtables_match dhcpmac_mt_reg = { static struct xtables_match dhcpmac_mt_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "dhcpmac", .name = "dhcpmac",

View File

@@ -83,6 +83,15 @@ static void fuzzy_mt_check(unsigned int flags)
{ {
} }
static void fuzzy_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_fuzzy_mtinfo *info = (const void *)match->data;
printf(" fuzzy: lower limit = %u pps - upper limit = %u pps ",
info->minimum_rate, info->maximum_rate);
}
static void fuzzy_mt_save(const void *ip, const struct xt_entry_match *match) static void fuzzy_mt_save(const void *ip, const struct xt_entry_match *match)
{ {
const struct xt_fuzzy_mtinfo *info = (const void *)match->data; const struct xt_fuzzy_mtinfo *info = (const void *)match->data;
@@ -91,13 +100,6 @@ static void fuzzy_mt_save(const void *ip, const struct xt_entry_match *match)
printf(" --upper-limit %u ", info->maximum_rate); printf(" --upper-limit %u ", info->maximum_rate);
} }
static void fuzzy_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
printf(" -m fuzzy");
fuzzy_mt_save(ip, match);
}
static struct xtables_match fuzzy_mt_reg = { static struct xtables_match fuzzy_mt_reg = {
.name = "fuzzy", .name = "fuzzy",
.revision = 1, .revision = 1,

View File

@@ -1,4 +1,3 @@
.PP
This module matches a rate limit based on a fuzzy logic controller (FLC). This module matches a rate limit based on a fuzzy logic controller (FLC).
.TP .TP
\fB\-\-lower\-limit\fP \fInumber\fP \fB\-\-lower\-limit\fP \fInumber\fP

View File

@@ -49,44 +49,30 @@ static struct option geoip_opts[] = {
{NULL}, {NULL},
}; };
#if __BYTE_ORDER == __LITTLE_ENDIAN
static void geoip_swap_le32(uint32_t *buf)
{
unsigned char *p = (void *)buf;
uint32_t n = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
p[0] = (n >> 24) & 0xff;
p[1] = (n >> 16) & 0xff;
p[2] = (n >> 8) & 0xff;
p[3] = n & 0xff;
}
static void geoip_swap_in6(struct in6_addr *in6)
{
geoip_swap_le32(&in6->s6_addr32[0]);
geoip_swap_le32(&in6->s6_addr32[1]);
geoip_swap_le32(&in6->s6_addr32[2]);
geoip_swap_le32(&in6->s6_addr32[3]);
}
#endif
static void * static void *
geoip_get_subnets(const char *code, uint32_t *count, uint8_t nfproto) geoip_get_subnets(const char *code, uint32_t *count, uint8_t nfproto)
{ {
void *subnets; void *subnets;
struct stat sb; struct stat sb;
char buf[256]; char buf[256];
#if __BYTE_ORDER == __LITTLE_ENDIAN int fd;
unsigned int n;
#endif
/* Use simple integer vector files */ /* Use simple integer vector files */
if (nfproto == NFPROTO_IPV6) if (nfproto == NFPROTO_IPV6) {
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/%s.iv6", code); #if __BYTE_ORDER == _BIG_ENDIAN
else snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/BE/%s.iv6", code);
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/%s.iv4", code); #else
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/LE/%s.iv6", code);
#endif
} else {
#if __BYTE_ORDER == _BIG_ENDIAN
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/BE/%s.iv4", code);
#else
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/LE/%s.iv4", code);
#endif
}
int fd = open(buf, O_RDONLY); if ((fd = open(buf, O_RDONLY)) < 0) {
if (fd < 0) {
fprintf(stderr, "Could not open %s: %s\n", buf, strerror(errno)); fprintf(stderr, "Could not open %s: %s\n", buf, strerror(errno));
xtables_error(OTHER_PROBLEM, "Could not read geoip database"); xtables_error(OTHER_PROBLEM, "Could not read geoip database");
} }
@@ -112,25 +98,6 @@ geoip_get_subnets(const char *code, uint32_t *count, uint8_t nfproto)
xtables_error(OTHER_PROBLEM, "geoip: insufficient memory"); xtables_error(OTHER_PROBLEM, "geoip: insufficient memory");
read(fd, subnets, sb.st_size); read(fd, subnets, sb.st_size);
close(fd); close(fd);
#if __BYTE_ORDER == __LITTLE_ENDIAN
for (n = 0; n < *count; ++n) {
switch (nfproto) {
case NFPROTO_IPV6: {
struct geoip_subnet6 *gs6 = &(((struct geoip_subnet6 *)subnets)[n]);
geoip_swap_in6(&gs6->begin);
geoip_swap_in6(&gs6->end);
break;
}
case NFPROTO_IPV4: {
struct geoip_subnet4 *gs4 = &(((struct geoip_subnet4 *)subnets)[n]);
geoip_swap_le32(&gs4->begin);
geoip_swap_le32(&gs4->end);
break;
}
}
}
#endif
return subnets; return subnets;
} }
@@ -203,8 +170,7 @@ static unsigned int parse_geoip_cc(const char *ccstr, uint16_t *cc,
next = strchr(cp, ','); next = strchr(cp, ',');
if (next) *next++ = '\0'; if (next) *next++ = '\0';
cctmp = check_geoip_cc(cp, cc, count); if ((cctmp = check_geoip_cc(cp, cc, count)) != 0) {
if (cctmp != 0) {
if ((mem[count++].user = if ((mem[count++].user =
(unsigned long)geoip_load_cc(cp, cctmp, nfproto)) == 0) (unsigned long)geoip_load_cc(cp, cctmp, nfproto)) == 0)
xtables_error(OTHER_PROBLEM, xtables_error(OTHER_PROBLEM,
@@ -285,6 +251,31 @@ geoip_final_check(unsigned int flags)
"geoip: missing arguments"); "geoip: missing arguments");
} }
static void
geoip_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_geoip_match_info *info = (void*)match->data;
u_int8_t i;
if (info->flags & XT_GEOIP_SRC)
printf(" Source ");
else
printf(" Destination ");
if (info->count > 1)
printf("countries: ");
else
printf("country: ");
if (info->flags & XT_GEOIP_INV)
printf("! ");
for (i = 0; i < info->count; i++)
printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i]));
printf(" ");
}
static void static void
geoip_save(const void *ip, const struct xt_entry_match *match) geoip_save(const void *ip, const struct xt_entry_match *match)
{ {
@@ -304,13 +295,6 @@ geoip_save(const void *ip, const struct xt_entry_match *match)
printf(" "); printf(" ");
} }
static void
geoip_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
printf(" -m geoip");
geoip_save(ip, match);
}
static struct xtables_match geoip_match[] = { static struct xtables_match geoip_match[] = {
{ {
.family = NFPROTO_IPV6, .family = NFPROTO_IPV6,

View File

@@ -1,4 +1,3 @@
.PP
Match a packet by its source or destination country. Match a packet by its source or destination country.
.TP .TP
[\fB!\fP] \fB\-\-src\-cc\fP, \fB\-\-source\-country\fP \fIcountry\fP[\fB,\fP\fIcountry\fP\fB...\fP] [\fB!\fP] \fB\-\-src\-cc\fP, \fB\-\-source\-country\fP \fIcountry\fP[\fB,\fP\fIcountry\fP\fB...\fP]
@@ -16,8 +15,8 @@ with the source package, and which should be available in compiled packages in
/usr/lib(exec)/xtables-addons/. The first command retrieves CSV files from /usr/lib(exec)/xtables-addons/. The first command retrieves CSV files from
MaxMind, while the other two build packed bisectable range files: MaxMind, while the other two build packed bisectable range files:
.PP .PP
mkdir \-p /usr/share/xt_geoip; cd /tmp; $path/to/xt_geoip_dl; mkdir -p /usr/share/xt_geoip; cd /tmp; $path/to/xt_geoip_dl;
.PP .PP
$path/to/xt_geoip_build \-D /usr/share/xt_geoip GeoIP*.csv; $path/to/xt_geoip_build -D /usr/share/xt_geoip GeoIP*.csv;
.PP .PP
The shared library is hardcoded to look in these paths, so use them. The shared library is hardcoded to look in these paths, so use them.

View File

@@ -57,6 +57,17 @@ static void gradm_mt_check(unsigned int flags)
{ {
} }
static void gradm_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_gradm_mtinfo *info = (const void *)match->data;
if (info->invflags)
printf("gradm: disabled");
else
printf("gradm: enabled");
}
static void gradm_mt_save(const void *ip, const struct xt_entry_match *match) static void gradm_mt_save(const void *ip, const struct xt_entry_match *match)
{ {
const struct xt_gradm_mtinfo *info = (const void *)match->data; const struct xt_gradm_mtinfo *info = (const void *)match->data;
@@ -67,13 +78,6 @@ static void gradm_mt_save(const void *ip, const struct xt_entry_match *match)
printf(" --enabled "); printf(" --enabled ");
} }
static void gradm_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
printf(" -m gradm");
gradm_mt_save(ip, match);
}
static struct xtables_match gradm_mt_reg = { static struct xtables_match gradm_mt_reg = {
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.name = "gradm", .name = "gradm",

View File

@@ -1,4 +1,3 @@
.PP
This module matches packets based on grsecurity RBAC status. This module matches packets based on grsecurity RBAC status.
.TP .TP
[\fB!\fP] \fB\-\-enabled\fP [\fB!\fP] \fB\-\-enabled\fP

View File

@@ -175,6 +175,33 @@ static void iface_mt_check(unsigned int flags)
"iface: You must specify at least one option"); "iface: You must specify at least one option");
} }
static void iface_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_iface_mtinfo *info = (const void *)match->data;
printf(" iface: ");
if (info->flags & XT_IFACE_DEV_IN)
printf("(in)");
else if (info->flags & XT_IFACE_DEV_OUT)
printf("(out)");
else
printf("%s", info->ifname);
printf(" [state:");
iface_print_opt(info, XT_IFACE_UP, "up");
iface_print_opt(info, XT_IFACE_BROADCAST, "broadcast");
iface_print_opt(info, XT_IFACE_LOOPBACK, "loopback");
iface_print_opt(info, XT_IFACE_POINTOPOINT, "pointopoint");
iface_print_opt(info, XT_IFACE_RUNNING, "running");
iface_print_opt(info, XT_IFACE_NOARP, "noarp");
iface_print_opt(info, XT_IFACE_PROMISC, "promisc");
iface_print_opt(info, XT_IFACE_MULTICAST, "multicast");
iface_print_opt(info, XT_IFACE_DYNAMIC, "dynamic");
iface_print_opt(info, XT_IFACE_LOWER_UP, "lower_up");
iface_print_opt(info, XT_IFACE_DORMANT, "dormant");
printf("] ");
}
static void iface_mt_save(const void *ip, const struct xt_entry_match *match) static void iface_mt_save(const void *ip, const struct xt_entry_match *match)
{ {
const struct xt_iface_mtinfo *info = (const void *)match->data; const struct xt_iface_mtinfo *info = (const void *)match->data;
@@ -199,13 +226,6 @@ static void iface_mt_save(const void *ip, const struct xt_entry_match *match)
printf(" "); printf(" ");
} }
static void iface_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
printf(" -m iface");
iface_mt_save(ip, match);
}
static struct xtables_match iface_mt_reg = { static struct xtables_match iface_mt_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "iface", .name = "iface",

View File

@@ -1,4 +1,3 @@
.PP
Allows you to check interface states. First, an interface needs to be selected Allows you to check interface states. First, an interface needs to be selected
for comparison. Exactly one option of the following three must be specified: for comparison. Exactly one option of the following three must be specified:
.TP .TP

254
extensions/libxt_ipaddr.c Normal file
View File

@@ -0,0 +1,254 @@
/*
* "ipaddr" demo match extension for iptables
* written by Jan Engelhardt <jengelh [at] medozas de>, 2008 - 2009
* released in the Public Domain
*/
#include <arpa/inet.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter.h>
#include "xt_ipaddr.h"
static const struct option ipaddr_mt_opts[] = {
{.name = "ipsrc", .has_arg = true, .val = '1'},
{.name = "ipdst", .has_arg = true, .val = '2'},
{NULL},
};
static void ipaddr_mt_help(void)
{
printf(
"ipaddr match options:\n"
"[!] --ipsrc addr Match source address of packet\n"
"[!] --ipdst addr Match destination address of packet\n"
);
}
static void ipaddr_mt_init(struct xt_entry_match *match)
{
struct xt_ipaddr_mtinfo *info = (void *)match->data;
inet_pton(PF_INET, "192.0.2.137", &info->dst.in);
}
static int ipaddr_mt4_parse(int c, char **argv, int invert,
unsigned int *flags, const void *entry, struct xt_entry_match **match)
{
struct xt_ipaddr_mtinfo *info = (void *)(*match)->data;
struct in_addr *addrs, mask;
unsigned int naddrs;
switch (c) {
case '1': /* --ipsrc */
if (*flags & XT_IPADDR_SRC)
xtables_error(PARAMETER_PROBLEM, "xt_ipaddr: "
"Only use \"--ipsrc\" once!");
*flags |= XT_IPADDR_SRC;
info->flags |= XT_IPADDR_SRC;
if (invert)
info->flags |= XT_IPADDR_SRC_INV;
xtables_ipparse_any(optarg, &addrs, &mask, &naddrs);
if (naddrs != 1)
xtables_error(PARAMETER_PROBLEM,
"%s does not resolve to exactly "
"one address", optarg);
memcpy(&info->src.in, addrs, sizeof(*addrs));
return true;
case '2': /* --ipdst */
if (*flags & XT_IPADDR_DST)
xtables_error(PARAMETER_PROBLEM, "xt_ipaddr: "
"Only use \"--ipdst\" once!");
*flags |= XT_IPADDR_DST;
info->flags |= XT_IPADDR_DST;
if (invert)
info->flags |= XT_IPADDR_DST_INV;
addrs = xtables_numeric_to_ipaddr(optarg);
if (addrs == NULL)
xtables_error(PARAMETER_PROBLEM, "xt_ipaddr: "
"Parse error at %s\n", optarg);
memcpy(&info->dst.in, addrs, sizeof(*addrs));
return true;
}
return false;
}
static int ipaddr_mt6_parse(int c, char **argv, int invert,
unsigned int *flags, const void *entry, struct xt_entry_match **match)
{
struct xt_ipaddr_mtinfo *info = (void *)(*match)->data;
struct in6_addr *addrs;
switch (c) {
case '1': /* --ipsrc */
if (*flags & XT_IPADDR_SRC)
xtables_error(PARAMETER_PROBLEM, "xt_ipaddr: "
"Only use \"--ipsrc\" once!");
*flags |= XT_IPADDR_SRC;
info->flags |= XT_IPADDR_SRC;
if (invert)
info->flags |= XT_IPADDR_SRC_INV;
addrs = xtables_numeric_to_ip6addr(optarg);
if (addrs == NULL)
xtables_error(PARAMETER_PROBLEM, "xt_ipaddr: "
"Parse error at %s", optarg);
memcpy(&info->src.in6, addrs, sizeof(*addrs));
return true;
case '2': /* --ipdst */
if (*flags & XT_IPADDR_DST)
xtables_error(PARAMETER_PROBLEM, "xt_ipaddr: "
"Only use \"--ipdst\" once!");
*flags |= XT_IPADDR_DST;
info->flags |= XT_IPADDR_DST;
if (invert)
info->flags |= XT_IPADDR_DST_INV;
addrs = xtables_numeric_to_ip6addr(optarg);
if (addrs == NULL)
xtables_error(PARAMETER_PROBLEM, "xt_ipaddr: "
"Parse error at %s", optarg);
memcpy(&info->dst.in6, addrs, sizeof(*addrs));
return true;
}
return false;
}
static void ipaddr_mt_check(unsigned int flags)
{
if (flags == 0)
xtables_error(PARAMETER_PROBLEM, "xt_ipaddr: You need to "
"specify at least \"--ipsrc\" or \"--ipdst\".");
}
static void ipaddr_mt4_print(const void *entry,
const struct xt_entry_match *match, int numeric)
{
const struct xt_ipaddr_mtinfo *info = (const void *)match->data;
if (info->flags & XT_IPADDR_SRC) {
printf("src IP ");
if (info->flags & XT_IPADDR_SRC_INV)
printf("! ");
printf("%s ", numeric ?
xtables_ipaddr_to_numeric(&info->src.in) :
xtables_ipaddr_to_anyname(&info->src.in));
}
if (info->flags & XT_IPADDR_DST) {
printf("dst IP ");
if (info->flags & XT_IPADDR_DST_INV)
printf("! ");
printf("%s ", numeric ?
xtables_ipaddr_to_numeric(&info->dst.in) :
xtables_ipaddr_to_anyname(&info->dst.in));
}
}
static void ipaddr_mt6_print(const void *entry,
const struct xt_entry_match *match, int numeric)
{
const struct xt_ipaddr_mtinfo *info = (const void *)match->data;
if (info->flags & XT_IPADDR_SRC) {
printf("src IP ");
if (info->flags & XT_IPADDR_SRC_INV)
printf("! ");
printf("%s ", numeric ?
xtables_ip6addr_to_numeric(&info->src.in6) :
xtables_ip6addr_to_anyname(&info->src.in6));
}
if (info->flags & XT_IPADDR_DST) {
printf("dst IP ");
if (info->flags & XT_IPADDR_DST_INV)
printf("! ");
printf("%s ", numeric ?
xtables_ip6addr_to_numeric(&info->dst.in6) :
xtables_ip6addr_to_anyname(&info->dst.in6));
}
}
static void ipaddr_mt4_save(const void *entry,
const struct xt_entry_match *match)
{
const struct xt_ipaddr_mtinfo *info = (const void *)match->data;
if (info->flags & XT_IPADDR_SRC) {
if (info->flags & XT_IPADDR_SRC_INV)
printf("! ");
printf("--ipsrc %s ",
xtables_ipaddr_to_numeric(&info->src.in));
}
if (info->flags & XT_IPADDR_DST) {
if (info->flags & XT_IPADDR_DST_INV)
printf("! ");
printf("--ipdst %s ",
xtables_ipaddr_to_numeric(&info->dst.in));
}
}
static void ipaddr_mt6_save(const void *entry,
const struct xt_entry_match *match)
{
const struct xt_ipaddr_mtinfo *info = (const void *)match->data;
if (info->flags & XT_IPADDR_SRC) {
if (info->flags & XT_IPADDR_SRC_INV)
printf("! ");
printf("--ipsrc %s ",
xtables_ip6addr_to_numeric(&info->src.in6));
}
if (info->flags & XT_IPADDR_DST) {
if (info->flags & XT_IPADDR_DST_INV)
printf("! ");
printf("--ipdst %s ",
xtables_ip6addr_to_numeric(&info->dst.in6));
}
}
static struct xtables_match ipaddr_mt_reg = {
.version = XTABLES_VERSION,
.name = "ipaddr",
.revision = 0,
.family = PF_INET,
.size = XT_ALIGN(sizeof(struct xt_ipaddr_mtinfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_ipaddr_mtinfo)),
.help = ipaddr_mt_help,
.init = ipaddr_mt_init,
.parse = ipaddr_mt4_parse,
.final_check = ipaddr_mt_check,
.print = ipaddr_mt4_print,
.save = ipaddr_mt4_save,
.extra_opts = ipaddr_mt_opts,
};
static struct xtables_match ipaddr_mt6_reg = {
.version = XTABLES_VERSION,
.name = "ipaddr",
.revision = 0,
.family = PF_INET6,
.size = XT_ALIGN(sizeof(struct xt_ipaddr_mtinfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_ipaddr_mtinfo)),
.help = ipaddr_mt_help,
.init = ipaddr_mt_init,
.parse = ipaddr_mt6_parse,
.final_check = ipaddr_mt_check,
.print = ipaddr_mt6_print,
.save = ipaddr_mt6_save,
.extra_opts = ipaddr_mt_opts,
};
static void _init(void)
{
xtables_register_match(&ipaddr_mt_reg);
xtables_register_match(&ipaddr_mt6_reg);
}

View File

@@ -0,0 +1,9 @@
.TP
[\fB!\fP] \fB--ipsrc\fP \fIaddr\fP
Match packets that have \fIaddr\fP as source address.
.TP
[\fB!\fP] \fB--ipdst\fP \fIaddr\fP
Match packets that have \fIaddr\fP as destination address.
.PP
The ipaddr module serves only as a demonstration. It is equivalent to the
iptables \fB-s\fP and \fB-d\fP options, but ipaddr does not support masks.

View File

@@ -217,7 +217,7 @@ ipp2p_mt_print1(const void *entry, const struct xt_entry_match *match,
static void ipp2p_mt_print(const void *entry, static void ipp2p_mt_print(const void *entry,
const struct xt_entry_match *match, int numeric) const struct xt_entry_match *match, int numeric)
{ {
printf(" -m ipp2p "); printf(" ipp2p ");
ipp2p_mt_print1(entry, match, true); ipp2p_mt_print1(entry, match, true);
} }

View File

@@ -1,4 +1,3 @@
.PP
This module matches certain packets in P2P flows. It is not This module matches certain packets in P2P flows. It is not
designed to match all packets belonging to a P2P connection \(em designed to match all packets belonging to a P2P connection \(em
use IPP2P together with CONNMARK for this purpose. use IPP2P together with CONNMARK for this purpose.

View File

@@ -133,6 +133,17 @@ static void ipv4options_print_flags(const struct xt_ipv4options_mtinfo1 *info,
} }
} }
static void ipv4options_mt_print(const void *ip,
const struct xt_entry_match *match, int numeric)
{
const struct xt_ipv4options_mtinfo1 *info = (void *)match->data;
printf(" ipv4options %s ",
(info->flags & XT_V4OPTS_ANY) ? "any-of" : "all-of");
ipv4options_print_flags(info, numeric);
printf(" ");
}
static void ipv4options_mt_save(const void *ip, static void ipv4options_mt_save(const void *ip,
const struct xt_entry_match *match) const struct xt_entry_match *match)
{ {
@@ -147,13 +158,6 @@ static void ipv4options_mt_save(const void *ip,
printf(" "); printf(" ");
} }
static void ipv4options_mt_print(const void *ip,
const struct xt_entry_match *match, int numeric)
{
printf(" -m ipv4options");
ipv4options_mt_save(ip, match);
}
static struct xtables_match ipv4options_mt_reg = { static struct xtables_match ipv4options_mt_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "ipv4options", .name = "ipv4options",

View File

@@ -1,4 +1,3 @@
.PP
The "ipv4options" module allows to match against a set of IPv4 header options. The "ipv4options" module allows to match against a set of IPv4 header options.
.TP .TP
\fB\-\-flags\fP [\fB!\fP]\fIsymbol\fP[\fB,\fP[\fB!\fP]\fIsymbol...\fP] \fB\-\-flags\fP [\fB!\fP]\fIsymbol\fP[\fB,\fP[\fB!\fP]\fIsymbol...\fP]

View File

@@ -107,6 +107,29 @@ static void length_mt_check(unsigned int flags)
"--layer3. Consider specifying it explicitly.\n"); "--layer3. Consider specifying it explicitly.\n");
} }
static void length_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_length_mtinfo2 *info = (const void *)match->data;
if (info->flags & XT_LENGTH_LAYER3)
printf(" layer3 ");
else if (info->flags & XT_LENGTH_LAYER4)
printf(" layer4 ");
else if (info->flags & XT_LENGTH_LAYER5)
printf(" layer5 ");
else if (info->flags & XT_LENGTH_LAYER7)
printf(" layer7 ");
printf(" length ");
if (info->flags & XT_LENGTH_INVERT)
printf("! ");
if (info->min == info->max)
printf("%u ", (unsigned int)info->min);
else
printf("%u-%u ", (unsigned int)info->min,
(unsigned int)info->max);
}
static void length_mt_save(const void *ip, const struct xt_entry_match *match) static void length_mt_save(const void *ip, const struct xt_entry_match *match)
{ {
const struct xt_length_mtinfo2 *info = (const void *)match->data; const struct xt_length_mtinfo2 *info = (const void *)match->data;
@@ -129,13 +152,6 @@ static void length_mt_save(const void *ip, const struct xt_entry_match *match)
(unsigned int)info->max); (unsigned int)info->max);
} }
static void length_mt_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
printf(" -m length2");
length_mt_save(ip, match);
}
static struct xtables_match length2_mt_reg = { static struct xtables_match length2_mt_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "length2", .name = "length2",

View File

@@ -1,4 +1,3 @@
.PP
This module matches the length of a packet against a specific value or range of This module matches the length of a packet against a specific value or range of
values. values.
.TP .TP

View File

@@ -24,7 +24,6 @@ static const struct option lscan_mt_opts[] = {
{.name = "synscan", .has_arg = false, .val = 's'}, {.name = "synscan", .has_arg = false, .val = 's'},
{.name = "cnscan", .has_arg = false, .val = 'c'}, {.name = "cnscan", .has_arg = false, .val = 'c'},
{.name = "grscan", .has_arg = false, .val = 'g'}, {.name = "grscan", .has_arg = false, .val = 'g'},
{.name = "mirai", .has_arg = false, .val = 'm'},
{NULL}, {NULL},
}; };
@@ -36,8 +35,7 @@ static void lscan_mt_help(void)
" --stealth Match TCP Stealth packets\n" " --stealth Match TCP Stealth packets\n"
" --synscan Match TCP SYN scans\n" " --synscan Match TCP SYN scans\n"
" --cnscan Match TCP Connect scans\n" " --cnscan Match TCP Connect scans\n"
" --grscan Match Banner Grabbing scans\n" " --grscan Match Banner Grabbing scans\n");
" --mirai Match TCP scan with ISN = dest. IP\n");
} }
static int lscan_mt_parse(int c, char **argv, int invert, static int lscan_mt_parse(int c, char **argv, int invert,
@@ -47,19 +45,16 @@ static int lscan_mt_parse(int c, char **argv, int invert,
switch (c) { switch (c) {
case 'c': case 'c':
info->match_fl3 |= LSCAN_FL3_CN; info->match_cn = true;
return true; return true;
case 'g': case 'g':
info->match_fl4 |= LSCAN_FL4_GR; info->match_gr = true;
return true;
case 'm':
info->match_fl1 |= LSCAN_FL1_MIRAI;
return true; return true;
case 's': case 's':
info->match_fl2 |= LSCAN_FL2_SYN; info->match_syn = true;
return true; return true;
case 'x': case 'x':
info->match_fl1 |= LSCAN_FL1_STEALTH; info->match_stealth = true;
return true; return true;
} }
return false; return false;
@@ -69,27 +64,42 @@ static void lscan_mt_check(unsigned int flags)
{ {
} }
static void lscan_mt_print(const void *ip,
const struct xt_entry_match *match, int numeric)
{
const struct xt_lscan_mtinfo *info = (const void *)(match->data);
const char *s = "";
printf(" lscan ");
if (info->match_stealth) {
printf("STEALTH");
s = ",";
}
if (info->match_syn) {
printf("%sSYNSCAN", s);
s = ",";
}
if (info->match_cn) {
printf("%sCNSCAN", s);
s = ",";
}
if (info->match_gr)
printf("%sGRSCAN", s);
printf(" ");
}
static void lscan_mt_save(const void *ip, const struct xt_entry_match *match) static void lscan_mt_save(const void *ip, const struct xt_entry_match *match)
{ {
const struct xt_lscan_mtinfo *info = (const void *)(match->data); const struct xt_lscan_mtinfo *info = (const void *)(match->data);
if (info->match_fl1 & LSCAN_FL1_STEALTH) if (info->match_stealth)
printf(" --stealth "); printf(" --stealth ");
if (info->match_fl2 & LSCAN_FL2_SYN) if (info->match_syn)
printf(" --synscan "); printf(" --synscan ");
if (info->match_fl3 & LSCAN_FL3_CN) if (info->match_cn)
printf(" --cnscan "); printf(" --cnscan ");
if (info->match_fl4 & LSCAN_FL4_GR) if (info->match_gr)
printf(" --grscan "); printf(" --grscan ");
if (info->match_fl1 & LSCAN_FL1_MIRAI)
printf(" --mirai ");
}
static void lscan_mt_print(const void *ip,
const struct xt_entry_match *match, int numeric)
{
printf(" -m lscan");
lscan_mt_save(ip, match);
} }
static struct xtables_match lscan_mt_reg = { static struct xtables_match lscan_mt_reg = {

View File

@@ -1,5 +1,4 @@
.PP Detects simple low-level scan attemps based upon the packet's contents.
Detects simple low-level scan attempts based upon the packet's contents.
(This is (This is
different from other implementations, which also try to match the rate of new different from other implementations, which also try to match the rate of new
connections.) Note that an attempt is only discovered after it has been carried connections.) Note that an attempt is only discovered after it has been carried
@@ -27,11 +26,6 @@ warranted single-direction data flows, usually bulk data transfers such as
FTP DATA connections or IRC DCC. Grab Scan Detection should only be used on FTP DATA connections or IRC DCC. Grab Scan Detection should only be used on
ports where a protocol runs that is guaranteed to do a bidirectional exchange ports where a protocol runs that is guaranteed to do a bidirectional exchange
of bytes. of bytes.
.TP
\fB\-\-mirai\fP
Match if the TCP ISN is equal to the IPv4 destination address; this is used
by the devices in the Mirai botnet as a form of TCP SYN scan, so you will
have to explicitly specify --syn for the rule.
.PP .PP
NOTE: Some clients (Windows XP for example) may do what looks like a SYN scan, NOTE: Some clients (Windows XP for example) may do what looks like a SYN scan,
so be advised to carefully use xt_lscan in conjunction with blocking rules, so be advised to carefully use xt_lscan in conjunction with blocking rules,

View File

@@ -117,6 +117,18 @@ static int psd_mt_parse(int c, char **argv, int invert, unsigned int *flags,
/* Final check; nothing. */ /* Final check; nothing. */
static void psd_mt_final_check(unsigned int flags) {} static void psd_mt_final_check(unsigned int flags) {}
/* Prints out the targinfo. */
static void psd_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_psd_info *psdinfo = (const struct xt_psd_info *)match->data;
printf(" psd ");
printf("weight-threshold: %u ", psdinfo->weight_threshold);
printf("delay-threshold: %u ", psdinfo->delay_threshold);
printf("lo-ports-weight: %u ", psdinfo->lo_ports_weight);
printf("hi-ports-weight: %u ", psdinfo->hi_ports_weight);
}
/* Saves the union ipt_targinfo in parsable form to stdout. */
static void psd_mt_save(const void *ip, const struct xt_entry_match *match) static void psd_mt_save(const void *ip, const struct xt_entry_match *match)
{ {
const struct xt_psd_info *psdinfo = (const struct xt_psd_info *)match->data; const struct xt_psd_info *psdinfo = (const struct xt_psd_info *)match->data;
@@ -126,12 +138,6 @@ static void psd_mt_save(const void *ip, const struct xt_entry_match *match)
printf("--psd-hi-ports-weight %u ", psdinfo->hi_ports_weight); printf("--psd-hi-ports-weight %u ", psdinfo->hi_ports_weight);
} }
static void psd_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
printf(" -m psd");
psd_mt_save(ip, match);
}
static struct xtables_match psd_mt_reg = { static struct xtables_match psd_mt_reg = {
.name = "psd", .name = "psd",
.version = XTABLES_VERSION, .version = XTABLES_VERSION,

View File

@@ -1,4 +1,3 @@
.PP
Attempt to detect TCP and UDP port scans. This match was derived from Attempt to detect TCP and UDP port scans. This match was derived from
Solar Designer's scanlogd. Solar Designer's scanlogd.
.TP .TP

View File

@@ -70,7 +70,7 @@ quota_mt2_parse(int c, char **argv, int invert, unsigned int *flags,
/* zero termination done on behalf of the kernel module */ /* zero termination done on behalf of the kernel module */
xtables_param_act(XTF_ONLY_ONCE, "quota", "--name", *flags & FL_NAME); xtables_param_act(XTF_ONLY_ONCE, "quota", "--name", *flags & FL_NAME);
xtables_param_act(XTF_NO_INVERT, "quota", "--name", invert); xtables_param_act(XTF_NO_INVERT, "quota", "--name", invert);
snprintf(info->name, sizeof(info->name), "%s", optarg); strncpy(info->name, optarg, sizeof(info->name));
*flags |= FL_NAME; *flags |= FL_NAME;
return true; return true;
case 'p': case 'p':
@@ -98,6 +98,8 @@ quota_mt2_save(const void *ip, const struct xt_entry_match *match)
{ {
const struct xt_quota_mtinfo2 *q = (void *)match->data; const struct xt_quota_mtinfo2 *q = (void *)match->data;
if (q->flags & XT_QUOTA_INVERT)
printf(" !");
if (q->flags & XT_QUOTA_GROW) if (q->flags & XT_QUOTA_GROW)
printf(" --grow "); printf(" --grow ");
if (q->flags & XT_QUOTA_NO_CHANGE) if (q->flags & XT_QUOTA_NO_CHANGE)
@@ -106,16 +108,29 @@ quota_mt2_save(const void *ip, const struct xt_entry_match *match)
printf(" --packets "); printf(" --packets ");
if (*q->name != '\0') if (*q->name != '\0')
printf(" --name %s ", q->name); printf(" --name %s ", q->name);
if (q->flags & XT_QUOTA_INVERT)
printf(" !");
printf(" --quota %llu ", (unsigned long long)q->quota); printf(" --quota %llu ", (unsigned long long)q->quota);
} }
static void quota_mt2_print(const void *ip, const struct xt_entry_match *match, static void quota_mt2_print(const void *ip, const struct xt_entry_match *match,
int numeric) int numeric)
{ {
printf(" -m quota"); const struct xt_quota_mtinfo2 *q = (const void *)match->data;
quota_mt2_save(ip, match);
if (q->flags & XT_QUOTA_INVERT)
printf(" !");
if (q->flags & XT_QUOTA_GROW)
printf(" counter");
else
printf(" quota");
if (*q->name != '\0')
printf(" %s:", q->name);
printf(" %llu ", (unsigned long long)q->quota);
if (q->flags & XT_QUOTA_PACKET)
printf("packets ");
else
printf("bytes ");
if (q->flags & XT_QUOTA_NO_CHANGE)
printf("(no-change mode) ");
} }
static struct xtables_match quota_mt2_reg = { static struct xtables_match quota_mt2_reg = {

View File

@@ -1,4 +1,3 @@
.PP
The "quota2" implements a named counter which can be increased or decreased The "quota2" implements a named counter which can be increased or decreased
on a per-match basis. Available modes are packet counting or byte counting. on a per-match basis. Available modes are packet counting or byte counting.
The value of the counter can be read and reset through procfs, thereby making The value of the counter can be read and reset through procfs, thereby making

View File

@@ -5,5 +5,4 @@ AM_CFLAGS = ${regular_CFLAGS} ${libxtables_CFLAGS}
include ../../Makefile.extra include ../../Makefile.extra
sbin_PROGRAMS = pknlusr noinst_PROGRAMS = pknlusr
dist_man_MANS = pknlusr.8

View File

@@ -1,17 +0,0 @@
from Crypto.Hash import SHA256
from Crypto.Hash import MD5
import sys
import hmac
import struct
import socket
from time import time
def gen_hmac(secret, ip):
epoch_mins = (long)(time()/60)
s = hmac.HMAC(secret, digestmod = SHA256)
s.update(socket.inet_aton(socket.gethostbyname(ip)))
s.update(struct.pack("i", epoch_mins)) # "i" is for integer
print s.hexdigest()
if __name__ == '__main__':
gen_hmac(sys.argv[1], sys.argv[2])

View File

@@ -1,6 +0,0 @@
#!/bin/bash
if [ "$#" -ne 4 ]; then
echo "usage: $0 <IP src> <IP dst> <PORT dst> <secret>"
exit 1
fi
python gen_hmac.py "$4" "$1" | socat - "udp-sendto:$2:$3,bind=$1"

View File

@@ -11,6 +11,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <xtables.h> #include <xtables.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter_ipv4/ip_tables.h>
@@ -69,6 +70,7 @@ parse_ports(const char *portstring, uint16_t *ports, const char *proto)
if (cp != NULL) if (cp != NULL)
xtables_error(PARAMETER_PROBLEM, "too many ports specified"); xtables_error(PARAMETER_PROBLEM, "too many ports specified");
free(buffer); free(buffer);
return i; return i;
} }
@@ -89,11 +91,12 @@ proto_to_name(uint8_t proto)
static const char * static const char *
check_proto(uint16_t pnum, uint8_t invflags) check_proto(uint16_t pnum, uint8_t invflags)
{ {
char *proto;
if (invflags & XT_INV_PROTO) if (invflags & XT_INV_PROTO)
xtables_error(PARAMETER_PROBLEM, PKNOCK "only works with TCP and UDP."); xtables_error(PARAMETER_PROBLEM, PKNOCK "only works with TCP and UDP.");
const char *proto = proto_to_name(pnum); if ((proto = proto_to_name(pnum)) != NULL)
if (proto != NULL)
return proto; return proto;
else if (pnum == 0) else if (pnum == 0)
xtables_error(PARAMETER_PROBLEM, PKNOCK "needs `-p tcp' or `-p udp'"); xtables_error(PARAMETER_PROBLEM, PKNOCK "needs `-p tcp' or `-p udp'");
@@ -120,7 +123,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags,
info->ports_count = parse_ports(optarg, info->port, proto); info->ports_count = parse_ports(optarg, info->port, proto);
info->option |= XT_PKNOCK_KNOCKPORT; info->option |= XT_PKNOCK_KNOCKPORT;
*flags |= XT_PKNOCK_KNOCKPORT; *flags |= XT_PKNOCK_KNOCKPORT;
#ifdef DEBUG #if DEBUG
printf("ports_count: %d\n", info->ports_count); printf("ports_count: %d\n", info->ports_count);
#endif #endif
break; break;
@@ -159,7 +162,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags,
info->rule_name_len = strlen(info->rule_name); info->rule_name_len = strlen(info->rule_name);
info->option |= XT_PKNOCK_NAME; info->option |= XT_PKNOCK_NAME;
*flags |= XT_PKNOCK_NAME; *flags |= XT_PKNOCK_NAME;
#ifdef DEBUG #if DEBUG
printf("info->rule_name: %s\n", info->rule_name); printf("info->rule_name: %s\n", info->rule_name);
#endif #endif
break; break;
@@ -210,6 +213,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags,
if (invert) if (invert)
xtables_error(PARAMETER_PROBLEM, PKNOCK "does not support invert."); xtables_error(PARAMETER_PROBLEM, PKNOCK "does not support invert.");
return 1; return 1;
} }

View File

@@ -15,10 +15,10 @@ modprobe xt_pknock
.PP .PP
Example 1 (TCP mode, manual closing of opened port not possible): Example 1 (TCP mode, manual closing of opened port not possible):
.IP .IP
iptables \-P INPUT DROP iptables -P INPUT DROP
.IP .IP
iptables \-A INPUT \-p tcp \-m pknock \-\-knockports 4002,4001,4004 \-\-strict iptables -A INPUT -p tcp -m pknock --knockports 4002,4001,4004 --strict
\-\-name SSH \-\-time 10 \-\-autoclose 60 \-\-dport 22 \-j ACCEPT --name SSH --time 10 --autoclose 60 --dport 22 -j ACCEPT
.PP .PP
The rule will allow tcp port 22 for the attempting IP address after the successful reception of TCP SYN packets The rule will allow tcp port 22 for the attempting IP address after the successful reception of TCP SYN packets
to ports 4002, 4001 and 4004, in this order (a.k.a. port-knocking). to ports 4002, 4001 and 4004, in this order (a.k.a. port-knocking).
@@ -33,10 +33,10 @@ Example 2 (UDP mode \(em non-replayable and non-spoofable, manual closing
of opened port possible, secure, also called "SPA" = Secure Port of opened port possible, secure, also called "SPA" = Secure Port
Authorization): Authorization):
.IP .IP
iptables \-A INPUT \-p udp \-m pknock \-\-knockports 4000 \-\-name FTP iptables -A INPUT -p udp -m pknock --knockports 4000 --name FTP
\-\-opensecret foo \-\-closesecret bar \-\-autoclose 240 \-j DROP --opensecret foo --closesecret bar --autoclose 240 -j DROP
.IP .IP
iptables \-A INPUT \-p tcp \-m pknock \-\-checkip \-\-name FTP \-\-dport 21 \-j ACCEPT iptables -A INPUT -p tcp -m pknock --checkip --name FTP --dport 21 -j ACCEPT
.PP .PP
The first rule will create an "ALLOWED" record in /proc/net/xt_pknock/FTP after The first rule will create an "ALLOWED" record in /proc/net/xt_pknock/FTP after
the successful reception of an UDP packet to port 4000. The packet payload must be the successful reception of an UDP packet to port 4000. The packet payload must be
@@ -62,6 +62,8 @@ Specifying \fB--autoclose 0\fP means that no automatic close will be performed a
xt_pknock is capable of sending information about successful matches xt_pknock is capable of sending information about successful matches
via a netlink socket to userspace, should you need to implement your own via a netlink socket to userspace, should you need to implement your own
way of receiving and handling portknock notifications. way of receiving and handling portknock notifications.
Be sure to read the documentation in the doc/pknock/ directory,
or visit the original site \(em http://portknocko.berlios.de/ .
.PP .PP
\fBTCP mode\fP: \fBTCP mode\fP:
.PP .PP

View File

@@ -1,18 +0,0 @@
.TH pknlusr 8 "2020-10-22" "xtables-addons" "xtables-addons"
.SH NAME
.PP
pknlusr \(em userspace monitor for successful xt_pknock matches
.SH Synopsis
.PP
\fBpknlusr\fP [\fIgroup-id\fP]
.SH Description
\fIxt_pknock\fP is an xtables match extension that implements so-called \fIport
knocking\fP. It can be configured to send information about each successful
match via a netlink socket to userspace. \fBpknluser\fP listens for these
notifications.
.PP
By default, \fBpknlusr\fP listens for messages sent to netlink multicast group
1. Another group ID may be passed as a command-line argument.
.SH See also
.PP
xtables-addons(8)

View File

@@ -7,94 +7,85 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/connector.h> #include <linux/connector.h>
#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include "xt_pknock.h" #include "xt_pknock.h"
#define DEFAULT_GROUP_ID 1 #define GROUP 1
#define MIN_GROUP_ID DEFAULT_GROUP_ID
#define MAX_GROUP_ID \
(sizeof((struct sockaddr_nl){0}.nl_groups) * CHAR_BIT)
int main(int argc, char **argv) static struct sockaddr_nl src_addr, dest_addr;
static int sock_fd;
static unsigned char *buf;
static struct xt_pknock_nl_msg *nlmsg;
int main(void)
{ {
socklen_t addrlen;
int status; int status;
unsigned int group_id = DEFAULT_GROUP_ID; int group = GROUP;
struct sockaddr_nl local_addr = {.nl_family = AF_NETLINK};
int sock_fd;
size_t nlmsg_size;
struct nlmgrhdr *nlmsg;
struct cn_msg *cn_msg;
struct xt_pknock_nl_msg *pknock_msg;
if (argc > 2) { int buf_size;
char *prog = strdup(argv[0]);
if (prog == NULL) {
perror("strdup()");
} else {
fprintf(stderr, "%s [ group-id ]\n", basename(prog));
free(prog);
}
exit(EXIT_FAILURE);
}
if (argc == 2) { const char *ip;
long n; char ipbuf[48];
char *end;
errno = 0;
n = strtol(argv[1], &end, 10);
if (*end || (errno && (n == LONG_MIN || n == LONG_MAX)) ||
n < MIN_GROUP_ID || n > MAX_GROUP_ID) {
fputs("Group ID invalid.\n", stderr);
exit(EXIT_FAILURE);
}
group_id = n;
}
sock_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); sock_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (sock_fd == -1) { if (sock_fd == -1) {
perror("socket()"); perror("socket()");
exit(EXIT_FAILURE); return 1;
} }
local_addr.nl_groups = 1U << (group_id - 1); memset(&src_addr, 0, sizeof(src_addr));
status = bind(sock_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)); src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid();
src_addr.nl_groups = group;
status = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
if (status == -1) { if (status == -1) {
close(sock_fd);
perror("bind()"); perror("bind()");
goto err_close_sock; return 1;
} }
nlmsg_size = NLMSG_SPACE(sizeof(*cn_msg) + sizeof(*pknock_msg)); memset(&dest_addr, 0, sizeof(dest_addr));
nlmsg = malloc(nlmsg_size); dest_addr.nl_family = AF_NETLINK;
if (!nlmsg) { dest_addr.nl_pid = 0;
dest_addr.nl_groups = group;
buf_size = sizeof(struct xt_pknock_nl_msg) + sizeof(struct cn_msg) + sizeof(struct nlmsghdr);
buf = malloc(buf_size);
if (!buf) {
perror("malloc()"); perror("malloc()");
goto err_close_sock; return 1;
} }
addrlen = sizeof(dest_addr);
while(1) { while(1) {
const char *ip;
char ipbuf[INET_ADDRSTRLEN];
memset(nlmsg, 0, nlmsg_size); memset(buf, 0, buf_size);
status = recv(sock_fd, nlmsg, nlmsg_size, 0);
if (status < 0) { status = recvfrom(sock_fd, buf, buf_size, 0, (struct sockaddr *)&dest_addr, &addrlen);
perror("recv()");
goto err_free_msg; if (status <= 0) {
} perror("recvfrom()");
if (status == 0) return 1;
break; }
cn_msg = NLMSG_DATA(nlmsg);
pknock_msg = (struct xt_pknock_nl_msg *)(cn_msg->data); nlmsg = (struct xt_pknock_nl_msg *) (buf + sizeof(struct cn_msg) + sizeof(struct nlmsghdr));
ip = inet_ntop(AF_INET, &pknock_msg->peer_ip, ipbuf, sizeof(ipbuf));
printf("rule_name: %s - ip %s\n", pknock_msg->rule_name, ip); ip = inet_ntop(AF_INET, &nlmsg->peer_ip, ipbuf, sizeof(ipbuf));
printf("rule_name: %s - ip %s\n", nlmsg->rule_name, ip);
} }
err_free_msg:
free(nlmsg);
err_close_sock:
close(sock_fd); close(sock_fd);
exit(status == -1 ? EXIT_FAILURE : EXIT_SUCCESS);
free(buf);
return 0;
} }

View File

@@ -19,14 +19,16 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/crypto.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/connector.h> #include <linux/connector.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <crypto/hash.h>
#include "xt_pknock.h" #include "xt_pknock.h"
#include "compat_xtables.h" #include "compat_xtables.h"
@@ -90,22 +92,28 @@ enum {
#define hashtable_for_each_safe(pos, n, head, size, i) \ #define hashtable_for_each_safe(pos, n, head, size, i) \
for ((i) = 0; (i) < (size); ++(i)) \ for ((i) = 0; (i) < (size); ++(i)) \
list_for_each_safe((pos), (n), (&head[(i)])) list_for_each_safe((pos), (n), (&head[(i)]))
#define pk_debug(msg, peer) pr_debug("(S) peer: %pI4 - %s.\n", &((peer)->ip), msg)
#define pk_debug(msg, peer) pr_debug( \
"(S) peer: " NIPQUAD_FMT " - %s.\n", \
NIPQUAD((peer)->ip), msg)
static uint32_t ipt_pknock_hash_rnd; static uint32_t ipt_pknock_hash_rnd;
static unsigned int rule_hashsize = DEFAULT_RULE_HASH_SIZE; static unsigned int rule_hashsize = DEFAULT_RULE_HASH_SIZE;
static unsigned int peer_hashsize = DEFAULT_PEER_HASH_SIZE; static unsigned int peer_hashsize = DEFAULT_PEER_HASH_SIZE;
static unsigned int gc_expir_time = DEFAULT_GC_EXPIRATION_TIME; static unsigned int gc_expir_time = DEFAULT_GC_EXPIRATION_TIME;
static int nl_multicast_group = -1; static int nl_multicast_group = -1;
static struct list_head *rule_hashtable; static struct list_head *rule_hashtable;
static struct proc_dir_entry *pde; static struct proc_dir_entry *pde;
static DEFINE_SPINLOCK(list_lock); static DEFINE_SPINLOCK(list_lock);
static struct { static struct {
const char *algo; const char *algo;
struct crypto_shash *tfm; struct crypto_hash *tfm;
unsigned int size; unsigned int size;
struct shash_desc desc; struct hash_desc desc;
} crypto = { } crypto = {
.algo = "hmac(sha256)", .algo = "hmac(sha256)",
.tfm = NULL, .tfm = NULL,
@@ -153,6 +161,7 @@ alloc_hashtable(unsigned int size)
return NULL; return NULL;
for (i = 0; i < size; ++i) for (i = 0; i < size; ++i)
INIT_LIST_HEAD(&hash[i]); INIT_LIST_HEAD(&hash[i]);
return hash; return hash;
} }
@@ -181,11 +190,14 @@ status_itoa(enum status status)
static void * static void *
pknock_seq_start(struct seq_file *s, loff_t *pos) pknock_seq_start(struct seq_file *s, loff_t *pos)
{ {
const struct xt_pknock_rule *rule = s->private; const struct proc_dir_entry *pde = s->private;
const struct xt_pknock_rule *rule = pde->data;
spin_lock_bh(&list_lock); spin_lock_bh(&list_lock);
if (*pos >= peer_hashsize) if (*pos >= peer_hashsize)
return NULL; return NULL;
return rule->peer_head + *pos; return rule->peer_head + *pos;
} }
@@ -198,11 +210,13 @@ pknock_seq_start(struct seq_file *s, loff_t *pos)
static void * static void *
pknock_seq_next(struct seq_file *s, void *v, loff_t *pos) pknock_seq_next(struct seq_file *s, void *v, loff_t *pos)
{ {
const struct xt_pknock_rule *rule = s->private; const struct proc_dir_entry *pde = s->private;
const struct xt_pknock_rule *rule = pde->data;
++*pos; ++*pos;
if (*pos >= peer_hashsize) if (*pos >= peer_hashsize)
return NULL; return NULL;
return rule->peer_head + *pos; return rule->peer_head + *pos;
} }
@@ -228,11 +242,14 @@ pknock_seq_show(struct seq_file *s, void *v)
const struct peer *peer; const struct peer *peer;
unsigned long time; unsigned long time;
const struct list_head *peer_head = v; const struct list_head *peer_head = v;
const struct xt_pknock_rule *rule = s->private;
const struct proc_dir_entry *pde = s->private;
const struct xt_pknock_rule *rule = pde->data;
list_for_each_safe(pos, n, peer_head) { list_for_each_safe(pos, n, peer_head) {
peer = list_entry(pos, struct peer, head); peer = list_entry(pos, struct peer, head);
seq_printf(s, "src=%pI4 ", &peer->ip);
seq_printf(s, "src=" NIPQUAD_FMT " ", NIPQUAD(peer->ip));
seq_printf(s, "proto=%s ", (peer->proto == IPPROTO_TCP) ? seq_printf(s, "proto=%s ", (peer->proto == IPPROTO_TCP) ?
"TCP" : "UDP"); "TCP" : "UDP");
seq_printf(s, "status=%s ", status_itoa(peer->status)); seq_printf(s, "status=%s ", status_itoa(peer->status));
@@ -247,11 +264,12 @@ pknock_seq_show(struct seq_file *s, void *v)
seq_printf(s, "expir_time=%lu [secs] ", time); seq_printf(s, "expir_time=%lu [secs] ", time);
} }
if (peer->status == ST_ALLOWED && rule->autoclose_time != 0) { if (peer->status == ST_ALLOWED && rule->autoclose_time != 0) {
unsigned long x = ktime_get_seconds();
unsigned long y = peer->login_sec + rule->autoclose_time * 60;
time = 0; time = 0;
if (time_before(x, y)) if (time_before(get_seconds(), peer->login_sec +
time = y - x; rule->autoclose_time * 60))
time = peer->login_sec +
rule->autoclose_time * 60 -
get_seconds();
seq_printf(s, "autoclose_time=%lu [secs] ", time); seq_printf(s, "autoclose_time=%lu [secs] ", time);
} }
seq_printf(s, "\n"); seq_printf(s, "\n");
@@ -277,16 +295,17 @@ pknock_proc_open(struct inode *inode, struct file *file)
int ret = seq_open(file, &pknock_seq_ops); int ret = seq_open(file, &pknock_seq_ops);
if (ret == 0) { if (ret == 0) {
struct seq_file *sf = file->private_data; struct seq_file *sf = file->private_data;
sf->private = PDE_DATA(inode); sf->private = PDE(inode);
} }
return ret; return ret;
} }
static const struct proc_ops pknock_proc_ops = { static const struct file_operations pknock_proc_ops = {
.proc_open = pknock_proc_open, .owner = THIS_MODULE,
.proc_read = seq_read, .open = pknock_proc_open,
.proc_lseek = seq_lseek, .read = seq_read,
.proc_release = seq_release, .llseek = seq_lseek,
.release = seq_release
}; };
/** /**
@@ -298,6 +317,7 @@ static void update_rule_gc_timer(struct xt_pknock_rule *rule)
{ {
if (timer_pending(&rule->timer)) if (timer_pending(&rule->timer))
del_timer(&rule->timer); del_timer(&rule->timer);
rule->timer.expires = jiffies + msecs_to_jiffies(gc_expir_time); rule->timer.expires = jiffies + msecs_to_jiffies(gc_expir_time);
add_timer(&rule->timer); add_timer(&rule->timer);
} }
@@ -311,12 +331,8 @@ static void update_rule_gc_timer(struct xt_pknock_rule *rule)
static inline bool static inline bool
autoclose_time_passed(const struct peer *peer, unsigned int autoclose_time) autoclose_time_passed(const struct peer *peer, unsigned int autoclose_time)
{ {
unsigned long x, y; return peer != NULL && autoclose_time != 0 && time_after(get_seconds(),
if (peer == NULL || autoclose_time == 0) peer->login_sec + autoclose_time * 60);
return false;
x = ktime_get_seconds();
y = peer->login_sec + autoclose_time * 60;
return time_after(x, y);
} }
/** /**
@@ -338,12 +354,7 @@ is_interknock_time_exceeded(const struct peer *peer, unsigned int max_time)
static inline bool static inline bool
has_logged_during_this_minute(const struct peer *peer) has_logged_during_this_minute(const struct peer *peer)
{ {
unsigned long x, y; return peer != NULL && peer->login_sec / 60 == get_seconds() / 60;
if (peer == NULL)
return 0;
x = ktime_get_seconds();
y = peer->login_sec;
return do_div(y, 60) == do_div(x, 60);
} }
/** /**
@@ -351,10 +362,11 @@ has_logged_during_this_minute(const struct peer *peer)
* *
* @r: rule * @r: rule
*/ */
static void peer_gc(struct timer_list *tl) static void
peer_gc(unsigned long r)
{ {
unsigned int i; unsigned int i;
struct xt_pknock_rule *rule = from_timer(rule, tl, timer); struct xt_pknock_rule *rule = (struct xt_pknock_rule *)r;
struct peer *peer; struct peer *peer;
struct list_head *pos, *n; struct list_head *pos, *n;
@@ -428,6 +440,7 @@ add_rule(struct xt_pknock_mtinfo *info)
list_for_each_safe(pos, n, &rule_hashtable[hash]) { list_for_each_safe(pos, n, &rule_hashtable[hash]) {
rule = list_entry(pos, struct xt_pknock_rule, head); rule = list_entry(pos, struct xt_pknock_rule, head);
if (!rulecmp(info, rule)) if (!rulecmp(info, rule))
continue; continue;
++rule->ref_count; ++rule->ref_count;
@@ -436,6 +449,7 @@ add_rule(struct xt_pknock_mtinfo *info)
rule->max_time = info->max_time; rule->max_time = info->max_time;
rule->autoclose_time = info->autoclose_time; rule->autoclose_time = info->autoclose_time;
} }
if (info->option & XT_PKNOCK_CHECKIP) if (info->option & XT_PKNOCK_CHECKIP)
pr_debug("add_rule() (AC) rule found: %s - " pr_debug("add_rule() (AC) rule found: %s - "
"ref_count: %d\n", "ref_count: %d\n",
@@ -443,25 +457,34 @@ add_rule(struct xt_pknock_mtinfo *info)
return true; return true;
} }
rule = kzalloc(sizeof(*rule), GFP_KERNEL); rule = kmalloc(sizeof(*rule), GFP_KERNEL);
if (rule == NULL) if (rule == NULL)
return false; return false;
INIT_LIST_HEAD(&rule->head); INIT_LIST_HEAD(&rule->head);
memset(rule->rule_name, 0, sizeof(rule->rule_name));
strncpy(rule->rule_name, info->rule_name, info->rule_name_len); strncpy(rule->rule_name, info->rule_name, info->rule_name_len);
rule->rule_name_len = info->rule_name_len; rule->rule_name_len = info->rule_name_len;
rule->ref_count = 1; rule->ref_count = 1;
rule->max_time = info->max_time; rule->max_time = info->max_time;
rule->autoclose_time = info->autoclose_time; rule->autoclose_time = info->autoclose_time;
rule->peer_head = alloc_hashtable(peer_hashsize); rule->peer_head = alloc_hashtable(peer_hashsize);
if (rule->peer_head == NULL) if (rule->peer_head == NULL)
goto out; goto out;
timer_setup(&rule->timer, peer_gc, 0);
rule->status_proc = proc_create_data(info->rule_name, 0, pde, init_timer(&rule->timer);
&pknock_proc_ops, rule); rule->timer.function = peer_gc;
rule->timer.data = (unsigned long)rule;
rule->status_proc = create_proc_entry(info->rule_name, 0, pde);
if (rule->status_proc == NULL) if (rule->status_proc == NULL)
goto out; goto out;
rule->status_proc->proc_fops = &pknock_proc_ops;
rule->status_proc->data = rule;
list_add(&rule->head, &rule_hashtable[hash]); list_add(&rule->head, &rule_hashtable[hash]);
pr_debug("(A) rule_name: %s - created.\n", rule->rule_name); pr_debug("(A) rule_name: %s - created.\n", rule->rule_name);
return true; return true;
@@ -492,6 +515,7 @@ remove_rule(struct xt_pknock_mtinfo *info)
list_for_each_safe(pos, n, &rule_hashtable[hash]) { list_for_each_safe(pos, n, &rule_hashtable[hash]) {
rule = list_entry(pos, struct xt_pknock_rule, head); rule = list_entry(pos, struct xt_pknock_rule, head);
if (rulecmp(info, rule)) { if (rulecmp(info, rule)) {
found = 1; found = 1;
rule->ref_count--; rule->ref_count--;
@@ -517,6 +541,7 @@ remove_rule(struct xt_pknock_mtinfo *info)
pr_debug("(D) rule deleted: %s.\n", rule->rule_name); pr_debug("(D) rule deleted: %s.\n", rule->rule_name);
if (timer_pending(&rule->timer)) if (timer_pending(&rule->timer))
del_timer(&rule->timer); del_timer(&rule->timer);
list_del(&rule->head); list_del(&rule->head);
kfree(rule->peer_head); kfree(rule->peer_head);
kfree(rule); kfree(rule);
@@ -536,6 +561,7 @@ static struct peer *get_peer(struct xt_pknock_rule *rule, __be32 ip)
unsigned int hash; unsigned int hash;
hash = pknock_hash(&ip, sizeof(ip), ipt_pknock_hash_rnd, peer_hashsize); hash = pknock_hash(&ip, sizeof(ip), ipt_pknock_hash_rnd, peer_hashsize);
list_for_each_safe(pos, n, &rule->peer_head[hash]) { list_for_each_safe(pos, n, &rule->peer_head[hash]) {
peer = list_entry(pos, struct peer, head); peer = list_entry(pos, struct peer, head);
if (peer->ip == ip) if (peer->ip == ip)
@@ -569,12 +595,14 @@ static struct peer *new_peer(__be32 ip, uint8_t proto)
if (peer == NULL) if (peer == NULL)
return NULL; return NULL;
INIT_LIST_HEAD(&peer->head); INIT_LIST_HEAD(&peer->head);
peer->ip = ip; peer->ip = ip;
peer->proto = proto; peer->proto = proto;
peer->timestamp = jiffies/HZ; peer->timestamp = jiffies/HZ;
peer->login_sec = 0; peer->login_sec = 0;
reset_knock_status(peer); reset_knock_status(peer);
return peer; return peer;
} }
@@ -598,9 +626,8 @@ static void add_peer(struct peer *peer, struct xt_pknock_rule *rule)
*/ */
static void remove_peer(struct peer *peer) static void remove_peer(struct peer *peer)
{ {
if (peer == NULL)
return;
list_del(&peer->head); list_del(&peer->head);
if (peer != NULL)
kfree(peer); kfree(peer);
} }
@@ -662,19 +689,25 @@ static bool
msg_to_userspace_nl(const struct xt_pknock_mtinfo *info, msg_to_userspace_nl(const struct xt_pknock_mtinfo *info,
const struct peer *peer, int multicast_group) const struct peer *peer, int multicast_group)
{ {
#if IS_ENABLED(CONFIG_CONNECTOR) #if defined(CONFIG_CONNECTOR) || defined(CONFIG_CONNECTOR_MODULE)
struct cn_msg *m; struct cn_msg *m;
struct xt_pknock_nl_msg msg; struct xt_pknock_nl_msg msg;
m = kzalloc(sizeof(*m) + sizeof(msg), GFP_ATOMIC); m = kmalloc(sizeof(*m) + sizeof(msg), GFP_ATOMIC);
if (m == NULL) if (m == NULL)
return false; return false;
memset(m, 0, sizeof(*m) + sizeof(msg));
m->seq = 0;
m->len = sizeof(msg); m->len = sizeof(msg);
msg.peer_ip = peer->ip; msg.peer_ip = peer->ip;
scnprintf(msg.rule_name, info->rule_name_len + 1, info->rule_name); scnprintf(msg.rule_name, info->rule_name_len + 1, info->rule_name);
memcpy(m + 1, &msg, m->len); memcpy(m + 1, &msg, m->len);
cn_netlink_send(m, 0, multicast_group, GFP_ATOMIC);
cn_netlink_send(m, multicast_group, GFP_ATOMIC);
kfree(m); kfree(m);
#endif #endif
return true; return true;
@@ -712,12 +745,12 @@ static bool
has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc,
const unsigned char *payload, unsigned int payload_len) const unsigned char *payload, unsigned int payload_len)
{ {
char result[64] = ""; // 64 bytes * 8 = 512 bits struct scatterlist sg[2];
char result[64]; // 64 bytes * 8 = 512 bits
char *hexresult; char *hexresult;
unsigned int hexa_size; unsigned int hexa_size;
int ret; int ret;
bool fret = false; bool fret = false;
unsigned long x;
unsigned int epoch_min; unsigned int epoch_min;
if (payload_len == 0) if (payload_len == 0)
@@ -733,13 +766,21 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc,
/* + 1 cause we MUST add NULL in the payload */ /* + 1 cause we MUST add NULL in the payload */
if (payload_len != hexa_size + 1) if (payload_len != hexa_size + 1)
return false; return false;
hexresult = kzalloc(hexa_size, GFP_ATOMIC);
hexresult = kmalloc(hexa_size, GFP_ATOMIC);
if (hexresult == NULL) if (hexresult == NULL)
return false; return false;
x = ktime_get_seconds();
epoch_min = do_div(x, 60);
ret = crypto_shash_setkey(crypto.tfm, secret, secret_len); memset(result, 0, sizeof(result));
memset(hexresult, 0, hexa_size);
epoch_min = get_seconds() / 60;
sg_init_table(sg, ARRAY_SIZE(sg));
sg_set_buf(&sg[0], &ipsrc, sizeof(ipsrc));
sg_set_buf(&sg[1], &epoch_min, sizeof(epoch_min));
ret = crypto_hash_setkey(crypto.tfm, secret, secret_len);
if (ret != 0) { if (ret != 0) {
printk("crypto_hash_setkey() failed ret=%d\n", ret); printk("crypto_hash_setkey() failed ret=%d\n", ret);
goto out; goto out;
@@ -750,17 +791,20 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc,
* 4 bytes IP (32 bits) + * 4 bytes IP (32 bits) +
* 4 bytes int epoch_min (32 bits) * 4 bytes int epoch_min (32 bits)
*/ */
if ((ret = crypto_shash_update(&crypto.desc, (const void *)&ipsrc, sizeof(ipsrc))) != 0 || ret = crypto_hash_digest(&crypto.desc, sg,
(ret = crypto_shash_update(&crypto.desc, (const void *)&epoch_min, sizeof(epoch_min))) != 0 || sizeof(ipsrc) + sizeof(epoch_min), result);
(ret = crypto_shash_final(&crypto.desc, result)) != 0) { if (ret != 0) {
printk("crypto_shash_update/final() failed ret=%d\n", ret); printk("crypto_hash_digest() failed ret=%d\n", ret);
goto out; goto out;
} }
crypt_to_hex(hexresult, result, crypto.size); crypt_to_hex(hexresult, result, crypto.size);
if (memcmp(hexresult, payload, hexa_size) != 0) if (memcmp(hexresult, payload, hexa_size) != 0)
pr_debug("secret match failed\n"); pr_debug("secret match failed\n");
else else
fret = true; fret = true;
out: out:
kfree(hexresult); kfree(hexresult);
return fret; return fret;
@@ -792,6 +836,7 @@ pass_security(struct peer *peer, const struct xt_pknock_mtinfo *info,
info->open_secret_len, peer->ip, info->open_secret_len, peer->ip,
payload, payload_len)) payload, payload_len))
return true; return true;
return false; return false;
} }
@@ -818,6 +863,7 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info,
/* Peer must start the sequence from scratch. */ /* Peer must start the sequence from scratch. */
if (info->option & XT_PKNOCK_STRICT) if (info->option & XT_PKNOCK_STRICT)
remove_peer(peer); remove_peer(peer);
return false; return false;
} }
@@ -825,20 +871,25 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info,
if (info->option & XT_PKNOCK_OPENSECRET ) { if (info->option & XT_PKNOCK_OPENSECRET ) {
if (hdr->proto != IPPROTO_UDP && hdr->proto != IPPROTO_UDPLITE) if (hdr->proto != IPPROTO_UDP && hdr->proto != IPPROTO_UDPLITE)
return false; return false;
if (!pass_security(peer, info, hdr->payload, hdr->payload_len)) if (!pass_security(peer, info, hdr->payload, hdr->payload_len))
return false; return false;
} }
/* Update the gc timer when there is a state change. */ /* Update the gc timer when there is a state change. */
update_rule_gc_timer(rule); update_rule_gc_timer(rule);
++peer->accepted_knock_count; ++peer->accepted_knock_count;
if (is_last_knock(peer, info)) { if (is_last_knock(peer, info)) {
peer->status = ST_ALLOWED; peer->status = ST_ALLOWED;
pk_debug("ALLOWED", peer); pk_debug("ALLOWED", peer);
peer->login_sec = ktime_get_seconds(); peer->login_sec = get_seconds();
if (nl_multicast_group > 0) if (nl_multicast_group > 0)
msg_to_userspace_nl(info, peer, nl_multicast_group); msg_to_userspace_nl(info, peer, nl_multicast_group);
return true; return true;
} }
@@ -915,6 +966,7 @@ static bool pknock_mt(const struct sk_buff *skb,
switch (hdr.proto) { switch (hdr.proto) {
case IPPROTO_TCP: case IPPROTO_TCP:
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
case IPPROTO_UDPLITE: case IPPROTO_UDPLITE:
hdr_len = (iph->ihl * 4) + sizeof(struct udphdr); hdr_len = (iph->ihl * 4) + sizeof(struct udphdr);
@@ -936,10 +988,12 @@ static bool pknock_mt(const struct sk_buff *skb,
/* Gives the peer matching status added to rule depending on ip src. */ /* Gives the peer matching status added to rule depending on ip src. */
peer = get_peer(rule, iph->saddr); peer = get_peer(rule, iph->saddr);
if (info->option & XT_PKNOCK_CHECKIP) { if (info->option & XT_PKNOCK_CHECKIP) {
ret = is_allowed(peer); ret = is_allowed(peer);
goto out; goto out;
} }
if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_UDPLITE) { if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_UDPLITE) {
hdr.payload = (void *)iph + hdr_len; hdr.payload = (void *)iph + hdr_len;
hdr.payload_len = skb->len - hdr_len; hdr.payload_len = skb->len - hdr_len;
@@ -947,8 +1001,7 @@ static bool pknock_mt(const struct sk_buff *skb,
/* Sets, updates, removes or checks the peer matching status. */ /* Sets, updates, removes or checks the peer matching status. */
if (info->option & XT_PKNOCK_KNOCKPORT) { if (info->option & XT_PKNOCK_KNOCKPORT) {
ret = is_allowed(peer); if ((ret = is_allowed(peer))) {
if (ret != 0) {
if (info->option & XT_PKNOCK_CLOSESECRET && if (info->option & XT_PKNOCK_CLOSESECRET &&
(iph->protocol == IPPROTO_UDP || (iph->protocol == IPPROTO_UDP ||
iph->protocol == IPPROTO_UDPLITE)) iph->protocol == IPPROTO_UDPLITE))
@@ -966,8 +1019,10 @@ static bool pknock_mt(const struct sk_buff *skb,
peer = new_peer(iph->saddr, iph->protocol); peer = new_peer(iph->saddr, iph->protocol);
add_peer(peer, rule); add_peer(peer, rule);
} }
if (peer == NULL) if (peer == NULL)
goto out; goto out;
update_peer(peer, info, rule, &hdr); update_peer(peer, info, rule, &hdr);
} }
@@ -987,7 +1042,7 @@ out:
return ret; return ret;
} }
#define RETURN_ERR(err) do { pr_err(err); return -EINVAL; } while (false) #define RETURN_ERR(err) do { printk(KERN_ERR PKNOCK err); return -EINVAL; } while (false)
static int pknock_mt_check(const struct xt_mtchk_param *par) static int pknock_mt_check(const struct xt_mtchk_param *par)
{ {
@@ -1004,6 +1059,9 @@ static int pknock_mt_check(const struct xt_mtchk_param *par)
if (!(info->option & XT_PKNOCK_NAME)) if (!(info->option & XT_PKNOCK_NAME))
RETURN_ERR("You must specify --name option.\n"); RETURN_ERR("You must specify --name option.\n");
if (info->option & (XT_PKNOCK_OPENSECRET | XT_PKNOCK_CLOSESECRET))
RETURN_ERR("No crypto support available; "
"cannot use opensecret/closescret\n");
if (info->option & XT_PKNOCK_OPENSECRET && info->ports_count != 1) if (info->option & XT_PKNOCK_OPENSECRET && info->ports_count != 1)
RETURN_ERR("--opensecret must have just one knock port\n"); RETURN_ERR("--opensecret must have just one knock port\n");
if (info->option & XT_PKNOCK_KNOCKPORT) { if (info->option & XT_PKNOCK_KNOCKPORT) {
@@ -1037,9 +1095,11 @@ static int pknock_mt_check(const struct xt_mtchk_param *par)
memcmp(info->open_secret, info->close_secret, memcmp(info->open_secret, info->close_secret,
info->open_secret_len) == 0) info->open_secret_len) == 0)
RETURN_ERR("opensecret & closesecret cannot be equal.\n"); RETURN_ERR("opensecret & closesecret cannot be equal.\n");
if (!add_rule(info)) if (!add_rule(info))
/* should ENOMEM here */ /* should ENOMEM here */
RETURN_ERR("add_rule() error in checkentry() function.\n"); RETURN_ERR("add_rule() error in checkentry() function.\n");
return 0; return 0;
} }
@@ -1063,7 +1123,7 @@ static struct xt_match xt_pknock_mt_reg __read_mostly = {
static int __init xt_pknock_mt_init(void) static int __init xt_pknock_mt_init(void)
{ {
#if !IS_ENABLED(CONFIG_CONNECTOR) #if !defined(CONFIG_CONNECTOR) && !defined(CONFIG_CONNECTOR_MODULE)
if (nl_multicast_group != -1) if (nl_multicast_group != -1)
pr_info("CONFIG_CONNECTOR not present; " pr_info("CONFIG_CONNECTOR not present; "
"netlink messages disabled\n"); "netlink messages disabled\n");
@@ -1072,24 +1132,25 @@ static int __init xt_pknock_mt_init(void)
if (gc_expir_time < DEFAULT_GC_EXPIRATION_TIME) if (gc_expir_time < DEFAULT_GC_EXPIRATION_TIME)
gc_expir_time = DEFAULT_GC_EXPIRATION_TIME; gc_expir_time = DEFAULT_GC_EXPIRATION_TIME;
if (request_module(crypto.algo) < 0) { if (request_module(crypto.algo) < 0) {
pr_err("request_module('%s') error.\n", printk(KERN_ERR PKNOCK "request_module('%s') error.\n",
crypto.algo); crypto.algo);
return -ENXIO; return -ENXIO;
} }
crypto.tfm = crypto_alloc_shash(crypto.algo, 0, 0); crypto.tfm = crypto_alloc_hash(crypto.algo, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(crypto.tfm)) { if (IS_ERR(crypto.tfm)) {
pr_err("failed to load transform for %s\n", printk(KERN_ERR PKNOCK "failed to load transform for %s\n",
crypto.algo); crypto.algo);
return PTR_ERR(crypto.tfm); return PTR_ERR(crypto.tfm);
} }
crypto.size = crypto_shash_digestsize(crypto.tfm); crypto.size = crypto_hash_digestsize(crypto.tfm);
crypto.desc.tfm = crypto.tfm; crypto.desc.tfm = crypto.tfm;
crypto.desc.flags = 0;
pde = proc_mkdir("xt_pknock", init_net.proc_net); pde = proc_mkdir("xt_pknock", init_net.proc_net);
if (pde == NULL) { if (pde == NULL) {
pr_err("proc_mkdir() error in _init().\n"); printk(KERN_ERR PKNOCK "proc_mkdir() error in _init().\n");
return -ENXIO; return -ENXIO;
} }
return xt_register_match(&xt_pknock_mt_reg); return xt_register_match(&xt_pknock_mt_reg);
@@ -1101,7 +1162,7 @@ static void __exit xt_pknock_mt_exit(void)
xt_unregister_match(&xt_pknock_mt_reg); xt_unregister_match(&xt_pknock_mt_reg);
kfree(rule_hashtable); kfree(rule_hashtable);
if (crypto.tfm != NULL) if (crypto.tfm != NULL)
crypto_free_shash(crypto.tfm); crypto_free_hash(crypto.tfm);
} }
module_init(xt_pknock_mt_init); module_init(xt_pknock_mt_init);

View File

@@ -29,6 +29,8 @@ enum {
XT_PKNOCK_MAX_PASSWD_LEN = 31, XT_PKNOCK_MAX_PASSWD_LEN = 31,
}; };
#define DEBUG 1
struct xt_pknock_mtinfo { struct xt_pknock_mtinfo {
char rule_name[XT_PKNOCK_MAX_BUF_LEN+1]; char rule_name[XT_PKNOCK_MAX_BUF_LEN+1];
uint32_t rule_name_len; uint32_t rule_name_len;

View File

@@ -58,7 +58,8 @@ xt_chaos_total(struct sk_buff *skb, const struct xt_action_param *par)
{ {
struct xt_action_param local_par; struct xt_action_param local_par;
local_par.state = par->state; local_par.in = par->in,
local_par.out = par->out,
local_par.match = xm_tcp; local_par.match = xm_tcp;
local_par.matchinfo = &tcp_params; local_par.matchinfo = &tcp_params;
local_par.fragoff = fragoff; local_par.fragoff = fragoff;
@@ -67,21 +68,24 @@ xt_chaos_total(struct sk_buff *skb, const struct xt_action_param *par)
ret = xm_tcp->match(skb, &local_par); ret = xm_tcp->match(skb, &local_par);
hotdrop = local_par.hotdrop; hotdrop = local_par.hotdrop;
} }
if (!ret || hotdrop || (unsigned int)prandom_u32() > delude_percentage) if (!ret || hotdrop || (unsigned int)net_random() > delude_percentage)
return; return;
destiny = (info->variant == XTCHAOS_TARPIT) ? xt_tarpit : xt_delude; destiny = (info->variant == XTCHAOS_TARPIT) ? xt_tarpit : xt_delude;
{ {
struct xt_action_param local_par; struct xt_action_param local_par;
local_par.state = par->state; local_par.in = par->in;
local_par.out = par->out;
local_par.hooknum = par->hooknum;
local_par.target = destiny; local_par.target = destiny;
local_par.targinfo = par->targinfo; local_par.targinfo = par->targinfo;
local_par.family = par->family;
destiny->target(skb, &local_par); destiny->target(skb, &local_par);
} }
} }
static unsigned int static unsigned int
chaos_tg(struct sk_buff *skb, const struct xt_action_param *par) chaos_tg(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
/* /*
* Equivalent to: * Equivalent to:
@@ -92,19 +96,23 @@ chaos_tg(struct sk_buff *skb, const struct xt_action_param *par)
* -A chaos -j DROP; * -A chaos -j DROP;
*/ */
const struct xt_chaos_tginfo *info = par->targinfo; const struct xt_chaos_tginfo *info = par->targinfo;
struct sk_buff *skb = *pskb;
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
if ((unsigned int)prandom_u32() <= reject_percentage) { if ((unsigned int)net_random() <= reject_percentage) {
struct xt_action_param local_par; struct xt_action_param local_par;
local_par.state = par->state; local_par.in = par->in;
local_par.out = par->out;
local_par.hooknum = par->hooknum;
local_par.target = xt_reject; local_par.target = xt_reject;
local_par.targinfo = &reject_params; local_par.targinfo = &reject_params;
return xt_reject->target(skb, &local_par); return xt_reject->target(skb, &local_par);
} }
/* TARPIT/DELUDE may not be called from the OUTPUT chain */ /* TARPIT/DELUDE may not be called from the OUTPUT chain */
if (iph->protocol == IPPROTO_TCP && info->variant != XTCHAOS_NORMAL && if (iph->protocol == IPPROTO_TCP &&
par->state->hook != NF_INET_LOCAL_OUT) info->variant != XTCHAOS_NORMAL &&
par->hooknum != NF_INET_LOCAL_OUT)
xt_chaos_total(skb, par); xt_chaos_total(skb, par);
return NF_DROP; return NF_DROP;
@@ -171,8 +179,7 @@ static int __init chaos_tg_init(void)
printk(KERN_WARNING PFX "Warning: Could not find or load " printk(KERN_WARNING PFX "Warning: Could not find or load "
"\"DELUDE\" target\n"); "\"DELUDE\" target\n");
ret = xt_register_target(&chaos_tg_reg); if ((ret = xt_register_target(&chaos_tg_reg)) != 0) {
if (ret != 0) {
printk(KERN_WARNING PFX "xt_register_target returned " printk(KERN_WARNING PFX "xt_register_target returned "
"error %d\n", ret); "error %d\n", ret);
goto out3; goto out3;

View File

@@ -25,8 +25,7 @@
#include "compat_xtables.h" #include "compat_xtables.h"
#define PFX KBUILD_MODNAME ": " #define PFX KBUILD_MODNAME ": "
static void delude_send_reset(struct sk_buff *oldskb, static void delude_send_reset(struct sk_buff *oldskb, unsigned int hook)
const struct xt_action_param *par)
{ {
struct tcphdr _otcph, *tcph; struct tcphdr _otcph, *tcph;
const struct tcphdr *oth; const struct tcphdr *oth;
@@ -51,8 +50,7 @@ static void delude_send_reset(struct sk_buff *oldskb,
return; return;
/* Check checksum */ /* Check checksum */
if (nf_ip_checksum(oldskb, par->state->hook, ip_hdrlen(oldskb), if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
IPPROTO_TCP))
return; return;
nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) +
@@ -80,7 +78,7 @@ static void delude_send_reset(struct sk_buff *oldskb,
tcph->doff = sizeof(struct tcphdr) / 4; tcph->doff = sizeof(struct tcphdr) / 4;
/* DELUDE essential part */ /* DELUDE essential part */
if (oth->syn && !oth->ack && !oth->fin) { if (oth->syn && !oth->ack && !oth->rst && !oth->fin) {
tcph->syn = true; tcph->syn = true;
tcph->seq = 0; tcph->seq = 0;
tcph->ack = true; tcph->ack = true;
@@ -108,22 +106,17 @@ static void delude_send_reset(struct sk_buff *oldskb,
addr_type = RTN_UNSPEC; addr_type = RTN_UNSPEC;
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL &&
if (par->state->hook != NF_INET_FORWARD || nskb->nf_bridge->mask & BRNF_BRIDGED))
((struct nf_bridge_info *)skb_ext_find(nskb, SKB_EXT_BRIDGE_NF) != NULL &&
((struct nf_bridge_info *)skb_ext_find(nskb, SKB_EXT_BRIDGE_NF))->physoutdev))
#else #else
if (par->state->hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL && if (hook != NF_INET_FORWARD)
nskb->nf_bridge->physoutdev))
#endif
#else
if (par->state->hook != NF_INET_FORWARD)
#endif #endif
addr_type = RTN_LOCAL; addr_type = RTN_LOCAL;
/* ip_route_me_harder expects skb->dst to be set */ /* ip_route_me_harder expects skb->dst to be set */
skb_dst_set(nskb, dst_clone(skb_dst(oldskb))); skb_dst_set(nskb, dst_clone(skb_dst(oldskb)));
if (ip_route_me_harder(par_net(par), par->state->sk, nskb, addr_type))
if (ip_route_me_harder(&nskb, addr_type))
goto free_nskb; goto free_nskb;
else else
niph = ip_hdr(nskb); niph = ip_hdr(nskb);
@@ -136,7 +129,8 @@ static void delude_send_reset(struct sk_buff *oldskb,
goto free_nskb; goto free_nskb;
nf_ct_attach(nskb, oldskb); nf_ct_attach(nskb, oldskb);
ip_local_out(par_net(par), nskb->sk, nskb);
ip_local_out(nskb);
return; return;
free_nskb: free_nskb:
@@ -144,14 +138,12 @@ static void delude_send_reset(struct sk_buff *oldskb,
} }
static unsigned int static unsigned int
delude_tg(struct sk_buff *skb, const struct xt_action_param *par) delude_tg(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
/* /* WARNING: This code causes reentry within iptables.
* Sending the reset causes reentrancy within iptables - and should not pose This means that the iptables jump stack is now crap. We
* a problem, as that is supported since Linux 2.6.35. But since we do not must return an absolute verdict. --RR */
* actually want to have a connection open, we are still going to drop it. delude_send_reset(*pskb, par->hooknum);
*/
delude_send_reset(skb, par);
return NF_DROP; return NF_DROP;
} }

View File

@@ -34,9 +34,9 @@ static void ether_set(unsigned char *addr, const unsigned char *op,
unsigned int i; unsigned int i;
for (i = 0; i < ETH_ALEN && mask > 0; ++i) { for (i = 0; i < ETH_ALEN && mask > 0; ++i) {
lo_mask = (mask >= 8) ? 8 : mask; lo_mask = mask % 8;
/* FF << 4 >> 4 = 0F */ /* FF << 4 >> 4 = 0F */
lo_mask = (uint8_t)(~0U << lo_mask) >> lo_mask; lo_mask = ~(uint8_t)0U << lo_mask >> lo_mask;
addr[i] &= lo_mask; addr[i] &= lo_mask;
addr[i] |= op[i] & ~lo_mask; addr[i] |= op[i] & ~lo_mask;
if (mask >= 8) if (mask >= 8)
@@ -55,9 +55,9 @@ static bool ether_cmp(const unsigned char *lh, const unsigned char *rh,
#define ZMACHEX(s) s[0], s[1], s[2], s[3], s[4], s[5] #define ZMACHEX(s) s[0], s[1], s[2], s[3], s[4], s[5]
for (i = 0; i < ETH_ALEN && mask > 0; ++i) { for (i = 0; i < ETH_ALEN && mask > 0; ++i) {
lo_mask = (mask >= 8) ? 8 : mask; lo_mask = mask % 8;
/* ~(0xFF << 4 >> 4) = ~0x0F = 0xF0 */ /* ~(0xFF << 4 >> 4) = ~0x0F = 0xF0 */
lo_mask = ~((uint8_t)(~0U << lo_mask) >> lo_mask); lo_mask = ~(~(uint8_t)0U << lo_mask >> lo_mask);
if ((lh[i] ^ rh[i]) & lo_mask) if ((lh[i] ^ rh[i]) & lo_mask)
return false; return false;
if (mask >= 8) if (mask >= 8)
@@ -89,15 +89,15 @@ dhcpmac_mt(const struct sk_buff *skb, struct xt_action_param *par)
} }
static unsigned int static unsigned int
dhcpmac_tg(struct sk_buff *skb, const struct xt_action_param *par) dhcpmac_tg(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
const struct dhcpmac_info *info = par->targinfo; const struct dhcpmac_info *info = par->targinfo;
struct dhcp_message dhcpbuf, *dh; struct dhcp_message dhcpbuf, *dh;
struct udphdr udpbuf, *udph; struct udphdr udpbuf, *udph;
struct sk_buff *skb = *pskb;
unsigned int i; unsigned int i;
if (skb_ensure_writable(skb, ip_hdrlen(skb) + sizeof(udpbuf) + if (!skb_make_writable(pskb, 0))
sizeof(dhcpbuf)))
return NF_DROP; return NF_DROP;
udph = skb_header_pointer(skb, ip_hdrlen(skb), udph = skb_header_pointer(skb, ip_hdrlen(skb),
@@ -111,12 +111,13 @@ dhcpmac_tg(struct sk_buff *skb, const struct xt_action_param *par)
return NF_DROP; return NF_DROP;
for (i = 0; i < sizeof(dh->chaddr); i += 2) for (i = 0; i < sizeof(dh->chaddr); i += 2)
csum_replace2(&udph->check, *(const __be16 *)(dh->chaddr + i), 0); csum_replace2(&udph->check, *(const __be16 *)dh->chaddr, 0);
memset(dh->chaddr, 0, sizeof(dh->chaddr));
ether_set(dh->chaddr, info->addr, info->mask); ether_set(dh->chaddr, info->addr, info->mask);
for (i = 0; i < sizeof(dh->chaddr); i += 2) for (i = 0; i < sizeof(dh->chaddr); i += 2)
csum_replace2(&udph->check, 0, *(const __be16 *)(dh->chaddr + i)); csum_replace2(&udph->check, 0, *(const __be16 *)dh->chaddr);
return XT_CONTINUE; return XT_CONTINUE;
} }

View File

@@ -19,17 +19,13 @@
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#ifdef CONFIG_NF_NAT
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uidgid.h>
#include <linux/version.h> #include <linux/version.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
@@ -37,6 +33,12 @@
#include "compat_xtables.h" #include "compat_xtables.h"
#include "xt_DNETMAP.h" #include "xt_DNETMAP.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Kierdelewicz <marek@piasta.pl>");
MODULE_DESCRIPTION(
"Xtables: dynamic two-way 1:1 NAT mapping of IPv4 addresses");
MODULE_ALIAS("ipt_DNETMAP");
static unsigned int default_ttl = 600; static unsigned int default_ttl = 600;
static unsigned int proc_perms = S_IRUGO | S_IWUSR; static unsigned int proc_perms = S_IRUGO | S_IWUSR;
static unsigned int proc_uid; static unsigned int proc_uid;
@@ -61,8 +63,14 @@ MODULE_PARM_DESC(whole_prefix,
static unsigned int jtimeout; static unsigned int jtimeout;
struct dnetmap_entry { struct dnetmap_entry {
struct list_head list, glist, grlist, lru_list; struct list_head list;
__be32 prenat_addr, postnat_addr; /* priv2entry */
struct list_head glist;
/* pub2entry */
struct list_head grlist;
struct list_head lru_list;
__be32 prenat_addr;
__be32 postnat_addr;
__u8 flags; __u8 flags;
unsigned long stamp; unsigned long stamp;
struct dnetmap_prefix *prefix; struct dnetmap_prefix *prefix;
@@ -70,9 +78,10 @@ struct dnetmap_entry {
struct dnetmap_prefix { struct dnetmap_prefix {
struct nf_nat_range prefix; struct nf_nat_range prefix;
char prefix_str[20]; char prefix_str[16];
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
char proc_str_data[20], proc_str_stat[25]; char proc_str_data[20];
char proc_str_stat[25];
#endif #endif
struct list_head elist; // element list head struct list_head elist; // element list head
struct list_head list; // prefix list struct list_head list; // prefix list
@@ -103,9 +112,13 @@ static DEFINE_SPINLOCK(dnetmap_lock);
static DEFINE_MUTEX(dnetmap_mutex); static DEFINE_MUTEX(dnetmap_mutex);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static const struct proc_ops dnetmap_tg_fops, dnetmap_stat_proc_fops; static const struct file_operations dnetmap_tg_fops;
#endif #endif
static int dnetmap_stat_proc_read(char __user *buffer, char **start,
off_t offset, int length, int *eof,
void *data);
static inline unsigned int dnetmap_entry_hash(const __be32 addr) static inline unsigned int dnetmap_entry_hash(const __be32 addr)
{ {
return ntohl(addr) & (hash_size - 1); return ntohl(addr) & (hash_size - 1);
@@ -115,7 +128,9 @@ static struct dnetmap_entry *
dnetmap_entry_lookup(struct dnetmap_net *dnetmap_net, const __be32 addr) dnetmap_entry_lookup(struct dnetmap_net *dnetmap_net, const __be32 addr)
{ {
struct dnetmap_entry *e; struct dnetmap_entry *e;
unsigned int h = dnetmap_entry_hash(addr); unsigned int h;
h = dnetmap_entry_hash(addr);
list_for_each_entry(e, &dnetmap_net->dnetmap_iphash[h], glist) list_for_each_entry(e, &dnetmap_net->dnetmap_iphash[h], glist)
if (memcmp(&e->prenat_addr, &addr, sizeof(addr)) == 0) if (memcmp(&e->prenat_addr, &addr, sizeof(addr)) == 0)
@@ -127,7 +142,9 @@ static struct dnetmap_entry *
dnetmap_entry_rlookup(struct dnetmap_net *dnetmap_net, const __be32 addr) dnetmap_entry_rlookup(struct dnetmap_net *dnetmap_net, const __be32 addr)
{ {
struct dnetmap_entry *e; struct dnetmap_entry *e;
unsigned int h = dnetmap_entry_hash(addr); unsigned int h;
h = dnetmap_entry_hash(addr);
list_for_each_entry(e, &dnetmap_net->dnetmap_iphash[hash_size + h], list_for_each_entry(e, &dnetmap_net->dnetmap_iphash[hash_size + h],
grlist) grlist)
@@ -277,12 +294,12 @@ static int dnetmap_tg_check(const struct xt_tgchk_param *par)
ip_min = ntohl(mr->min_addr.ip) + (whole_prefix == 0); ip_min = ntohl(mr->min_addr.ip) + (whole_prefix == 0);
ip_max = ntohl(mr->max_addr.ip) - (whole_prefix == 0); ip_max = ntohl(mr->max_addr.ip) - (whole_prefix == 0);
sprintf(p->prefix_str, "%pI4/%u", &mr->min_addr.ip, sprintf(p->prefix_str, NIPQUAD_FMT "/%u", NIPQUAD(mr->min_addr.ip),
33 - ffs(~(ip_min ^ ip_max))); 33 - ffs(~(ip_min ^ ip_max)));
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
sprintf(p->proc_str_data, "%pI4_%u", &mr->min_addr.ip, sprintf(p->proc_str_data, NIPQUAD_FMT "_%u", NIPQUAD(mr->min_addr.ip),
33 - ffs(~(ip_min ^ ip_max))); 33 - ffs(~(ip_min ^ ip_max)));
sprintf(p->proc_str_stat, "%pI4_%u_stat", &mr->min_addr.ip, sprintf(p->proc_str_stat, NIPQUAD_FMT "_%u_stat", NIPQUAD(mr->min_addr.ip),
33 - ffs(~(ip_min ^ ip_max))); 33 - ffs(~(ip_min ^ ip_max)));
#endif #endif
printk(KERN_INFO KBUILD_MODNAME ": new prefix %s\n", p->prefix_str); printk(KERN_INFO KBUILD_MODNAME ": new prefix %s\n", p->prefix_str);
@@ -311,20 +328,21 @@ static int dnetmap_tg_check(const struct xt_tgchk_param *par)
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
proc_set_user(pde_data, make_kuid(&init_user_ns, proc_uid), pde_data->uid = proc_uid;
make_kgid(&init_user_ns, proc_gid)); pde_data->gid = proc_gid;
/* statistics */ /* statistics */
pde_stat = proc_create_data(p->proc_str_stat, proc_perms, pde_stat = create_proc_entry(p->proc_str_stat, proc_perms,
dnetmap_net->xt_dnetmap, dnetmap_net->xt_dnetmap);
&dnetmap_stat_proc_fops, p);
if (pde_stat == NULL) { if (pde_stat == NULL) {
kfree(p); kfree(p);
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
proc_set_user(pde_stat, make_kuid(&init_user_ns, proc_uid), pde_stat->data = p;
make_kgid(&init_user_ns, proc_gid)); pde_stat->read_proc = dnetmap_stat_proc_read;
pde_stat->uid = proc_uid;
pde_stat->gid = proc_gid;
#endif #endif
spin_lock_bh(&dnetmap_lock); spin_lock_bh(&dnetmap_lock);
@@ -338,27 +356,30 @@ out:
} }
static unsigned int static unsigned int
dnetmap_tg(struct sk_buff *skb, const struct xt_action_param *par) dnetmap_tg(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
struct net *net = dev_net(par->state->in ? par->state->in : par->state->out); struct sk_buff *skb = *pskb;
struct net *net = dev_net(par->in ? par->in : par->out);
struct dnetmap_net *dnetmap_net = dnetmap_pernet(net); struct dnetmap_net *dnetmap_net = dnetmap_pernet(net);
struct nf_conn *ct;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
__be32 prenat_ip, postnat_ip, prenat_ip_prev; __be32 prenat_ip, postnat_ip, prenat_ip_prev;
const struct xt_DNETMAP_tginfo *tginfo = par->targinfo; const struct xt_DNETMAP_tginfo *tginfo = par->targinfo;
const struct nf_nat_range *mr = &tginfo->prefix; const struct nf_nat_range *mr = &tginfo->prefix;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
struct nf_nat_range2 newrange;
#else
struct nf_nat_range newrange; struct nf_nat_range newrange;
#endif
struct dnetmap_entry *e; struct dnetmap_entry *e;
struct dnetmap_prefix *p; struct dnetmap_prefix *p;
unsigned int hooknum = par->state->hook; __s32 jttl;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
__s32 jttl = tginfo->flags & XT_DNETMAP_TTL ? tginfo->ttl * HZ : jtimeout; NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING ||
par->hooknum == NF_INET_LOCAL_OUT ||
par->hooknum == NF_INET_PRE_ROUTING);
ct = nf_ct_get(skb, &ctinfo);
jttl = tginfo->flags & XT_DNETMAP_TTL ? tginfo->ttl * HZ : jtimeout;
/* in prerouting we try to map postnat-ip to prenat-ip */ /* in prerouting we try to map postnat-ip to prenat-ip */
if (hooknum == NF_INET_PRE_ROUTING) { if (par->hooknum == NF_INET_PRE_ROUTING) {
postnat_ip = ip_hdr(skb)->daddr; postnat_ip = ip_hdr(skb)->daddr;
spin_lock_bh(&dnetmap_lock); spin_lock_bh(&dnetmap_lock);
@@ -371,7 +392,7 @@ dnetmap_tg(struct sk_buff *skb, const struct xt_action_param *par)
/* if prefix is specified, we check if /* if prefix is specified, we check if
it matches lookedup entry */ it matches lookedup entry */
if (tginfo->flags & XT_DNETMAP_PREFIX) if (tginfo->flags & XT_DNETMAP_PREFIX)
if (memcmp(mr, &e->prefix->prefix, sizeof(*mr))) if (memcmp(mr, &e->prefix, sizeof(*mr)))
goto no_rev_map; goto no_rev_map;
/* don't reset ttl if flag is set */ /* don't reset ttl if flag is set */
if (jttl >= 0 && (! (e->flags & XT_DNETMAP_STATIC) ) ) { if (jttl >= 0 && (! (e->flags & XT_DNETMAP_STATIC) ) ) {
@@ -389,7 +410,7 @@ dnetmap_tg(struct sk_buff *skb, const struct xt_action_param *par)
newrange.min_proto = mr->min_proto; newrange.min_proto = mr->min_proto;
newrange.max_proto = mr->max_proto; newrange.max_proto = mr->max_proto;
return nf_nat_setup_info(ct, &newrange, return nf_nat_setup_info(ct, &newrange,
HOOK2MANIP(hooknum)); HOOK2MANIP(par->hooknum));
} }
prenat_ip = ip_hdr(skb)->saddr; prenat_ip = ip_hdr(skb)->saddr;
@@ -410,8 +431,8 @@ bind_new_prefix:
if (e->prenat_addr != 0 && time_before(jiffies, e->stamp)) { if (e->prenat_addr != 0 && time_before(jiffies, e->stamp)) {
if (!disable_log && ! (p->flags & XT_DNETMAP_FULL) ){ if (!disable_log && ! (p->flags & XT_DNETMAP_FULL) ){
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": ip %pI4 - no free adresses in prefix %s\n", ": ip " NIPQUAD_FMT " - no free adresses in prefix %s\n",
&prenat_ip, p->prefix_str); NIPQUAD(prenat_ip), p->prefix_str);
p->flags |= XT_DNETMAP_FULL; p->flags |= XT_DNETMAP_FULL;
} }
goto no_free_ip; goto no_free_ip;
@@ -424,8 +445,8 @@ bind_new_prefix:
prenat_ip_prev = e->prenat_addr; prenat_ip_prev = e->prenat_addr;
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": timeout binding %pI4 -> %pI4\n", ": timeout binding " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
&prenat_ip_prev, &postnat_ip); NIPQUAD(prenat_ip_prev), NIPQUAD(postnat_ip) );
list_del(&e->glist); list_del(&e->glist);
list_del(&e->grlist); list_del(&e->grlist);
} }
@@ -442,16 +463,18 @@ bind_new_prefix:
(postnat_ip)]); (postnat_ip)]);
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": add binding %pI4 -> %pI4\n", ": add binding " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
&prenat_ip, &postnat_ip); NIPQUAD(prenat_ip),NIPQUAD(postnat_ip));
} else { } else {
if (!(tginfo->flags & XT_DNETMAP_REUSE) && !(e->flags & XT_DNETMAP_STATIC)) if (!(tginfo->flags & XT_DNETMAP_REUSE) && !(e->flags & XT_DNETMAP_STATIC))
if (time_before(e->stamp, jiffies) && p != e->prefix) { if (time_before(e->stamp, jiffies) && p != e->prefix) {
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": timeout binding %pI4 -> %pI4\n", ": timeout binding " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
&e->prenat_addr, &e->postnat_addr); NIPQUAD(e->prenat_addr),
NIPQUAD(e->postnat_addr));
list_del(&e->glist); list_del(&e->glist);
list_del(&e->grlist); list_del(&e->grlist);
e->prenat_addr = 0; e->prenat_addr = 0;
@@ -475,7 +498,8 @@ bind_new_prefix:
newrange.max_addr.ip = postnat_ip; newrange.max_addr.ip = postnat_ip;
newrange.min_proto = mr->min_proto; newrange.min_proto = mr->min_proto;
newrange.max_proto = mr->max_proto; newrange.max_proto = mr->max_proto;
return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->state->hook)); return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum));
no_rev_map: no_rev_map:
no_free_ip: no_free_ip:
spin_unlock_bh(&dnetmap_lock); spin_unlock_bh(&dnetmap_lock);
@@ -550,13 +574,12 @@ static int dnetmap_seq_show(struct seq_file *seq, void *v)
const struct dnetmap_entry *e = v; const struct dnetmap_entry *e = v;
if((e->flags & XT_DNETMAP_STATIC) == 0){ if((e->flags & XT_DNETMAP_STATIC) == 0){
seq_printf(seq, "%pI4 -> %pI4 --- ttl: %d lasthit: %lu\n", seq_printf(seq, NIPQUAD_FMT " -> " NIPQUAD_FMT " --- ttl: %d lasthit: %lu\n",
&e->prenat_addr, &e->postnat_addr, NIPQUAD(e->prenat_addr), NIPQUAD(e->postnat_addr),
(int)(e->stamp - jiffies) / HZ, (int)(e->stamp - jiffies) / HZ, (e->stamp - jtimeout) / HZ);
(e->stamp - jtimeout) / HZ);
}else{ }else{
seq_printf(seq, "%pI4 -> %pI4 --- ttl: S lasthit: S\n", seq_printf(seq, NIPQUAD_FMT " -> " NIPQUAD_FMT " --- ttl: S lasthit: S\n",
&e->prenat_addr, &e->postnat_addr); NIPQUAD(e->prenat_addr), NIPQUAD(e->postnat_addr));
} }
return 0; return 0;
} }
@@ -570,24 +593,27 @@ static const struct seq_operations dnetmap_seq_ops = {
static int dnetmap_seq_open(struct inode *inode, struct file *file) static int dnetmap_seq_open(struct inode *inode, struct file *file)
{ {
struct proc_dir_entry *pde = PDE(inode);
struct dnetmap_iter_state *st; struct dnetmap_iter_state *st;
st = __seq_open_private(file, &dnetmap_seq_ops, sizeof(*st)); st = __seq_open_private(file, &dnetmap_seq_ops, sizeof(*st));
if (st == NULL) if (st == NULL)
return -ENOMEM; return -ENOMEM;
st->p = PDE_DATA(inode); st->p = pde->data;
return 0; return 0;
} }
static ssize_t static ssize_t
dnetmap_tg_proc_write(struct file *file, const char __user *input,size_t size, loff_t *loff) dnetmap_tg_proc_write(struct file *file, const char __user *input,size_t size, loff_t *loff)
{ {
struct dnetmap_prefix *p = PDE_DATA(file_inode(file)); const struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
struct dnetmap_prefix *p = pde->data;
struct dnetmap_entry *e; struct dnetmap_entry *e;
char buf[sizeof("+192.168.100.100:200.200.200.200")]; char buf[sizeof("+192.168.100.100:200.200.200.200")];
const char *c = buf; const char *c = buf;
const char *c2; const char *c2;
//union nf_inet_addr addr = {};
__be32 addr1,addr2; __be32 addr1,addr2;
bool add; bool add;
char str[25]; char str[25];
@@ -678,8 +704,8 @@ dnetmap_tg_proc_write(struct file *file, const char __user *input,size_t size, l
if(e != NULL){ if(e != NULL){
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": timeout binding %pI4 -> %pI4\n", ": timeout binding " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
&e->prenat_addr, &e->postnat_addr); NIPQUAD(e->prenat_addr), NIPQUAD(e->postnat_addr) );
list_del(&e->glist); list_del(&e->glist);
list_del(&e->grlist); list_del(&e->grlist);
}else{ }else{
@@ -701,7 +727,7 @@ dnetmap_tg_proc_write(struct file *file, const char __user *input,size_t size, l
(e->postnat_addr)]); (e->postnat_addr)]);
list_del(&e->lru_list); list_del(&e->lru_list);
sprintf(str, "%pI4:%pI4", &addr1, &addr2); sprintf(str, NIPQUAD_FMT ":" NIPQUAD_FMT, NIPQUAD(addr1),NIPQUAD(addr2));
printk(KERN_INFO KBUILD_MODNAME ": adding static binding %s\n", str); printk(KERN_INFO KBUILD_MODNAME ": adding static binding %s\n", str);
// case of removing binding // case of removing binding
@@ -717,8 +743,8 @@ dnetmap_tg_proc_write(struct file *file, const char __user *input,size_t size, l
if(e != NULL){ if(e != NULL){
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": remove binding %pI4 -> %pI4\n", ": remove binding " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
&e->prenat_addr, &e->postnat_addr); NIPQUAD(e->prenat_addr), NIPQUAD(e->postnat_addr) );
list_del(&e->glist); list_del(&e->glist);
list_del(&e->grlist); list_del(&e->grlist);
if(e->flags & XT_DNETMAP_STATIC){ if(e->flags & XT_DNETMAP_STATIC){
@@ -748,17 +774,20 @@ dnetmap_tg_proc_write(struct file *file, const char __user *input,size_t size, l
} }
static const struct proc_ops dnetmap_tg_fops = { static const struct file_operations dnetmap_tg_fops = {
.proc_open = dnetmap_seq_open, .open = dnetmap_seq_open,
.proc_read = seq_read, .read = seq_read,
.proc_write = dnetmap_tg_proc_write, .write = dnetmap_tg_proc_write,
.proc_release = seq_release_private, .release = seq_release_private,
.owner = THIS_MODULE,
}; };
/* for statistics */ /* for statistics */
static int dnetmap_stat_proc_show(struct seq_file *m, void *data) static int dnetmap_stat_proc_read(char __user *buffer, char **start,
off_t offset, int length, int *eof,
void *data)
{ {
const struct dnetmap_prefix *p = m->private; const struct dnetmap_prefix *p = data;
struct dnetmap_entry *e; struct dnetmap_entry *e;
unsigned int used, used_static, all; unsigned int used, used_static, all;
long int ttl, sum_ttl; long int ttl, sum_ttl;
@@ -784,25 +813,16 @@ static int dnetmap_stat_proc_show(struct seq_file *m, void *data)
} }
sum_ttl = used > 0 ? sum_ttl / (used * HZ) : 0; sum_ttl = used > 0 ? sum_ttl / (used * HZ) : 0;
seq_printf(m, "%u %u %u %ld %s\n", used, used_static, all, sum_ttl,(p->flags & XT_DNETMAP_PERSISTENT ? "persistent" : "")); sprintf(buffer, "%u %u %u %ld %s\n", used, used_static, all, sum_ttl,(p->flags & XT_DNETMAP_PERSISTENT ? "persistent" : ""));
if (length >= strlen(buffer))
*eof = true;
spin_unlock_bh(&dnetmap_lock); spin_unlock_bh(&dnetmap_lock);
return 0; return strlen(buffer);
} }
static int dnetmap_stat_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, dnetmap_stat_proc_show, PDE_DATA(inode));
}
static const struct proc_ops dnetmap_stat_proc_fops = {
.proc_open = dnetmap_stat_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static int __net_init dnetmap_proc_net_init(struct net *net) static int __net_init dnetmap_proc_net_init(struct net *net)
{ {
struct dnetmap_net *dnetmap_net = dnetmap_pernet(net); struct dnetmap_net *dnetmap_net = dnetmap_pernet(net);
@@ -815,7 +835,7 @@ static int __net_init dnetmap_proc_net_init(struct net *net)
static void __net_exit dnetmap_proc_net_exit(struct net *net) static void __net_exit dnetmap_proc_net_exit(struct net *net)
{ {
remove_proc_entry("xt_DNETMAP", net->proc_net); proc_net_remove(net, "xt_DNETMAP");
} }
#else #else
@@ -916,18 +936,6 @@ static void __exit dnetmap_tg_exit(void)
xt_unregister_target(&dnetmap_tg_reg); xt_unregister_target(&dnetmap_tg_reg);
unregister_pernet_subsys(&dnetmap_net_ops); unregister_pernet_subsys(&dnetmap_net_ops);
} }
#else /* CONFIG_NF_NAT */
static int __init dnetmap_tg_init(void)
{
pr_err("CONFIG_NF_NAT is not available in your kernel, hence this module cannot function.");
return -EINVAL;
}
static void __exit dnetmap_tg_exit(void) {}
#endif
module_init(dnetmap_tg_init); module_init(dnetmap_tg_init);
module_exit(dnetmap_tg_exit); module_exit(dnetmap_tg_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Kierdelewicz <marek@piasta.pl>");
MODULE_DESCRIPTION("Xtables: dynamic two-way 1:1 NAT mapping of IPv4 addresses");
MODULE_ALIAS("ipt_DNETMAP");

View File

@@ -24,8 +24,9 @@
#include "compat_xtables.h" #include "compat_xtables.h"
static unsigned int static unsigned int
echo_tg6(struct sk_buff *oldskb, const struct xt_action_param *par) echo_tg6(struct sk_buff **poldskb, const struct xt_action_param *par)
{ {
const struct sk_buff *oldskb = *poldskb;
const struct udphdr *oldudp; const struct udphdr *oldudp;
const struct ipv6hdr *oldip; const struct ipv6hdr *oldip;
struct udphdr *newudp, oldudp_buf; struct udphdr *newudp, oldudp_buf;
@@ -35,10 +36,10 @@ echo_tg6(struct sk_buff *oldskb, const struct xt_action_param *par)
void *payload; void *payload;
struct flowi6 fl; struct flowi6 fl;
struct dst_entry *dst = NULL; struct dst_entry *dst = NULL;
struct net *net = dev_net((par->state->in != NULL) ? par->state->in : par->state->out); struct net *net = dev_net((par->in != NULL) ? par->in : par->out);
/* This allows us to do the copy operation in fewer lines of code. */ /* This allows us to do the copy operation in fewer lines of code. */
if (skb_linearize(oldskb) < 0) if (skb_linearize(*poldskb) < 0)
return NF_DROP; return NF_DROP;
oldip = ipv6_hdr(oldskb); oldip = ipv6_hdr(oldskb);
@@ -76,7 +77,6 @@ echo_tg6(struct sk_buff *oldskb, const struct xt_action_param *par)
payload = skb_header_pointer(oldskb, par->thoff + payload = skb_header_pointer(oldskb, par->thoff +
sizeof(*oldudp), data_len, NULL); sizeof(*oldudp), data_len, NULL);
memcpy(skb_put(newskb, data_len), payload, data_len); memcpy(skb_put(newskb, data_len), payload, data_len);
newip->payload_len = htons(newskb->len);
#if 0 #if 0
/* /*
@@ -97,11 +97,7 @@ echo_tg6(struct sk_buff *oldskb, const struct xt_action_param *par)
memcpy(&fl.daddr, &newip->daddr, sizeof(fl.daddr)); memcpy(&fl.daddr, &newip->daddr, sizeof(fl.daddr));
fl.fl6_sport = newudp->source; fl.fl6_sport = newudp->source;
fl.fl6_dport = newudp->dest; fl.fl6_dport = newudp->dest;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
security_skb_classify_flow((struct sk_buff *)oldskb, flowi6_to_flowi_common(&fl));
#else
security_skb_classify_flow((struct sk_buff *)oldskb, flowi6_to_flowi(&fl)); security_skb_classify_flow((struct sk_buff *)oldskb, flowi6_to_flowi(&fl));
#endif
dst = ip6_route_output(net, NULL, &fl); dst = ip6_route_output(net, NULL, &fl);
if (dst == NULL || dst->error != 0) { if (dst == NULL || dst->error != 0) {
dst_release(dst); dst_release(dst);
@@ -116,8 +112,8 @@ echo_tg6(struct sk_buff *oldskb, const struct xt_action_param *par)
if (newskb->len > dst_mtu(skb_dst(newskb))) if (newskb->len > dst_mtu(skb_dst(newskb)))
goto free_nskb; goto free_nskb;
nf_ct_attach(newskb, oldskb); nf_ct_attach(newskb, *poldskb);
ip6_local_out(par_net(par), par->state->sk, newskb); ip6_local_out(newskb);
return NF_DROP; return NF_DROP;
free_nskb: free_nskb:
@@ -126,8 +122,9 @@ echo_tg6(struct sk_buff *oldskb, const struct xt_action_param *par)
} }
static unsigned int static unsigned int
echo_tg4(struct sk_buff *oldskb, const struct xt_action_param *par) echo_tg4(struct sk_buff **poldskb, const struct xt_action_param *par)
{ {
const struct sk_buff *oldskb = *poldskb;
const struct udphdr *oldudp; const struct udphdr *oldudp;
const struct iphdr *oldip; const struct iphdr *oldip;
struct udphdr *newudp, oldudp_buf; struct udphdr *newudp, oldudp_buf;
@@ -137,7 +134,7 @@ echo_tg4(struct sk_buff *oldskb, const struct xt_action_param *par)
void *payload; void *payload;
/* This allows us to do the copy operation in fewer lines of code. */ /* This allows us to do the copy operation in fewer lines of code. */
if (skb_linearize(oldskb) < 0) if (skb_linearize(*poldskb) < 0)
return NF_DROP; return NF_DROP;
oldip = ip_hdr(oldskb); oldip = ip_hdr(oldskb);
@@ -161,8 +158,8 @@ echo_tg4(struct sk_buff *oldskb, const struct xt_action_param *par)
newip->version = oldip->version; newip->version = oldip->version;
newip->ihl = sizeof(*newip) / 4; newip->ihl = sizeof(*newip) / 4;
newip->tos = oldip->tos; newip->tos = oldip->tos;
newip->id = oldip->id; newip->id = 0;
newip->frag_off = 0; newip->frag_off = htons(IP_DF);
newip->protocol = oldip->protocol; newip->protocol = oldip->protocol;
newip->check = 0; newip->check = 0;
newip->saddr = oldip->daddr; newip->saddr = oldip->daddr;
@@ -178,7 +175,6 @@ echo_tg4(struct sk_buff *oldskb, const struct xt_action_param *par)
payload = skb_header_pointer(oldskb, par->thoff + payload = skb_header_pointer(oldskb, par->thoff +
sizeof(*oldudp), data_len, NULL); sizeof(*oldudp), data_len, NULL);
memcpy(skb_put(newskb, data_len), payload, data_len); memcpy(skb_put(newskb, data_len), payload, data_len);
newip->tot_len = htons(newskb->len);
#if 0 #if 0
/* /*
@@ -195,8 +191,8 @@ echo_tg4(struct sk_buff *oldskb, const struct xt_action_param *par)
/* ip_route_me_harder expects the skb's dst to be set */ /* ip_route_me_harder expects the skb's dst to be set */
skb_dst_set(newskb, dst_clone(skb_dst(oldskb))); skb_dst_set(newskb, dst_clone(skb_dst(oldskb)));
if (ip_route_me_harder(par_net(par), par->state->sk, newskb,
RTN_UNSPEC) != 0) if (ip_route_me_harder(&newskb, RTN_UNSPEC) != 0)
goto free_nskb; goto free_nskb;
newip->ttl = ip4_dst_hoplimit(skb_dst(newskb)); newip->ttl = ip4_dst_hoplimit(skb_dst(newskb));
@@ -206,8 +202,8 @@ echo_tg4(struct sk_buff *oldskb, const struct xt_action_param *par)
if (newskb->len > dst_mtu(skb_dst(newskb))) if (newskb->len > dst_mtu(skb_dst(newskb)))
goto free_nskb; goto free_nskb;
nf_ct_attach(newskb, oldskb); nf_ct_attach(newskb, *poldskb);
ip_local_out(par_net(par), newskb->sk, newskb); ip_local_out(newskb);
return NF_DROP; return NF_DROP;
free_nskb: free_nskb:

View File

@@ -25,9 +25,10 @@ MODULE_ALIAS("ipt_IPMARK");
MODULE_ALIAS("ip6t_IPMARK"); MODULE_ALIAS("ip6t_IPMARK");
static unsigned int static unsigned int
ipmark_tg4(struct sk_buff *skb, const struct xt_action_param *par) ipmark_tg4(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
const struct xt_ipmark_tginfo *ipmarkinfo = par->targinfo; const struct xt_ipmark_tginfo *ipmarkinfo = par->targinfo;
const struct sk_buff *skb = *pskb;
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
__u32 mark; __u32 mark;
@@ -60,9 +61,10 @@ static __u32 ipmark_from_ip6(const struct in6_addr *a, unsigned int s)
} }
static unsigned int static unsigned int
ipmark_tg6(struct sk_buff *skb, const struct xt_action_param *par) ipmark_tg6(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
const struct xt_ipmark_tginfo *info = par->targinfo; const struct xt_ipmark_tginfo *info = par->targinfo;
const struct sk_buff *skb = *pskb;
const struct ipv6hdr *iph = ipv6_hdr(skb); const struct ipv6hdr *iph = ipv6_hdr(skb);
__u32 mark; __u32 mark;

View File

@@ -52,24 +52,20 @@ static void logmark_ct(const struct nf_conn *ct, enum ip_conntrack_info ctinfo)
printk("EXPECTED"); printk("EXPECTED");
prev = true; prev = true;
} }
if (ct->status & IPS_SEEN_REPLY) { if (ct->status & IPS_SEEN_REPLY)
printk("%s""SEEN_REPLY", prev ? "," : ""); printk("%s""SEEN_REPLY", prev++ ? "," : "");
prev = true; if (ct->status & IPS_ASSURED)
} printk("%s""ASSURED", prev++ ? "," : "");
if (ct->status & IPS_ASSURED) { if (ct->status & IPS_CONFIRMED)
printk("%s""ASSURED", prev ? "," : ""); printk("%s""CONFIRMED", prev++ ? "," : "");
prev = true; printk(" lifetime=%lus",
} (jiffies - ct->timeout.expires) / HZ);
if (ct->status & IPS_CONFIRMED) {
printk("%s""CONFIRMED", prev ? "," : "");
prev = true;
}
printk(" lifetime=%lus", nf_ct_expires(ct) / HZ);
} }
static unsigned int static unsigned int
logmark_tg(struct sk_buff *skb, const struct xt_action_param *par) logmark_tg(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
const struct sk_buff *skb = *pskb;
const struct xt_logmark_tginfo *info = par->targinfo; const struct xt_logmark_tginfo *info = par->targinfo;
const struct nf_conn *ct; const struct nf_conn *ct;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
@@ -77,13 +73,15 @@ logmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
printk("<%u>%.*s""iif=%d hook=%s nfmark=0x%x " printk("<%u>%.*s""iif=%d hook=%s nfmark=0x%x "
"secmark=0x%x classify=0x%x", "secmark=0x%x classify=0x%x",
info->level, (unsigned int)sizeof(info->prefix), info->prefix, info->level, (unsigned int)sizeof(info->prefix), info->prefix,
skb_ifindex(skb), hook_names[par->state->hook], skb_ifindex(skb), hook_names[par->hooknum],
skb_nfmark(skb), skb_secmark(skb), skb->priority); skb_nfmark(skb), skb_secmark(skb), skb->priority);
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
printk(" ctdir=%s", dir_names[ctinfo >= IP_CT_IS_REPLY]); printk(" ctdir=%s", dir_names[ctinfo >= IP_CT_IS_REPLY]);
if (ct == NULL) if (ct == NULL)
printk(" ct=NULL ctmark=NULL ctstate=INVALID ctstatus=NONE"); printk(" ct=NULL ctmark=NULL ctstate=INVALID ctstatus=NONE");
else if (nf_ct_is_untracked(ct))
printk(" ct=UNTRACKED ctmark=NULL ctstate=UNTRACKED ctstatus=NONE");
else else
logmark_ct(ct, ctinfo); logmark_ct(ct, ctinfo);

View File

@@ -1,156 +0,0 @@
/*
* Protocol modification target for IP tables
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include <net/checksum.h>
#include <linux/netfilter/x_tables.h>
#include "xt_PROTO.h"
MODULE_AUTHOR("Shanker Wang <i@innull.com>");
MODULE_DESCRIPTION("Xtables: Protocol field modification target");
MODULE_LICENSE("GPL");
static unsigned int
proto_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
struct iphdr *iph;
const struct xt_PROTO_info *info = par->targinfo;
int new_proto;
if (skb_ensure_writable(skb, skb->len))
return NF_DROP;
iph = ip_hdr(skb);
new_proto = iph->protocol;
if (info->mode & (1 << XT_PROTO_SET))
new_proto = info->proto;
if (new_proto != iph->protocol) {
csum_replace2(&iph->check, htons(iph->protocol & 0xff),
htons(new_proto & 0xff));
iph->protocol = new_proto;
}
return XT_CONTINUE;
}
static unsigned int
proto_tg6(struct sk_buff *skb, const struct xt_action_param *par)
{
struct ipv6hdr *ip6h;
const struct xt_PROTO_info *info = par->targinfo;
u8 *nexthdr;
unsigned int hdr_offset;
__be16 *fp;
if (skb_ensure_writable(skb, skb->len))
return NF_DROP;
ip6h = ipv6_hdr(skb);
nexthdr = &ip6h->nexthdr;
hdr_offset = sizeof(struct ipv6hdr);
for (;;) {
struct ipv6_opt_hdr _opthdr, *opthp;
unsigned int hdrlen;
unsigned short _frag_off;
if (!ipv6_ext_hdr(*nexthdr) || *nexthdr == NEXTHDR_NONE)
break;
opthp = skb_header_pointer(skb, skb_network_offset(skb) + hdr_offset, sizeof(_opthdr), &_opthdr);
if (!opthp)
return NF_DROP;
if (*nexthdr == NEXTHDR_FRAGMENT) {
if (info->mode & (1 << XT_PROTO_STOP_AT_FRAG))
break;
fp = skb_header_pointer(skb, skb_network_offset(skb) +
hdr_offset + offsetof(struct frag_hdr, frag_off),
sizeof(_frag_off), &_frag_off);
if (!fp)
return NF_DROP;
_frag_off = ntohs(*fp) & ~0x7;
if (_frag_off) { // if the packet is not the first fragment
if (!ipv6_ext_hdr(opthp->nexthdr) || opthp->nexthdr == NEXTHDR_NONE ||
(info->mode & (1 << XT_PROTO_STOP_AT_AUTH) && opthp->nexthdr == NEXTHDR_AUTH)) {
nexthdr = &((struct ipv6_opt_hdr *)(skb_network_header(skb) + hdr_offset))->nexthdr;
break;
} else {
return XT_CONTINUE;
}
}
hdrlen = 8;
} else if(*nexthdr == NEXTHDR_AUTH) {
if (info->mode & (1 << XT_PROTO_STOP_AT_AUTH))
break;
hdrlen = (opthp->hdrlen + 2) << 2;
} else {
hdrlen = ipv6_optlen(opthp);
}
nexthdr = &((struct ipv6_opt_hdr *)(skb_network_header(skb) + hdr_offset))->nexthdr;
hdr_offset += hdrlen;
}
if (info->mode & (1 << XT_PROTO_SET))
*nexthdr = info->proto;
return XT_CONTINUE;
}
static int proto_tg_check(const struct xt_tgchk_param *par)
{
const struct xt_PROTO_info *info = par->targinfo;
if ((info->mode & (1 << XT_PROTO_SET)) == 0) {
pr_info_ratelimited("Did not specify any proto to set\n");
return -EINVAL;
}
if (par->family != NFPROTO_IPV6 && (info->mode & ((1 << XT_PROTO_STOP_AT_FRAG) | (1 << XT_PROTO_STOP_AT_AUTH))) != 0) {
pr_info_ratelimited("Must not specify stop-at-frag and stop-at-auth on non-ipv6 targets\n");
return -EPROTOTYPE;
}
return 0;
}
static struct xt_target proto_tg_reg[] __read_mostly = {
{
.name = "PROTO",
.revision = 0,
.family = NFPROTO_IPV4,
.target = proto_tg,
.targetsize = sizeof(struct xt_PROTO_info),
.table = "mangle",
.checkentry = proto_tg_check,
.me = THIS_MODULE,
},
{
.name = "PROTO",
.revision = 0,
.family = NFPROTO_IPV6,
.target = proto_tg6,
.targetsize = sizeof(struct xt_PROTO_info),
.table = "mangle",
.checkentry = proto_tg_check,
.me = THIS_MODULE,
},
};
static int __init proto_tg_init(void)
{
return xt_register_targets(proto_tg_reg, ARRAY_SIZE(proto_tg_reg));
}
static void __exit proto_tg_exit(void)
{
xt_unregister_targets(proto_tg_reg, ARRAY_SIZE(proto_tg_reg));
}
module_init(proto_tg_init);
module_exit(proto_tg_exit);
MODULE_ALIAS("ipt_PROTO");
MODULE_ALIAS("ip6t_PROTO");

View File

@@ -1,20 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/* Protocol modification module for IP tables */
#ifndef _XT_PROTO_H
#define _XT_PROTO_H
#include <linux/types.h>
enum {
XT_PROTO_SET = 0,
XT_PROTO_STOP_AT_FRAG = 1,
XT_PROTO_STOP_AT_AUTH = 2
};
struct xt_PROTO_info {
__u8 mode;
__u8 proto;
};
#endif

354
extensions/xt_RAWNAT.c Normal file
View File

@@ -0,0 +1,354 @@
/*
* "RAWNAT" target extension for Xtables - untracked NAT
* Copyright © Jan Engelhardt, 2008 - 2009
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License; either
* version 2 of the License, or any later version, as published by the
* Free Software Foundation.
*/
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/version.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_conntrack_common.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include "compat_xtables.h"
#include "xt_RAWNAT.h"
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
# define WITH_IPV6 1
#endif
static inline __be32
remask(__be32 addr, __be32 repl, unsigned int shift)
{
uint32_t mask = (shift == 32) ? 0 : (~(uint32_t)0 >> shift);
return htonl((ntohl(addr) & mask) | (ntohl(repl) & ~mask));
}
#ifdef WITH_IPV6
static void
rawnat_ipv6_mask(__be32 *addr, const __be32 *repl, unsigned int mask)
{
switch (mask) {
case 0:
break;
case 1 ... 31:
addr[0] = remask(addr[0], repl[0], mask);
break;
case 32:
addr[0] = repl[0];
break;
case 33 ... 63:
addr[0] = repl[0];
addr[1] = remask(addr[1], repl[1], mask - 32);
break;
case 64:
addr[0] = repl[0];
addr[1] = repl[1];
break;
case 65 ... 95:
addr[0] = repl[0];
addr[1] = repl[1];
addr[2] = remask(addr[2], repl[2], mask - 64);
case 96:
addr[0] = repl[0];
addr[1] = repl[1];
addr[2] = repl[2];
break;
case 97 ... 127:
addr[0] = repl[0];
addr[1] = repl[1];
addr[2] = repl[2];
addr[3] = remask(addr[3], repl[3], mask - 96);
break;
case 128:
addr[0] = repl[0];
addr[1] = repl[1];
addr[2] = repl[2];
addr[3] = repl[3];
break;
}
}
#endif
static void rawnat4_update_l4(struct sk_buff *skb, __be32 oldip, __be32 newip)
{
struct iphdr *iph = ip_hdr(skb);
void *transport_hdr = (void *)iph + ip_hdrlen(skb);
struct tcphdr *tcph;
struct udphdr *udph;
bool cond;
switch (iph->protocol) {
case IPPROTO_TCP:
tcph = transport_hdr;
inet_proto_csum_replace4(&tcph->check, skb, oldip, newip, true);
break;
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
udph = transport_hdr;
cond = udph->check != 0;
cond |= skb->ip_summed == CHECKSUM_PARTIAL;
if (cond) {
inet_proto_csum_replace4(&udph->check, skb,
oldip, newip, true);
if (udph->check == 0)
udph->check = CSUM_MANGLED_0;
}
break;
}
}
static unsigned int rawnat4_writable_part(const struct iphdr *iph)
{
unsigned int wlen = sizeof(*iph);
switch (iph->protocol) {
case IPPROTO_TCP:
wlen += sizeof(struct tcphdr);
break;
case IPPROTO_UDP:
wlen += sizeof(struct udphdr);
break;
}
return wlen;
}
static unsigned int
rawsnat_tg4(struct sk_buff **pskb, const struct xt_action_param *par)
{
const struct xt_rawnat_tginfo *info = par->targinfo;
struct iphdr *iph;
__be32 new_addr;
iph = ip_hdr(*pskb);
new_addr = remask(iph->saddr, info->addr.ip, info->mask);
if (iph->saddr == new_addr)
return XT_CONTINUE;
if (!skb_make_writable(pskb, rawnat4_writable_part(iph)))
return NF_DROP;
iph = ip_hdr(*pskb);
csum_replace4(&iph->check, iph->saddr, new_addr);
rawnat4_update_l4(*pskb, iph->saddr, new_addr);
iph->saddr = new_addr;
return XT_CONTINUE;
}
static unsigned int
rawdnat_tg4(struct sk_buff **pskb, const struct xt_action_param *par)
{
const struct xt_rawnat_tginfo *info = par->targinfo;
struct iphdr *iph;
__be32 new_addr;
iph = ip_hdr(*pskb);
new_addr = remask(iph->daddr, info->addr.ip, info->mask);
if (iph->daddr == new_addr)
return XT_CONTINUE;
if (!skb_make_writable(pskb, rawnat4_writable_part(iph)))
return NF_DROP;
iph = ip_hdr(*pskb);
csum_replace4(&iph->check, iph->daddr, new_addr);
rawnat4_update_l4(*pskb, iph->daddr, new_addr);
iph->daddr = new_addr;
return XT_CONTINUE;
}
#ifdef WITH_IPV6
static bool rawnat6_prepare_l4(struct sk_buff **pskb, unsigned int *l4offset,
unsigned int *l4proto)
{
static const unsigned int types[] =
{IPPROTO_TCP, IPPROTO_UDP, IPPROTO_UDPLITE};
unsigned int i;
int err;
*l4proto = NEXTHDR_MAX;
for (i = 0; i < ARRAY_SIZE(types); ++i) {
err = ipv6_find_hdr(*pskb, l4offset, types[i], NULL, NULL);
if (err >= 0) {
*l4proto = types[i];
break;
}
if (err != -ENOENT)
return false;
}
switch (*l4proto) {
case IPPROTO_TCP:
if (!skb_make_writable(pskb, *l4offset + sizeof(struct tcphdr)))
return false;
break;
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
if (!skb_make_writable(pskb, *l4offset + sizeof(struct udphdr)))
return false;
break;
}
return true;
}
static void rawnat6_update_l4(struct sk_buff *skb, unsigned int l4proto,
unsigned int l4offset, const struct in6_addr *oldip,
const struct in6_addr *newip)
{
const struct ipv6hdr *iph = ipv6_hdr(skb);
struct tcphdr *tcph;
struct udphdr *udph;
unsigned int i;
bool cond;
switch (l4proto) {
case IPPROTO_TCP:
tcph = (void *)iph + l4offset;
for (i = 0; i < 4; ++i)
inet_proto_csum_replace4(&tcph->check, skb,
oldip->s6_addr32[i], newip->s6_addr32[i], true);
break;
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
udph = (void *)iph + l4offset;
cond = udph->check;
cond |= skb->ip_summed == CHECKSUM_PARTIAL;
if (cond) {
for (i = 0; i < 4; ++i)
inet_proto_csum_replace4(&udph->check, skb,
oldip->s6_addr32[i],
newip->s6_addr32[i], true);
if (udph->check == 0)
udph->check = CSUM_MANGLED_0;
}
break;
}
}
static unsigned int
rawsnat_tg6(struct sk_buff **pskb, const struct xt_action_param *par)
{
const struct xt_rawnat_tginfo *info = par->targinfo;
unsigned int l4offset = 0, l4proto;
struct ipv6hdr *iph;
struct in6_addr new_addr;
iph = ipv6_hdr(*pskb);
memcpy(&new_addr, &iph->saddr, sizeof(new_addr));
rawnat_ipv6_mask(new_addr.s6_addr32, info->addr.ip6, info->mask);
if (ipv6_addr_cmp(&iph->saddr, &new_addr) == 0)
return XT_CONTINUE;
if (!rawnat6_prepare_l4(pskb, &l4offset, &l4proto))
return NF_DROP;
iph = ipv6_hdr(*pskb);
rawnat6_update_l4(*pskb, l4proto, l4offset, &iph->saddr, &new_addr);
memcpy(&iph->saddr, &new_addr, sizeof(new_addr));
return XT_CONTINUE;
}
static unsigned int
rawdnat_tg6(struct sk_buff **pskb, const struct xt_action_param *par)
{
const struct xt_rawnat_tginfo *info = par->targinfo;
unsigned int l4offset = 0, l4proto;
struct ipv6hdr *iph;
struct in6_addr new_addr;
iph = ipv6_hdr(*pskb);
memcpy(&new_addr, &iph->daddr, sizeof(new_addr));
rawnat_ipv6_mask(new_addr.s6_addr32, info->addr.ip6, info->mask);
if (ipv6_addr_cmp(&iph->daddr, &new_addr) == 0)
return XT_CONTINUE;
if (!rawnat6_prepare_l4(pskb, &l4offset, &l4proto))
return NF_DROP;
iph = ipv6_hdr(*pskb);
rawnat6_update_l4(*pskb, l4proto, l4offset, &iph->daddr, &new_addr);
memcpy(&iph->daddr, &new_addr, sizeof(new_addr));
return XT_CONTINUE;
}
#endif
static int rawnat_tg_check(const struct xt_tgchk_param *par)
{
if (strcmp(par->table, "raw") == 0 ||
strcmp(par->table, "rawpost") == 0)
return 0;
printk(KERN_ERR KBUILD_MODNAME " may only be used in the \"raw\" or "
"\"rawpost\" table.\n");
return -EINVAL;
}
static struct xt_target rawnat_tg_reg[] __read_mostly = {
{
.name = "RAWSNAT",
.revision = 0,
.family = NFPROTO_IPV4,
.target = rawsnat_tg4,
.targetsize = sizeof(struct xt_rawnat_tginfo),
.checkentry = rawnat_tg_check,
.me = THIS_MODULE,
},
#ifdef WITH_IPV6
{
.name = "RAWSNAT",
.revision = 0,
.family = NFPROTO_IPV6,
.target = rawsnat_tg6,
.targetsize = sizeof(struct xt_rawnat_tginfo),
.checkentry = rawnat_tg_check,
.me = THIS_MODULE,
},
#endif
{
.name = "RAWDNAT",
.revision = 0,
.family = NFPROTO_IPV4,
.target = rawdnat_tg4,
.targetsize = sizeof(struct xt_rawnat_tginfo),
.checkentry = rawnat_tg_check,
.me = THIS_MODULE,
},
#ifdef WITH_IPV6
{
.name = "RAWDNAT",
.revision = 0,
.family = NFPROTO_IPV6,
.target = rawdnat_tg6,
.targetsize = sizeof(struct xt_rawnat_tginfo),
.checkentry = rawnat_tg_check,
.me = THIS_MODULE,
},
#endif
};
static int __init rawnat_tg_init(void)
{
return xt_register_targets(rawnat_tg_reg, ARRAY_SIZE(rawnat_tg_reg));
}
static void __exit rawnat_tg_exit(void)
{
xt_unregister_targets(rawnat_tg_reg, ARRAY_SIZE(rawnat_tg_reg));
}
module_init(rawnat_tg_init);
module_exit(rawnat_tg_exit);
MODULE_AUTHOR("Jan Engelhardt ");
MODULE_DESCRIPTION("Xtables: conntrack-less raw NAT");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_RAWSNAT");
MODULE_ALIAS("ipt_RAWDNAT");
MODULE_ALIAS("ip6t_RAWSNAT");
MODULE_ALIAS("ip6t_RAWDNAT");

9
extensions/xt_RAWNAT.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef _LINUX_NETFILTER_XT_TARGET_RAWNAT
#define _LINUX_NETFILTER_XT_TARGET_RAWNAT 1
struct xt_rawnat_tginfo {
union nf_inet_addr addr;
__u8 mask;
};
#endif /* _LINUX_NETFILTER_XT_TARGET_RAWNAT */

67
extensions/xt_STEAL.c Normal file
View File

@@ -0,0 +1,67 @@
/*
* "STEAL" demo target extension for Xtables
* written by Jan Engelhardt, 2008 - 2009
* placed in the Public Domain
*/
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/skbuff.h>
#include "compat_xtables.h"
static unsigned int
steal_tg(struct sk_buff **pskb, const struct xt_action_param *par)
{
kfree_skb(*pskb);
return NF_STOLEN;
}
static struct xt_target steal_tg_reg[] __read_mostly = {
{
.name = "STEAL",
.revision = 0,
.family = NFPROTO_UNSPEC,
.target = steal_tg,
.me = THIS_MODULE,
},
{
.name = "STEAL",
.revision = 0,
.family = NFPROTO_IPV6,
.target = steal_tg,
.me = THIS_MODULE,
},
{
.name = "STEAL",
.revision = 0,
.family = NFPROTO_ARP,
.target = steal_tg,
.me = THIS_MODULE,
},
{
.name = "STEAL",
.revision = 0,
.family = NFPROTO_BRIDGE,
.target = steal_tg,
.me = THIS_MODULE,
},
};
static int __init steal_tg_init(void)
{
return xt_register_targets(steal_tg_reg, ARRAY_SIZE(steal_tg_reg));
}
static void __exit steal_tg_exit(void)
{
xt_unregister_targets(steal_tg_reg, ARRAY_SIZE(steal_tg_reg));
}
module_init(steal_tg_init);
module_exit(steal_tg_exit);
MODULE_AUTHOR("Jan Engelhardt ");
MODULE_DESCRIPTION("Xtables: Silently DROP packets on output chain");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_STEAL");
MODULE_ALIAS("ip6t_STEAL");
MODULE_ALIAS("arpt_STEAL");
MODULE_ALIAS("ebt_STEAL");

View File

@@ -1,6 +1,6 @@
/* /*
* "SYSRQ" target extension for Xtables * "SYSRQ" target extension for Xtables
* Copyright Jan Engelhardt, 2016 * Copyright © Jan Engelhardt, 2008 - 2012
* *
* Based upon the ipt_SYSRQ idea by Marek Zalem <marek [at] terminus sk> * Based upon the ipt_SYSRQ idea by Marek Zalem <marek [at] terminus sk>
* *
@@ -21,7 +21,8 @@
#include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h> #include <linux/netfilter_ipv6/ip6_tables.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <crypto/hash.h> #include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include "compat_xtables.h" #include "compat_xtables.h"
@@ -49,7 +50,7 @@ MODULE_PARM_DESC(seqno, "sequence number for remote sysrq");
MODULE_PARM_DESC(debug, "debugging: 0=off, 1=on"); MODULE_PARM_DESC(debug, "debugging: 0=off, 1=on");
#ifdef WITH_CRYPTO #ifdef WITH_CRYPTO
static struct crypto_shash *sysrq_tfm; static struct crypto_hash *sysrq_tfm;
static int sysrq_digest_size; static int sysrq_digest_size;
static unsigned char *sysrq_digest_password; static unsigned char *sysrq_digest_password;
static unsigned char *sysrq_digest; static unsigned char *sysrq_digest;
@@ -74,7 +75,8 @@ static unsigned int sysrq_tg(const void *pdata, uint16_t len)
{ {
const char *data = pdata; const char *data = pdata;
int i, n; int i, n;
struct shash_desc desc; struct scatterlist sg[2];
struct hash_desc desc;
int ret; int ret;
long new_seqno = 0; long new_seqno = 0;
@@ -114,15 +116,16 @@ static unsigned int sysrq_tg(const void *pdata, uint16_t len)
} }
desc.tfm = sysrq_tfm; desc.tfm = sysrq_tfm;
ret = crypto_shash_init(&desc); desc.flags = 0;
ret = crypto_hash_init(&desc);
if (ret != 0) if (ret != 0)
goto hash_fail; goto hash_fail;
if (crypto_shash_update(&desc, data, n) != 0) sg_init_table(sg, 2);
goto hash_fail; sg_set_buf(&sg[0], data, n);
if (crypto_shash_update(&desc, sysrq_digest_password, i = strlen(sysrq_digest_password);
strlen(sysrq_digest_password)) != 0) sg_set_buf(&sg[1], sysrq_digest_password, i);
goto hash_fail; ret = crypto_hash_digest(&desc, sg, n + i, sysrq_digest);
if (crypto_shash_final(&desc, sysrq_digest) != 0) if (ret != 0)
goto hash_fail; goto hash_fail;
for (i = 0; i < sysrq_digest_size; ++i) { for (i = 0; i < sysrq_digest_size; ++i) {
@@ -186,8 +189,9 @@ static unsigned int sysrq_tg(const void *pdata, uint16_t len)
#endif #endif
static unsigned int static unsigned int
sysrq_tg4(struct sk_buff *skb, const struct xt_action_param *par) sysrq_tg4(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
struct sk_buff *skb = *pskb;
const struct iphdr *iph; const struct iphdr *iph;
const struct udphdr *udph; const struct udphdr *udph;
uint16_t len; uint16_t len;
@@ -204,19 +208,21 @@ sysrq_tg4(struct sk_buff *skb, const struct xt_action_param *par)
if (sysrq_debug) if (sysrq_debug)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": %pI4:%hu -> :%hu len=%u\n", ": " NIPQUAD_FMT ":%u -> :%u len=%u\n",
&iph->saddr, htons(udph->source), NIPQUAD(iph->saddr), htons(udph->source),
htons(udph->dest), len); htons(udph->dest), len);
#ifdef WITH_CRYPTO #ifdef WITH_CRYPTO
sprintf(sysrq_digest_password, "%pI4,%s", &iph->daddr, sysrq_password); sprintf(sysrq_digest_password, NIPQUAD_FMT ",%s",
NIPQUAD(iph->daddr), sysrq_password);
#endif #endif
return sysrq_tg((void *)udph + sizeof(struct udphdr), len); return sysrq_tg((void *)udph + sizeof(struct udphdr), len);
} }
#ifdef WITH_IPV6 #ifdef WITH_IPV6
static unsigned int static unsigned int
sysrq_tg6(struct sk_buff *skb, const struct xt_action_param *par) sysrq_tg6(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
struct sk_buff *skb = *pskb;
const struct ipv6hdr *iph; const struct ipv6hdr *iph;
const struct udphdr *udph; const struct udphdr *udph;
unsigned short frag_off; unsigned short frag_off;
@@ -237,11 +243,13 @@ sysrq_tg6(struct sk_buff *skb, const struct xt_action_param *par)
len = ntohs(udph->len) - sizeof(struct udphdr); len = ntohs(udph->len) - sizeof(struct udphdr);
if (sysrq_debug) if (sysrq_debug)
printk(KERN_INFO KBUILD_MODNAME ": %pI6:%hu -> :%hu len=%u\n", printk(KERN_INFO KBUILD_MODNAME
&iph->saddr, ntohs(udph->source), ": " NIP6_FMT ":%hu -> :%hu len=%u\n",
NIP6(iph->saddr), ntohs(udph->source),
ntohs(udph->dest), len); ntohs(udph->dest), len);
#ifdef WITH_CRYPTO #ifdef WITH_CRYPTO
sprintf(sysrq_digest_password, "%pI6,%s", &iph->daddr, sysrq_password); sprintf(sysrq_digest_password, NIP6_FMT ",%s",
NIP6(iph->daddr), sysrq_password);
#endif #endif
return sysrq_tg((void *)udph + sizeof(struct udphdr), len); return sysrq_tg((void *)udph + sizeof(struct udphdr), len);
} }
@@ -297,7 +305,7 @@ static void sysrq_crypto_exit(void)
{ {
#ifdef WITH_CRYPTO #ifdef WITH_CRYPTO
if (sysrq_tfm) if (sysrq_tfm)
crypto_free_shash(sysrq_tfm); crypto_free_hash(sysrq_tfm);
if (sysrq_digest) if (sysrq_digest)
kfree(sysrq_digest); kfree(sysrq_digest);
if (sysrq_hexdigest) if (sysrq_hexdigest)
@@ -310,10 +318,10 @@ static void sysrq_crypto_exit(void)
static int __init sysrq_crypto_init(void) static int __init sysrq_crypto_init(void)
{ {
#if defined(WITH_CRYPTO) #if defined(WITH_CRYPTO)
struct timespec64 now; struct timeval now;
int ret; int ret;
sysrq_tfm = crypto_alloc_shash(sysrq_hash, 0, 0); sysrq_tfm = crypto_alloc_hash(sysrq_hash, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(sysrq_tfm)) { if (IS_ERR(sysrq_tfm)) {
printk(KERN_WARNING KBUILD_MODNAME printk(KERN_WARNING KBUILD_MODNAME
": Error: Could not find or load %s hash\n", ": Error: Could not find or load %s hash\n",
@@ -322,7 +330,7 @@ static int __init sysrq_crypto_init(void)
sysrq_tfm = NULL; sysrq_tfm = NULL;
goto fail; goto fail;
} }
sysrq_digest_size = crypto_shash_digestsize(sysrq_tfm); sysrq_digest_size = crypto_hash_digestsize(sysrq_tfm);
sysrq_digest = kmalloc(sysrq_digest_size, GFP_KERNEL); sysrq_digest = kmalloc(sysrq_digest_size, GFP_KERNEL);
ret = -ENOMEM; ret = -ENOMEM;
if (sysrq_digest == NULL) if (sysrq_digest == NULL)
@@ -335,7 +343,7 @@ static int __init sysrq_crypto_init(void)
sizeof(sysrq_password), GFP_KERNEL); sizeof(sysrq_password), GFP_KERNEL);
if (sysrq_digest_password == NULL) if (sysrq_digest_password == NULL)
goto fail; goto fail;
ktime_get_real_ts64(&now); do_gettimeofday(&now);
sysrq_seqno = now.tv_sec; sysrq_seqno = now.tv_sec;
return 0; return 0;

View File

@@ -79,7 +79,7 @@ static bool xttarpit_tarpit(struct tcphdr *tcph, const struct tcphdr *oth)
#if 0 #if 0
/* Rate-limit replies to !SYN,ACKs */ /* Rate-limit replies to !SYN,ACKs */
if (!oth->syn && oth->ack) if (!oth->syn && oth->ack)
if (!xrlim_allow(&ort->dst, HZ)) if (!xrlim_allow(rt_dst(ort), HZ))
return false; return false;
#endif #endif
@@ -107,8 +107,8 @@ static bool xttarpit_honeypot(struct tcphdr *tcph, const struct tcphdr *oth,
tcph->syn = true; tcph->syn = true;
tcph->ack = true; tcph->ack = true;
tcph->window = oth->window & tcph->window = oth->window &
((prandom_u32() & 0x1f) - 0xf); ((net_random() & 0x1f) - 0xf);
tcph->seq = htonl(prandom_u32() & ~oth->seq); tcph->seq = htonl(net_random() & ~oth->seq);
tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn); tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn);
} }
@@ -117,7 +117,7 @@ static bool xttarpit_honeypot(struct tcphdr *tcph, const struct tcphdr *oth,
tcph->syn = false; tcph->syn = false;
tcph->ack = true; tcph->ack = true;
tcph->window = oth->window & tcph->window = oth->window &
((prandom_u32() & 0x1f) - 0xf); ((net_random() & 0x1f) - 0xf);
tcph->ack_seq = payload > 100 ? tcph->ack_seq = payload > 100 ?
htonl(ntohl(oth->seq) + payload) : htonl(ntohl(oth->seq) + payload) :
oth->seq; oth->seq;
@@ -170,8 +170,8 @@ static bool tarpit_generic(struct tcphdr *tcph, const struct tcphdr *oth,
return true; return true;
} }
static void tarpit_tcp4(const struct xt_action_param *par, static void tarpit_tcp4(struct sk_buff *oldskb, unsigned int hook,
struct sk_buff *oldskb, unsigned int mode) unsigned int mode)
{ {
struct tcphdr _otcph, *tcph; struct tcphdr _otcph, *tcph;
const struct tcphdr *oth; const struct tcphdr *oth;
@@ -191,8 +191,7 @@ static void tarpit_tcp4(const struct xt_action_param *par,
return; return;
/* Check checksum. */ /* Check checksum. */
if (nf_ip_checksum(oldskb, par->state->hook, ip_hdrlen(oldskb), if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
IPPROTO_TCP))
return; return;
/* /*
@@ -206,11 +205,7 @@ static void tarpit_tcp4(const struct xt_action_param *par,
return; return;
/* This packet will not be the same as the other: clear nf fields */ /* This packet will not be the same as the other: clear nf fields */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
nf_reset_ct(nskb);
#else
nf_reset(nskb); nf_reset(nskb);
#endif
skb_nfmark(nskb) = 0; skb_nfmark(nskb) = 0;
skb_init_secmark(nskb); skb_init_secmark(nskb);
skb_shinfo(nskb)->gso_size = 0; skb_shinfo(nskb)->gso_size = 0;
@@ -254,20 +249,14 @@ static void tarpit_tcp4(const struct xt_action_param *par,
niph->id = ~oldhdr->id + 1; niph->id = ~oldhdr->id + 1;
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL &&
if (par->state->hook != NF_INET_FORWARD || nskb->nf_bridge->mask & BRNF_BRIDGED))
((struct nf_bridge_info *)skb_ext_find(nskb, SKB_EXT_BRIDGE_NF) != NULL &&
((struct nf_bridge_info *)skb_ext_find(nskb, SKB_EXT_BRIDGE_NF))->physoutdev))
#else #else
if (par->state->hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL && if (hook != NF_INET_FORWARD)
nskb->nf_bridge->physoutdev != NULL))
#endif
#else
if (par->state->hook != NF_INET_FORWARD)
#endif #endif
addr_type = RTN_LOCAL; addr_type = RTN_LOCAL;
if (ip_route_me_harder(par_net(par), par->state->sk, nskb, addr_type) != 0) if (ip_route_me_harder(&nskb, addr_type))
goto free_nskb; goto free_nskb;
else else
niph = ip_hdr(nskb); niph = ip_hdr(nskb);
@@ -289,8 +278,9 @@ static void tarpit_tcp4(const struct xt_action_param *par,
goto free_nskb; goto free_nskb;
nf_ct_attach(nskb, oldskb); nf_ct_attach(nskb, oldskb);
NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, par_net(par), nskb->sk, nskb,
NULL, skb_dst(nskb)->dev, dst_output); NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, nskb, NULL,
skb_dst(nskb)->dev, dst_output);
return; return;
free_nskb: free_nskb:
@@ -298,8 +288,8 @@ static void tarpit_tcp4(const struct xt_action_param *par,
} }
#ifdef WITH_IPV6 #ifdef WITH_IPV6
static void tarpit_tcp6(const struct xt_action_param *par, static void tarpit_tcp6(struct sk_buff *oldskb, unsigned int hook,
struct sk_buff *oldskb, unsigned int mode) unsigned int mode)
{ {
struct sk_buff *nskb; struct sk_buff *nskb;
struct tcphdr *tcph, oth; struct tcphdr *tcph, oth;
@@ -352,11 +342,7 @@ static void tarpit_tcp6(const struct xt_action_param *par,
} }
/* This packet will not be the same as the other: clear nf fields */ /* This packet will not be the same as the other: clear nf fields */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
nf_reset_ct(nskb);
#else
nf_reset(nskb); nf_reset(nskb);
#endif
skb_nfmark(nskb) = 0; skb_nfmark(nskb) = 0;
skb_init_secmark(nskb); skb_init_secmark(nskb);
skb_shinfo(nskb)->gso_size = 0; skb_shinfo(nskb)->gso_size = 0;
@@ -400,14 +386,16 @@ static void tarpit_tcp6(const struct xt_action_param *par,
&ipv6_hdr(nskb)->daddr, sizeof(struct tcphdr), &ipv6_hdr(nskb)->daddr, sizeof(struct tcphdr),
IPPROTO_TCP, IPPROTO_TCP,
csum_partial(tcph, sizeof(struct tcphdr), 0)); csum_partial(tcph, sizeof(struct tcphdr), 0));
if (ip6_route_me_harder(par_net(par), nskb->sk, nskb))
if (ip6_route_me_harder(nskb))
goto free_nskb; goto free_nskb;
nskb->ip_summed = CHECKSUM_NONE; nskb->ip_summed = CHECKSUM_NONE;
nf_ct_attach(nskb, oldskb); nf_ct_attach(nskb, oldskb);
NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, par_net(par), nskb->sk, nskb,
NULL, skb_dst(nskb)->dev, dst_output); NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, nskb, NULL,
skb_dst(nskb)->dev, dst_output);
return; return;
free_nskb: free_nskb:
@@ -416,8 +404,9 @@ static void tarpit_tcp6(const struct xt_action_param *par,
#endif #endif
static unsigned int static unsigned int
tarpit_tg4(struct sk_buff *skb, const struct xt_action_param *par) tarpit_tg4(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
const struct sk_buff *skb = *pskb;
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
const struct rtable *rt = skb_rtable(skb); const struct rtable *rt = skb_rtable(skb);
const struct xt_tarpit_tginfo *info = par->targinfo; const struct xt_tarpit_tginfo *info = par->targinfo;
@@ -445,14 +434,16 @@ tarpit_tg4(struct sk_buff *skb, const struct xt_action_param *par)
/* We are not interested in fragments */ /* We are not interested in fragments */
if (iph->frag_off & htons(IP_OFFSET)) if (iph->frag_off & htons(IP_OFFSET))
return NF_DROP; return NF_DROP;
tarpit_tcp4(par, skb, info->variant);
tarpit_tcp4(*pskb, par->hooknum, info->variant);
return NF_DROP; return NF_DROP;
} }
#ifdef WITH_IPV6 #ifdef WITH_IPV6
static unsigned int static unsigned int
tarpit_tg6(struct sk_buff *skb, const struct xt_action_param *par) tarpit_tg6(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
const struct sk_buff *skb = *pskb;
const struct ipv6hdr *iph = ipv6_hdr(skb); const struct ipv6hdr *iph = ipv6_hdr(skb);
const struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); const struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
const struct xt_tarpit_tginfo *info = par->targinfo; const struct xt_tarpit_tginfo *info = par->targinfo;
@@ -486,7 +477,8 @@ tarpit_tg6(struct sk_buff *skb, const struct xt_action_param *par)
pr_debug("addr is not unicast.\n"); pr_debug("addr is not unicast.\n");
return NF_DROP; return NF_DROP;
} }
tarpit_tcp6(par, skb, info->variant);
tarpit_tcp6(*pskb, par->hooknum, info->variant);
return NF_DROP; return NF_DROP;
} }
#endif #endif

View File

@@ -7,7 +7,6 @@
* Authors: * Authors:
* Stephane Ouellette <ouellettes [at] videotron ca>, 2002-10-22 * Stephane Ouellette <ouellettes [at] videotron ca>, 2002-10-22
* Massimiliano Hofer <max [at] nucleus it>, 2006-05-15 * Massimiliano Hofer <max [at] nucleus it>, 2006-05-15
* Grzegorz Kuczyński <grzegorz.kuczynski [at] koba pl>, 2017-02-27
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License; either version 2 * under the terms of the GNU General Public License; either version 2
@@ -22,8 +21,6 @@
#include <linux/version.h> #include <linux/version.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include "xt_condition.h" #include "xt_condition.h"
#include "compat_xtables.h" #include "compat_xtables.h"
@@ -55,44 +52,31 @@ struct condition_variable {
struct proc_dir_entry *status_proc; struct proc_dir_entry *status_proc;
unsigned int refcount; unsigned int refcount;
bool enabled; bool enabled;
char name[sizeof(((struct xt_condition_mtinfo *)NULL)->name)];
}; };
/* proc_lock is a user context only semaphore used for write access */ /* proc_lock is a user context only semaphore used for write access */
/* to the conditions' list. */ /* to the conditions' list. */
static DEFINE_MUTEX(proc_lock); static DEFINE_MUTEX(proc_lock);
struct condition_net { static LIST_HEAD(conditions_list);
struct list_head conditions_list; static struct proc_dir_entry *proc_net_condition;
struct proc_dir_entry *proc_net_condition;
bool after_clear;
};
static int condition_net_id; static int condition_proc_read(char __user *buffer, char **start, off_t offset,
int length, int *eof, void *data)
static inline struct condition_net *condition_pernet(struct net *net)
{ {
return net_generic(net, condition_net_id); const struct condition_variable *var = data;
buffer[0] = var->enabled ? '1' : '0';
buffer[1] = '\n';
if (length >= 2)
*eof = true;
return 2;
} }
static int condition_proc_show(struct seq_file *m, void *data) static int condition_proc_write(struct file *file, const char __user *buffer,
unsigned long length, void *data)
{ {
const struct condition_variable *var = m->private; struct condition_variable *var = data;
seq_printf(m, var->enabled ? "1\n" : "0\n");
return 0;
}
static int condition_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, condition_proc_show, PDE_DATA(inode));
}
static ssize_t
condition_proc_write(struct file *file, const char __user *buffer,
size_t length, loff_t *loff)
{
struct condition_variable *var = PDE_DATA(file_inode(file));
char newval; char newval;
if (length > 0) { if (length > 0) {
@@ -111,14 +95,6 @@ condition_proc_write(struct file *file, const char __user *buffer,
return length; return length;
} }
static const struct proc_ops condition_proc_fops = {
.proc_open = condition_proc_open,
.proc_read = seq_read,
.proc_write = condition_proc_write,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static bool static bool
condition_mt(const struct sk_buff *skb, struct xt_action_param *par) condition_mt(const struct sk_buff *skb, struct xt_action_param *par)
{ {
@@ -132,7 +108,6 @@ static int condition_mt_check(const struct xt_mtchk_param *par)
{ {
struct xt_condition_mtinfo *info = par->matchinfo; struct xt_condition_mtinfo *info = par->matchinfo;
struct condition_variable *var; struct condition_variable *var;
struct condition_net *condition_net = condition_pernet(par->net);
/* Forbid certain names */ /* Forbid certain names */
if (*info->name == '\0' || *info->name == '.' || if (*info->name == '\0' || *info->name == '.' ||
@@ -148,8 +123,8 @@ static int condition_mt_check(const struct xt_mtchk_param *par)
* or increase the reference counter. * or increase the reference counter.
*/ */
mutex_lock(&proc_lock); mutex_lock(&proc_lock);
list_for_each_entry(var, &condition_net->conditions_list, list) { list_for_each_entry(var, &conditions_list, list) {
if (strcmp(info->name, var->name) == 0) { if (strcmp(info->name, var->status_proc->name) == 0) {
var->refcount++; var->refcount++;
mutex_unlock(&proc_lock); mutex_unlock(&proc_lock);
info->condvar = var; info->condvar = var;
@@ -164,23 +139,24 @@ static int condition_mt_check(const struct xt_mtchk_param *par)
return -ENOMEM; return -ENOMEM;
} }
memcpy(var->name, info->name, sizeof(info->name));
/* Create the condition variable's proc file entry. */ /* Create the condition variable's proc file entry. */
var->status_proc = proc_create_data(info->name, condition_list_perms, var->status_proc = create_proc_entry(info->name, condition_list_perms,
condition_net->proc_net_condition, &condition_proc_fops, var); proc_net_condition);
if (var->status_proc == NULL) { if (var->status_proc == NULL) {
kfree(var); kfree(var);
mutex_unlock(&proc_lock); mutex_unlock(&proc_lock);
return -ENOMEM; return -ENOMEM;
} }
proc_set_user(var->status_proc,
make_kuid(&init_user_ns, condition_uid_perms),
make_kgid(&init_user_ns, condition_gid_perms));
var->refcount = 1; var->refcount = 1;
var->enabled = false; var->enabled = false;
var->status_proc->data = var;
wmb(); wmb();
list_add(&var->list, &condition_net->conditions_list); var->status_proc->read_proc = condition_proc_read;
var->status_proc->write_proc = condition_proc_write;
list_add(&var->list, &conditions_list);
var->status_proc->uid = condition_uid_perms;
var->status_proc->gid = condition_gid_perms;
mutex_unlock(&proc_lock); mutex_unlock(&proc_lock);
info->condvar = var; info->condvar = var;
return 0; return 0;
@@ -190,15 +166,11 @@ static void condition_mt_destroy(const struct xt_mtdtor_param *par)
{ {
const struct xt_condition_mtinfo *info = par->matchinfo; const struct xt_condition_mtinfo *info = par->matchinfo;
struct condition_variable *var = info->condvar; struct condition_variable *var = info->condvar;
struct condition_net *cnet = condition_pernet(par->net);
if (cnet->after_clear)
return;
mutex_lock(&proc_lock); mutex_lock(&proc_lock);
if (--var->refcount == 0) { if (--var->refcount == 0) {
list_del(&var->list); list_del(&var->list);
remove_proc_entry(var->name, cnet->proc_net_condition); remove_proc_entry(var->status_proc->name, proc_net_condition);
mutex_unlock(&proc_lock); mutex_unlock(&proc_lock);
kfree(var); kfree(var);
return; return;
@@ -231,54 +203,18 @@ static struct xt_match condition_mt_reg[] __read_mostly = {
static const char *const dir_name = "nf_condition"; static const char *const dir_name = "nf_condition";
static int __net_init condition_net_init(struct net *net)
{
struct condition_net *condition_net = condition_pernet(net);
INIT_LIST_HEAD(&condition_net->conditions_list);
condition_net->proc_net_condition = proc_mkdir(dir_name, net->proc_net);
if (condition_net->proc_net_condition == NULL)
return -EACCES;
condition_net->after_clear = 0;
return 0;
}
static void __net_exit condition_net_exit(struct net *net)
{
struct condition_net *condition_net = condition_pernet(net);
struct list_head *pos, *q;
struct condition_variable *var = NULL;
remove_proc_subtree(dir_name, net->proc_net);
mutex_lock(&proc_lock);
list_for_each_safe(pos, q, &condition_net->conditions_list) {
var = list_entry(pos, struct condition_variable, list);
list_del(pos);
kfree(var);
}
mutex_unlock(&proc_lock);
condition_net->after_clear = true;
}
static struct pernet_operations condition_net_ops = {
.init = condition_net_init,
.exit = condition_net_exit,
.id = &condition_net_id,
.size = sizeof(struct condition_net),
};
static int __init condition_mt_init(void) static int __init condition_mt_init(void)
{ {
int ret; int ret;
mutex_init(&proc_lock); mutex_init(&proc_lock);
ret = register_pernet_subsys(&condition_net_ops); proc_net_condition = proc_mkdir(dir_name, init_net.proc_net);
if (ret != 0) if (proc_net_condition == NULL)
return ret; return -EACCES;
ret = xt_register_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg)); ret = xt_register_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg));
if (ret < 0) { if (ret < 0) {
unregister_pernet_subsys(&condition_net_ops); remove_proc_entry(dir_name, init_net.proc_net);
return ret; return ret;
} }
@@ -288,7 +224,7 @@ static int __init condition_mt_init(void)
static void __exit condition_mt_exit(void) static void __exit condition_mt_exit(void)
{ {
xt_unregister_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg)); xt_unregister_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg));
unregister_pernet_subsys(&condition_net_ops); remove_proc_entry(dir_name, init_net.proc_net);
} }
module_init(condition_mt_init); module_init(condition_mt_init);

View File

@@ -75,8 +75,7 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr,
if (copy_from_user(&umem, umem_ptr, sizeof(umem)) != 0) if (copy_from_user(&umem, umem_ptr, sizeof(umem)) != 0)
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
if (umem.count > SIZE_MAX / geoproto_size[proto])
return ERR_PTR(-E2BIG);
p = kmalloc(sizeof(struct geoip_country_kernel), GFP_KERNEL); p = kmalloc(sizeof(struct geoip_country_kernel), GFP_KERNEL);
if (p == NULL) if (p == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@@ -84,19 +83,11 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr,
p->count = umem.count; p->count = umem.count;
p->cc = umem.cc; p->cc = umem.cc;
size = p->count * geoproto_size[proto]; size = p->count * geoproto_size[proto];
if (size == 0) {
/*
* Believe it or not, vmalloc prints a warning to dmesg for
* zero-sized allocations :-/
*/
subnet = NULL;
} else {
subnet = vmalloc(size); subnet = vmalloc(size);
if (subnet == NULL) { if (subnet == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto free_p; goto free_p;
} }
}
if (copy_from_user(subnet, if (copy_from_user(subnet,
(const void __user *)(unsigned long)umem.subnets, size) != 0) { (const void __user *)(unsigned long)umem.subnets, size) != 0) {
ret = -EFAULT; ret = -EFAULT;

View File

@@ -45,9 +45,9 @@ static const struct net_device *iface_get(const struct xt_iface_mtinfo *info,
const struct xt_action_param *par, struct net_device **put) const struct xt_action_param *par, struct net_device **put)
{ {
if (info->flags & XT_IFACE_DEV_IN) if (info->flags & XT_IFACE_DEV_IN)
return par->state->in; return par->in;
else if (info->flags & XT_IFACE_DEV_OUT) else if (info->flags & XT_IFACE_DEV_OUT)
return par->state->out; return par->out;
return *put = dev_get_by_name(&init_net, info->ifname); return *put = dev_get_by_name(&init_net, info->ifname);
} }

141
extensions/xt_ipaddr.c Normal file
View File

@@ -0,0 +1,141 @@
/*
* "ipaddr" demo match for Xtables
* Copyright © Jan Engelhardt, 2008-2009
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the WTF Public License version 2 or
* (at your option) any later version.
*/
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter/x_tables.h>
#include <net/ipv6.h>
#include "compat_xtables.h"
#include "xt_ipaddr.h"
static bool ipaddr_mt4(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_ipaddr_mtinfo *info = par->matchinfo;
const struct iphdr *iph = ip_hdr(skb);
printk(KERN_INFO
"xt_ipaddr: IN=%s OUT=%s "
"SRC=" NIPQUAD_FMT " DST=" NIPQUAD_FMT " "
"IPSRC=" NIPQUAD_FMT " IPDST=" NIPQUAD_FMT "\n",
(par->in != NULL) ? par->in->name : "",
(par->out != NULL) ? par->out->name : "",
NIPQUAD(iph->saddr), NIPQUAD(iph->daddr),
NIPQUAD(info->src), NIPQUAD(info->dst));
if (info->flags & XT_IPADDR_SRC)
if ((iph->saddr != info->src.ip) ^
!!(info->flags & XT_IPADDR_SRC_INV)) {
printk(KERN_NOTICE "src IP - no match\n");
return false;
}
if (info->flags & XT_IPADDR_DST)
if ((iph->daddr != info->dst.ip) ^
!!(info->flags & XT_IPADDR_DST_INV)) {
printk(KERN_NOTICE "dst IP - no match\n");
return false;
}
return true;
}
static bool ipaddr_mt6(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_ipaddr_mtinfo *info = par->matchinfo;
const struct ipv6hdr *iph = ipv6_hdr(skb);
printk(KERN_INFO
"xt_ipaddr: IN=%s OUT=%s "
"SRC=" NIP6_FMT " DST=" NIP6_FMT " "
"IPSRC=" NIP6_FMT " IPDST=" NIP6_FMT "\n",
(par->in != NULL) ? par->in->name : "",
(par->out != NULL) ? par->out->name : "",
NIP6(iph->saddr), NIP6(iph->daddr),
NIP6(info->src.in6), NIP6(info->dst.in6));
if (info->flags & XT_IPADDR_SRC)
if ((ipv6_addr_cmp(&iph->saddr, &info->src.in6) != 0) ^
!!(info->flags & XT_IPADDR_SRC_INV)) {
printk(KERN_NOTICE "src IP - no match\n");
return false;
}
if (info->flags & XT_IPADDR_DST)
if ((ipv6_addr_cmp(&iph->daddr, &info->dst.in6) != 0) ^
!!(info->flags & XT_IPADDR_DST_INV)) {
printk(KERN_NOTICE "dst IP - no match\n");
return false;
}
return true;
}
static int ipaddr_mt_check(const struct xt_mtchk_param *par)
{
const struct xt_ipaddr_mtinfo *info = par->matchinfo;
printk(KERN_INFO "xt_ipaddr: Added a rule with -m ipaddr in "
"the %s table; this rule is reachable through hooks 0x%x\n",
par->table, par->hook_mask);
if (par->match->family == NFPROTO_IPV4 &&
ntohl(info->src.ip) == 0xDEADBEEF) {
printk(KERN_INFO "xt_ipaddr: I just thought I do not want "
"to let you match on 222.173.190.239\n");
return -EPERM;
}
return 0;
}
static void ipaddr_mt_destroy(const struct xt_mtdtor_param *par)
{
printk(KERN_INFO "One rule with ipaddr match got deleted\n");
}
static struct xt_match ipaddr_mt_reg[] __read_mostly = {
{
.name = "ipaddr",
.revision = 0,
.family = NFPROTO_IPV4,
.match = ipaddr_mt4,
.checkentry = ipaddr_mt_check,
.destroy = ipaddr_mt_destroy,
.matchsize = XT_ALIGN(sizeof(struct xt_ipaddr_mtinfo)),
.me = THIS_MODULE,
},
{
.name = "ipaddr",
.revision = 0,
.family = NFPROTO_IPV6,
.match = ipaddr_mt6,
.checkentry = ipaddr_mt_check,
.destroy = ipaddr_mt_destroy,
.matchsize = XT_ALIGN(sizeof(struct xt_ipaddr_mtinfo)),
.me = THIS_MODULE,
},
};
static int __init ipaddr_mt_init(void)
{
return xt_register_matches(ipaddr_mt_reg, ARRAY_SIZE(ipaddr_mt_reg));
}
static void __exit ipaddr_mt_exit(void)
{
xt_unregister_matches(ipaddr_mt_reg, ARRAY_SIZE(ipaddr_mt_reg));
}
module_init(ipaddr_mt_init);
module_exit(ipaddr_mt_exit);
MODULE_DESCRIPTION("Xtables: Match source/destination address");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_ipaddr");
MODULE_ALIAS("ip6t_ipaddr");

16
extensions/xt_ipaddr.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef _LINUX_NETFILTER_XT_IPADDR_H
#define _LINUX_NETFILTER_XT_IPADDR_H 1
enum {
XT_IPADDR_SRC = 1 << 0,
XT_IPADDR_DST = 1 << 1,
XT_IPADDR_SRC_INV = 1 << 2,
XT_IPADDR_DST_INV = 1 << 3,
};
struct xt_ipaddr_mtinfo {
union nf_inet_addr src, dst;
__u8 flags;
};
#endif /* _LINUX_NETFILTER_XT_IPADDR_H */

View File

@@ -511,7 +511,7 @@ search_bittorrent(const unsigned char *payload, const unsigned int plen)
* but *must have* one (or more) of strings listed below (true for scrape and announce) * but *must have* one (or more) of strings listed below (true for scrape and announce)
*/ */
if (memcmp(payload, "GET /", 5) == 0) { if (memcmp(payload, "GET /", 5) == 0) {
if (HX_memmem(payload, plen, "info_hash=", 10) != NULL) if (HX_memmem(payload, plen, "info_hash=", 9) != NULL)
return IPP2P_BIT * 100 + 1; return IPP2P_BIT * 100 + 1;
if (HX_memmem(payload, plen, "peer_id=", 8) != NULL) if (HX_memmem(payload, plen, "peer_id=", 8) != NULL)
return IPP2P_BIT * 100 + 2; return IPP2P_BIT * 100 + 2;
@@ -857,11 +857,8 @@ ipp2p_mt(const struct sk_buff *skb, struct xt_action_param *par)
p2p_result = matchlist[i].function_name(haystack, hlen); p2p_result = matchlist[i].function_name(haystack, hlen);
if (p2p_result) { if (p2p_result) {
if (info->debug) if (info->debug)
printk("IPP2P.debug:TCP-match: %d from: %pI4:%hu to: %pI4:%hu Length: %d\n", printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n",
p2p_result, &ip->saddr, p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen);
ntohs(tcph->source),
&ip->daddr,
ntohs(tcph->dest), hlen);
return p2p_result; return p2p_result;
} }
} }
@@ -891,11 +888,8 @@ ipp2p_mt(const struct sk_buff *skb, struct xt_action_param *par)
p2p_result = udp_list[i].function_name(haystack, hlen); p2p_result = udp_list[i].function_name(haystack, hlen);
if (p2p_result) { if (p2p_result) {
if (info->debug) if (info->debug)
printk("IPP2P.debug:UDP-match: %d from: %pI4:%hu to: %pI4:%hu Length: %d\n", printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n",
p2p_result, &ip->saddr, p2p_result, NIPQUAD(ip->saddr), ntohs(udph->source), NIPQUAD(ip->daddr), ntohs(udph->dest), hlen);
ntohs(udph->source),
&ip->daddr,
ntohs(udph->dest), hlen);
return p2p_result; return p2p_result;
} }
} }

View File

@@ -175,7 +175,6 @@ lscan_mt(const struct sk_buff *skb, struct xt_action_param *par)
{ {
const struct xt_lscan_mtinfo *info = par->matchinfo; const struct xt_lscan_mtinfo *info = par->matchinfo;
enum ip_conntrack_info ctstate; enum ip_conntrack_info ctstate;
const struct iphdr *iph = ip_hdr(skb);
const struct tcphdr *tcph; const struct tcphdr *tcph;
struct nf_conn *ctdata; struct nf_conn *ctdata;
struct tcphdr tcph_buf; struct tcphdr tcph_buf;
@@ -183,14 +182,10 @@ lscan_mt(const struct sk_buff *skb, struct xt_action_param *par)
tcph = skb_header_pointer(skb, par->thoff, sizeof(tcph_buf), &tcph_buf); tcph = skb_header_pointer(skb, par->thoff, sizeof(tcph_buf), &tcph_buf);
if (tcph == NULL) if (tcph == NULL)
return false; return false;
if (info->match_fl1 & LSCAN_FL1_MIRAI && iph != NULL &&
iph->version == 4 && iph->daddr == tcph->seq)
return true;
/* Check for invalid packets: -m conntrack --ctstate INVALID */ /* Check for invalid packets: -m conntrack --ctstate INVALID */
ctdata = nf_ct_get(skb, &ctstate); if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) {
if (ctdata == NULL) { if (info->match_stealth)
if (info->match_fl1 & LSCAN_FL1_STEALTH)
return lscan_mt_stealth(tcph); return lscan_mt_stealth(tcph);
/* /*
* If @ctdata is NULL, we cannot match the other scan * If @ctdata is NULL, we cannot match the other scan
@@ -209,26 +204,24 @@ lscan_mt(const struct sk_buff *skb, struct xt_action_param *par)
unsigned int n; unsigned int n;
n = lscan_mt_full(ctdata->mark & connmark_mask, ctstate, n = lscan_mt_full(ctdata->mark & connmark_mask, ctstate,
par->state->in == init_net.loopback_dev, tcph, par->in == init_net.loopback_dev, tcph,
skb->len - par->thoff - 4 * tcph->doff); skb->len - par->thoff - 4 * tcph->doff);
ctdata->mark = (ctdata->mark & ~connmark_mask) | n; ctdata->mark = (ctdata->mark & ~connmark_mask) | n;
skb_nfmark(skb) = (skb_nfmark(skb) & ~packet_mask) ^ mark_seen; skb_nfmark(skb) = (skb_nfmark(skb) & ~packet_mask) ^ mark_seen;
} }
return (info->match_fl1 & LSCAN_FL1_STEALTH && ctdata->mark == mark_synscan) || return (info->match_syn && ctdata->mark == mark_synscan) ||
(info->match_fl3 & LSCAN_FL3_CN && ctdata->mark == mark_cnscan) || (info->match_cn && ctdata->mark == mark_cnscan) ||
(info->match_fl4 & LSCAN_FL4_GR && ctdata->mark == mark_grscan); (info->match_gr && ctdata->mark == mark_grscan);
} }
static int lscan_mt_check(const struct xt_mtchk_param *par) static int lscan_mt_check(const struct xt_mtchk_param *par)
{ {
const struct xt_lscan_mtinfo *info = par->matchinfo; const struct xt_lscan_mtinfo *info = par->matchinfo;
if ((info->match_fl1 & ~(LSCAN_FL1_STEALTH | LSCAN_FL1_MIRAI)) || if ((info->match_stealth & ~1) || (info->match_syn & ~1) ||
(info->match_fl2 & ~LSCAN_FL2_SYN) || (info->match_cn & ~1) || (info->match_gr & ~1)) {
(info->match_fl3 & ~LSCAN_FL3_CN) ||
(info->match_fl4 & ~LSCAN_FL4_GR)) {
printk(KERN_WARNING PFX "Invalid flags\n"); printk(KERN_WARNING PFX "Invalid flags\n");
return -EINVAL; return -EINVAL;
} }

View File

@@ -1,16 +1,8 @@
#ifndef _LINUX_NETFILTER_XT_LSCAN_H #ifndef _LINUX_NETFILTER_XT_LSCAN_H
#define _LINUX_NETFILTER_XT_LSCAN_H 1 #define _LINUX_NETFILTER_XT_LSCAN_H 1
enum {
LSCAN_FL1_STEALTH = 1 << 0,
LSCAN_FL1_MIRAI = 1 << 1,
LSCAN_FL2_SYN = 1 << 0,
LSCAN_FL3_CN = 1 << 0,
LSCAN_FL4_GR = 1 << 0,
};
struct xt_lscan_mtinfo { struct xt_lscan_mtinfo {
uint8_t match_fl1, match_fl2, match_fl3, match_fl4; uint8_t match_stealth, match_syn, match_cn, match_gr;
}; };
#endif /* _LINUX_NETFILTER_XT_LSCAN_H */ #endif /* _LINUX_NETFILTER_XT_LSCAN_H */

View File

@@ -45,12 +45,12 @@ MODULE_ALIAS("ip6t_psd");
/* /*
* Keep track of up to LIST_SIZE source addresses, using a hash table of * Keep track of up to LIST_SIZE source addresses, using a hash table of
* PSD_HASH_SIZE entries for faster lookups, but limiting hash collisions to * HASH_SIZE entries for faster lookups, but limiting hash collisions to
* HASH_MAX source addresses per the same hash value. * HASH_MAX source addresses per the same hash value.
*/ */
#define LIST_SIZE 0x100 #define LIST_SIZE 0x100
#define HASH_LOG 9 #define HASH_LOG 9
#define PSD_HASH_SIZE (1 << HASH_LOG) #define HASH_SIZE (1 << HASH_LOG)
#define HASH_MAX 0x10 #define HASH_MAX 0x10
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
@@ -108,7 +108,7 @@ struct host6 {
static struct { static struct {
spinlock_t lock; spinlock_t lock;
struct host4 list[LIST_SIZE]; struct host4 list[LIST_SIZE];
struct host *hash[PSD_HASH_SIZE]; struct host *hash[HASH_SIZE];
int index; int index;
} state; } state;
@@ -143,12 +143,13 @@ static bool state6_alloc_mem(void)
if (state6.list == NULL) if (state6.list == NULL)
return false; return false;
memset(state6.list, 0, LIST_SIZE * sizeof(struct host6)); memset(state6.list, 0, LIST_SIZE * sizeof(struct host6));
state6.hash = vmalloc(PSD_HASH_SIZE * sizeof(struct host *));
state6.hash = vmalloc(HASH_SIZE * sizeof(struct host*));
if (state6.hash == NULL) { if (state6.hash == NULL) {
vfree(state6.list); vfree(state6.list);
return false; return false;
} }
memset(state6.hash, 0, PSD_HASH_SIZE * sizeof(struct host *)); memset(state6.hash, 0, HASH_SIZE * sizeof(struct host *));
return true; return true;
} }
#endif #endif
@@ -166,7 +167,8 @@ static unsigned int hashfunc(__be32 addr)
do { do {
hash ^= value; hash ^= value;
} while ((value >>= HASH_LOG) != 0); } while ((value >>= HASH_LOG) != 0);
return hash & (PSD_HASH_SIZE - 1);
return hash & (HASH_SIZE - 1);
} }
static inline unsigned int hashfunc6(const struct in6_addr *addr) static inline unsigned int hashfunc6(const struct in6_addr *addr)

Some files were not shown because too many files have changed in this diff Show More