Compare commits

...

75 Commits
v1.32 ... v1.36

Author SHA1 Message Date
Jan Engelhardt
4203259e5a Xtables-addons 1.36 2011-06-03 16:45:29 +02:00
Jan Engelhardt
e3956498ac doc: remove stray "userspace" wording 2011-06-01 01:44:54 +02:00
Jan Engelhardt
6f730f3ab2 xt_TARPIT: unlock for use with all tables 2011-06-01 01:37:05 +02:00
Jan Engelhardt
2b590a35fd Merge branch 'ipset-6' 2011-05-31 23:05:40 +02:00
Jan Engelhardt
3dd33dfe93 doc: move iptaccount(8) option overview to its own manpage 2011-05-31 23:05:31 +02:00
Jan Engelhardt
d417077816 doc: fix \(em in ipv4options 2011-05-31 23:00:35 +02:00
Jan Engelhardt
d057f6d6f0 doc: replace NOTRACK by CT-notrack 2011-05-31 22:58:34 +02:00
Jan Engelhardt
b2fc85c589 ipset: update to 6.7-genl 2011-05-31 22:54:49 +02:00
Martin Barrow Cliff
fa1348455d xt_TARPIT: honeypot and reset modes
Honeypot mode attempts to maintain a normal connection for the purpose
of capturing payload packets.

Reset mode provides the ability to send a reset packet in lieu of
using the DROP or REJECT targets.
2011-05-31 22:41:51 +02:00
Jan Engelhardt
1a5c079e6b Merge branch 'ipset-6' 2011-05-31 16:56:26 +02:00
Jan Engelhardt
75b3762ef4 build: support for Linux up to 3.0 2011-05-31 16:56:23 +02:00
Jan Engelhardt
cfb72bf468 ipset: update to 6.6a-genl 2011-05-31 16:14:44 +02:00
Jan Engelhardt
1b0790d151 ipset-6: move manpage into src/ 2011-05-31 16:09:03 +02:00
Changli Gao
a5355e74ea xt_geoip: avoid recursive function calls
The stack memory is very limited in Linux kernel.

Signed-off-by: Jan Engelhardt <jengelh@medozas.de>
2011-04-14 09:07:25 +02:00
Jan Engelhardt
757bf0e993 Xtables-addons 1.35
Trim the changelog to only reveal changes relevant to the end user.
2011-04-11 18:37:32 +02:00
Jan Engelhardt
cea4817a46 Merge branch 'ipset-6'
Conflicts:
	INSTALL
	mconfig
2011-04-11 04:23:18 +02:00
Jan Engelhardt
2dc79fe008 ipset; update to ipset-6.3 (genl)
* Handle EAGAIN from autoloading code.
* Turn one nfgenmsg site into genlmsg to avoid protocol mismatch
2011-04-11 04:11:30 +02:00
Jan Engelhardt
b60f8f1de2 Xtables-addons 1.34 2011-04-07 15:15:39 +02:00
Jan Engelhardt
499c6db75e ipset: update to ipset-6.2 2011-04-04 00:39:50 +02:00
Jan Engelhardt
509953daec build: notify of unsupported Linux kernel versions 2011-04-04 00:18:54 +02:00
Jan Engelhardt
c53a86874d build: notify of unsupported Linux kernel versions
I would like to move forward a bit, and today, two issues prompted
me to start removing old code:

* make 3.82 does not like mixing normal rules with implicit rules,
  which rejects Makefiles of Linux kernels before 2.6.34.

* xt_DNETMAP uses functionality not available before 2.6.29.
2011-04-04 00:10:23 +02:00
Jan Engelhardt
309b960012 Merge branch 'dnetmap' 2011-04-03 23:59:18 +02:00
Marek Kierdelewicz
f4882ca029 xt_DNETMAP: support for kernels below 2.6.34 2011-04-03 23:50:10 +02:00
Jan Engelhardt
8fd3eb56eb xt_DNETMAP: add missing alias 2011-04-03 23:49:53 +02:00
Jan Engelhardt
94574fb829 Merge branch 'pknock'
Kernel warnings and oopses resulting from the inversion (HEAD^2)
reported by user mancha (mancha!debian-tor@gateway/tor-sasl/mancha) on
irc.freenode.net/#netfilter.
2011-02-28 20:51:25 +01:00
Jan Engelhardt
32871bad39 xt_pknock: avoid inversion of rule lookup that led to warnings
Commit v1.18-48-g58839b9 had this screwed up.
2011-02-25 01:22:16 +01:00
Jan Engelhardt
0ba44bd461 xt_pknock: avoid crash when hash TFM could not be allocated 2011-02-25 01:22:16 +01:00
Jan Engelhardt
d4e6e3d155 xt_pknock: indent 2011-02-25 01:20:12 +01:00
Jan Engelhardt
18043f3e3a ipset: update to 6.0 2011-02-24 00:40:23 +01:00
Jan Engelhardt
ff27f61477 ipset: rename ipset-5 directory to ipset-6 2011-02-24 00:02:18 +01:00
Jan Engelhardt
9f59f21614 Xtables-addons 1.33 2011-02-02 05:21:56 +01:00
Jan Engelhardt
7a8bfed52c xt_geoip: keep compiling for <= 2.6.23 2011-02-02 05:21:56 +01:00
Jan Engelhardt
1edc9b943b build: do not forget including path for compat_user.h 2011-02-02 05:15:09 +01:00
Jan Engelhardt
ebfa77795a build: preliminary support for iptables 1.4.11 2011-02-02 05:09:58 +01:00
Jan Engelhardt
4a8aa505c4 build: support for Linux up to 2.6.38
No API changes that needed to be taken care of, so just bump the
version check in configure.ac.
2011-02-02 04:59:58 +01:00
Jan Engelhardt
4654ee127f xt_DNETMAP: disable by default for now
I wanted xt_DNETMAP to be included in the release already, though
because it only compiled with Linux >= 2.6.34 currently (some work
will be needed), deactive it by default.
2011-02-02 04:55:34 +01:00
Jan Engelhardt
a7a77d7146 Merge branch 'dnetmap' 2011-02-02 04:54:25 +01:00
Jan Engelhardt
2a61ca7d4b build: fix missing inclusion of dependency rules 2011-02-02 04:53:30 +01:00
Jan Engelhardt
39de351a91 libxt_geoip: update manual page with new tool syntax 2011-02-02 04:51:38 +01:00
Jan Engelhardt
07bf41a294 Merge branch 'geoip' 2011-02-02 04:48:40 +01:00
Jan Engelhardt
07cd29d9ce xt_geoip: IPv6 support 2011-02-02 04:47:28 +01:00
Jan Engelhardt
eb9634aee6 xt_geoip: v4/v6 name preparations 2011-02-02 04:43:36 +01:00
Jan Engelhardt
19f241a09c xt_geoip: cleanups, preparations for IPv6 geoip 2011-02-02 01:55:09 +01:00
Jan Engelhardt
0a29c1d32f xt_DNETMAP: style considerations
Clean up the files a bit. For one, break dangerously right-indented
function headers, and correct some spaces that should be tab.
2011-01-27 03:23:22 +01:00
Jan Engelhardt
93a17fdde0 geoipdb builder: get rid of some global variables 2011-01-27 03:05:30 +01:00
Jan Engelhardt
80444b0d31 geoipdb builder: separate into functions 2011-01-27 03:02:39 +01:00
Jan Engelhardt
f180c0e5c6 geoipdb tools: IPv6 support 2011-01-27 02:57:00 +01:00
Jan Engelhardt
ca8ebe4467 Merge remote branch 'origin/master' 2011-01-22 18:10:18 +01:00
Jan Engelhardt
e82410735a xt_DNETMAP: use compat_xtables layer to run on older kernels 2011-01-22 18:09:15 +01:00
Jan Engelhardt
89e72bb0f4 doc: Add version information to README 2011-01-22 17:34:48 +01:00
Jan Engelhardt
77b9f2024c Merge branch 'account' 2011-01-22 17:34:43 +01:00
Jan Engelhardt
0edb572f6e ACCOUNT: update to 1.16
There are no changes to integrate from ipt_ACCOUNT because xt_ACCOUT
already, by way of the compat_xtables layer, supports multiple kernel
versions.
2011-01-22 17:34:06 +01:00
Jan Engelhardt
8b1ff64b8b Merge branch 'pknock' 2011-01-22 17:33:45 +01:00
Jan Engelhardt
ebb61aa3c9 pknock: resolve warnings about unused variables 2011-01-22 17:32:38 +01:00
Jan Engelhardt
bd2e6108f3 pknock: use build flags in pknock Makefile 2011-01-22 17:31:53 +01:00
Jan Engelhardt
288492c820 xt_DNETMAP: order mconfig 2011-01-22 17:24:51 +01:00
Jan Engelhardt
e425c8f277 xt_DNETMAP: use more appropriate format specifiers
Substitute %i -> %d. Use %u for prefix length.
2011-01-22 17:23:13 +01:00
Jan Engelhardt
e3e88827fb xt_DNETMAP: use permission mnemonic constants 2011-01-22 15:59:15 +01:00
Jan Engelhardt
6c709fd682 Merge branch 'ipset-5' 2011-01-22 15:56:57 +01:00
Jan Engelhardt
1b53724a61 ipset: update to 5.4.1 2011-01-22 15:56:44 +01:00
Jan Engelhardt
983b28fe8e Merge branch 'ipset-5' 2011-01-21 23:26:11 +01:00
Jan Engelhardt
3141b2ff86 ipset: update to 5.3 2011-01-19 02:55:23 +01:00
Jan Engelhardt
980a53348f build: use AM_CPPFLAGS in ipset-5/ 2011-01-19 02:36:26 +01:00
Jan Engelhardt
8ea781e257 build: fix objdir builds for ipset-5 (xt-a specific) 2011-01-19 02:35:49 +01:00
Jan Engelhardt
7e39ee66e0 libxt_length: fix name of manpage file 2011-01-15 05:23:38 +01:00
Jan Engelhardt
65e97a66e6 build: restore functionality of configure's --without-kbuild option 2011-01-13 20:25:00 +01:00
Jan Engelhardt
d82b20ead7 xt_LOGMARK: fix detection of untracked connection for Linux >= 2.6.36 2011-01-12 01:53:38 +01:00
Marek Kierdelewicz
c5d4dd0bcf Import of xt_DNETMAP 2011-01-10 22:59:32 +01:00
Jan Engelhardt
0168f8e8a2 doc: deprecate --with-xtlibdir configure option 2011-01-04 12:47:09 +01:00
Jan Engelhardt
dc22ec7bd1 build: mark Linux 2.6.37 as supported 2011-01-04 12:44:55 +01:00
Jan Engelhardt
25bf680ead geoip: put IPv4 geoip data into its own map 2010-12-19 00:48:42 +01:00
Jan Engelhardt
4d547c2bfc geoip: remove -b option, always build both endianesses 2010-12-17 22:47:01 +01:00
Jan Engelhardt
f7c7264a65 geoip: remove %names map 2010-12-17 22:41:08 +01:00
Jan Engelhardt
0f42828fd6 geoip: add manpages to utility programs 2010-12-17 22:29:54 +01:00
Jan Engelhardt
e2da87230a geoip: prefix tools with xt_
This is preferable to have when manpages go into system locations.
2010-12-17 22:08:15 +01:00
141 changed files with 8433 additions and 5668 deletions

23
INSTALL
View File

@@ -4,7 +4,7 @@ Installation instructions for Xtables-addons
Xtables-addons uses the well-known configure(autotools) infrastructure
in combination with the kernel's Kbuild system.
$ ./configure --with-xtlibdir=SEE_BELOW
$ ./configure
$ make
# make install
@@ -14,7 +14,7 @@ Supported configurations for this release
* iptables >= 1.4.3
* kernel-source >= 2.6.17, no upper bound known
* kernel-source >= 2.6.29
with prepared build/output directory
- CONFIG_NF_CONNTRACK or CONFIG_IP_NF_CONNTRACK
- CONFIG_NF_CONNTRACK_MARK or CONFIG_IP_NF_CONNTRACK_MARK
@@ -22,13 +22,7 @@ Supported configurations for this release
- CONFIG_CONNECTOR y/m if you wish to receive userspace
notifications from pknock through netlink/connector
Extra notes:
* in the kernel 2.6.18.x series, >= 2.6.18.5 is required
* requires that no vendor backports interfere
For ipset-5 you need:
For ipset-6 you need:
* libmnl
@@ -60,11 +54,12 @@ Configuring and compiling
--with-xtlibdir=
Specifies the path to where the newly built extensions should
be installed when `make install` is run. It uses the same
default as the Xtables/iptables package, ${libexecdir}/xtables,
but you may need to specify this nevertheless, as autotools
defaults to using /usr/local as prefix, and distributions put
the files in differing locations.
be installed when `make install` is run. The default is to
use the same path that Xtables/iptables modules use, as
determined by `pkg-config xtables --variable xtlibdir`.
Thus, this option normally does NOT need to be specified
anymore, even if your distribution put modules in a strange
location.
If you want to enable debugging, use

7
README
View File

@@ -16,6 +16,13 @@ sanity checks and incorrect endianess handling have been fixed,
simplified, and sped up.
Included in this package
========================
- ipset 4.5
- ipset 6.7-genl
- xt_ACCOUNT 1.16, libxt_ACCOUNT 1.3
Inclusion into a kernel tree
============================

View File

@@ -1,9 +1,9 @@
AC_INIT([xtables-addons], [1.32])
AC_INIT([xtables-addons], [1.36])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_INSTALL
AM_INIT_AUTOMAKE([1.10.2 -Wall foreign subdir-objects])
AM_INIT_AUTOMAKE([1.10b -Wall foreign subdir-objects])
AC_PROG_CC
AM_PROG_CC_C_O
AC_DISABLE_STATIC
@@ -40,44 +40,36 @@ regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
-Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \
-Winline -pipe";
#
# check kernel version
#
if grep -q "CentOS release 5\." /etc/redhat-release 2>/dev/null ||
grep -q "Red Hat Enterprise Linux Server release 5" /etc/redhat-release 2>/dev/null; then
# しまった!
# Well, just a warning. Maybe the admin updated the kernel.
echo "WARNING: This distribution's shipped kernel is not supported.";
fi;
AC_MSG_CHECKING([kernel version that we will build against])
krel="$(make -sC "$kbuilddir" M=. kernelrelease)";
kmajor="${krel%%[[^0-9]]*}";
kmajor="$(($kmajor+0))";
krel="${krel:${#kmajor}}";
krel="${krel#.}";
kminor="${krel%%[[^0-9]]*}";
kminor="$(($kminor+0))";
krel="${krel:${#kminor}}";
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 -n "$kbuilddir"; then
AC_MSG_CHECKING([kernel version that we will build against])
krel="$(make -sC "$kbuilddir" M=. kernelrelease)";
kmajor="${krel%%[[^0-9]]*}";
kmajor="$(($kmajor+0))";
krel="${krel:${#kmajor}}";
krel="${krel#.}";
kminor="${krel%%[[^0-9]]*}";
kminor="$(($kminor+0))";
krel="${krel:${#kminor}}";
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
echo "WARNING: Version detection did not succeed. Continue at own luck.";
else
else
echo "$kmajor.$kminor.$kmicro.$kstable in $kbuilddir";
if test "$kmajor" -gt 2 -o "$kminor" -gt 6 -o "$kmicro" -gt 36; then
if test "$kmajor" -gt 3 -o "$kmajor" -eq 3 -a "$kminor" -gt 1; then
echo "WARNING: You are trying a newer kernel. Results may vary. :-)";
elif test \( "$kmajor" -lt 2 -o \
\( "$kmajor" -eq 2 -a "$kminor" -lt 6 \) -o \
\( "$kmajor" -eq 2 -a "$kminor" -eq 0 -a "$kmicro" -lt 17 \) -o \
\( "$kmajor" -eq 2 -a "$kminor" -eq 6 -a "$kmicro" -eq 18 -a \
"$kstable" -lt 5 \) \); then
echo "ERROR: That kernel version is not supported. Please see INSTALL for minimum configuration.";
exit 1;
elif test "$kmajor" -eq 3; then
:;
elif test "$kmajor" -eq 2 -a "$kminor" -ge 29; then
:;
else
echo "WARNING: That kernel version is not supported.";
fi;
fi;
fi;
@@ -87,6 +79,6 @@ AC_SUBST([kbuilddir])
AC_SUBST([xtlibdir])
AC_CONFIG_FILES([Makefile Makefile.iptrules Makefile.mans geoip/Makefile
extensions/Makefile extensions/ACCOUNT/Makefile
extensions/ipset-4/Makefile extensions/ipset-5/Makefile
extensions/ipset-4/Makefile extensions/ipset-6/Makefile
extensions/pknock/Makefile])
AC_OUTPUT

View File

@@ -3,6 +3,63 @@ HEAD
====
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.
Expect that compat code will be removed shortly.
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:

View File

@@ -1,8 +1,13 @@
# -*- Makefile -*-
AM_CPPFLAGS = ${regular_CPPFLAGS} -I${abs_top_srcdir}/extensions
AM_CFLAGS = ${regular_CFLAGS}
include ../../Makefile.extra
sbin_PROGRAMS = iptaccount
iptaccount_LDADD = libxt_ACCOUNT_cl.la
lib_LTLIBRARIES = libxt_ACCOUNT_cl.la
man_MANS = iptaccount.8

View File

@@ -0,0 +1 @@
1.16

View File

@@ -0,0 +1,26 @@
.TH iptaccount 8 "v1.16" "" "v1.16"
.SH Name
iptaccount \(em administrative utility to access xt_ACCOUNT statistics
.SH Syntax
\fBiptaccount\fP [\fB\-acfhu\fP] [\fB\-l\fP \fIname\fP]
.SH Options
.PP
\fB\-a\fP
List all (accounting) table names.
.PP
\fB\-c\fP
Loop every second (abort with CTRL+C).
.PP
\fB\-f\fP
Flush data after display.
.PP
\fB\-h\fP
Free all kernel handles. (Experts only!)
.PP
\fB\-l\fP \fIname\fP
Show data in accounting table called by \fIname\fP.
.TP
\fB\-u\fP
Show kernel handle usage.
.SH "See also"
\fBxtables-addons\fP(8)

View File

@@ -105,11 +105,11 @@ static void account_tg_print_it(const void *ip,
struct in_addr a;
if (!do_prefix)
printf("ACCOUNT ");
printf(" ACCOUNT ");
// Network information
if (do_prefix)
printf("--");
printf(" --");
printf("%s ", account_tg_opts[0].name);
a.s_addr = accountinfo->net_ip;
@@ -119,7 +119,7 @@ static void account_tg_print_it(const void *ip,
printf(" ");
if (do_prefix)
printf("--");
printf(" --");
printf("%s %s", account_tg_opts[1].name, accountinfo->table_name);
}

View File

@@ -40,19 +40,7 @@ to account the overall traffic to/from your internet provider.
.PP
The data can be queried using the userspace libxt_ACCOUNT_cl library,
and by the reference implementation to show usage of this library,
the \fBiptaccount\fP(8) tool, which features following options:
.PP
[\fB\-u\fP] show kernel handle usage
.PP
[\fB\-h\fP] free all kernel handles (experts only!)
.PP
[\fB\-a\fP] list all table names
.PP
[\fB\-l\fP \fIname\fP] show data in table \fIname\fP
.PP
[\fB\-f\fP] flush data after showing
.PP
[\fB\-c\fP] loop every second (abort with CTRL+C)
the \fBiptaccount\fP(8) tool.
.PP
Here is an example of use:
.PP

View File

@@ -3,7 +3,7 @@
* See http://www.intra2net.com/opensource/ipt_account *
* for further information *
* *
* Copyright (C) 2004-2008 by Intra2net AG *
* Copyright (C) 2004-2011 by Intra2net AG *
* opensource@intra2net.com *
* *
* This program is free software; you can redistribute it and/or modify *

View File

@@ -10,6 +10,7 @@ obj-${build_CHAOS} += xt_CHAOS.o
obj-${build_CHECKSUM} += xt_CHECKSUM.o
obj-${build_DELUDE} += xt_DELUDE.o
obj-${build_DHCPMAC} += xt_DHCPMAC.o
obj-${build_DNETMAP} += xt_DNETMAP.o
obj-${build_ECHO} += xt_ECHO.o
obj-${build_IPMARK} += xt_IPMARK.o
obj-${build_LOGMARK} += xt_LOGMARK.o
@@ -27,7 +28,7 @@ obj-${build_geoip} += xt_geoip.o
obj-${build_iface} += xt_iface.o
obj-${build_ipp2p} += xt_ipp2p.o
obj-${build_ipset4} += ipset-4/
obj-${build_ipset5} += ipset-5/
obj-${build_ipset6} += ipset-6/
obj-${build_ipv4options} += xt_ipv4options.o
obj-${build_length2} += xt_length2.o
obj-${build_lscan} += xt_lscan.o

View File

@@ -5,6 +5,7 @@ obj-${build_CHAOS} += libxt_CHAOS.so
obj-${build_CHECKSUM} += libxt_CHECKSUM.so
obj-${build_DELUDE} += libxt_DELUDE.so
obj-${build_DHCPMAC} += libxt_DHCPMAC.so libxt_dhcpmac.so
obj-${build_DNETMAP} += libxt_DNETMAP.so
obj-${build_ECHO} += libxt_ECHO.so
obj-${build_IPMARK} += libxt_IPMARK.so
obj-${build_LOGMARK} += libxt_LOGMARK.so
@@ -19,7 +20,7 @@ obj-${build_geoip} += libxt_geoip.so
obj-${build_iface} += libxt_iface.so
obj-${build_ipp2p} += libxt_ipp2p.so
obj-${build_ipset4} += ipset-4/
obj-${build_ipset5} += ipset-5/
obj-${build_ipset6} += ipset-6/
obj-${build_ipv4options} += libxt_ipv4options.so
obj-${build_length2} += libxt_length2.so
obj-${build_lscan} += libxt_lscan.so

View File

@@ -0,0 +1 @@
4.5

View File

@@ -1,649 +0,0 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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.
*/
/* Kernel module implementing an IP set type: the bitmap:port type */
#include "ip_set_kernel.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/netlink.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <net/netlink.h>
#include "ip_set.h"
#include "ip_set_bitmap.h"
#include "ip_set_getport.h"
#define IP_SET_BITMAP_TIMEOUT
#include "ip_set_timeout.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("bitmap:port type of IP sets");
MODULE_ALIAS("ip_set_bitmap:port");
/* Base variant */
struct bitmap_port {
void *members; /* the set members */
u16 first_port; /* host byte order, included in range */
u16 last_port; /* host byte order, included in range */
size_t memsize; /* members size */
};
static inline int
bitmap_port_test(const struct bitmap_port *map, u16 id)
{
return !!test_bit(id, map->members);
}
static inline int
bitmap_port_add(struct bitmap_port *map, u16 id)
{
if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
}
static int
bitmap_port_del(struct bitmap_port *map, u16 id)
{
if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
}
static int
bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_port *map = set->data;
u16 port = 0;
if (!get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &port))
return -EINVAL;
port = ntohs(port);
if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
port -= map->first_port;
switch (adt) {
case IPSET_TEST:
return bitmap_port_test(map, port);
case IPSET_ADD:
return bitmap_port_add(map, port);
case IPSET_DEL:
return bitmap_port_del(map, port);
default:
return -EINVAL;
}
}
static const struct nla_policy bitmap_port_adt_policy[IPSET_ATTR_ADT_MAX+1] = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct bitmap_port *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
u32 port; /* wraparound */
u16 id, port_to;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_port_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
if (tb[IPSET_ATTR_PORT])
port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT])
return -IPSET_ERR_TIMEOUT;
if (adt == IPSET_TEST)
return bitmap_port_test(map, port - map->first_port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to) {
swap(port, port_to);
if (port < map->first_port)
return -IPSET_ERR_BITMAP_RANGE;
}
} else
port_to = port;
if (port_to > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
for (; port <= port_to; port++) {
id = port - map->first_port;
ret = adt == IPSET_ADD ? bitmap_port_add(map, id)
: bitmap_port_del(map, id);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static void
bitmap_port_destroy(struct ip_set *set)
{
struct bitmap_port *map = set->data;
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_port_flush(struct ip_set *set)
{
struct bitmap_port *map = set->data;
memset(map->members, 0, map->memsize);
}
static int
bitmap_port_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_port *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port));
NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->memsize));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EFAULT;
}
static int
bitmap_port_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_port *map = set->data;
struct nlattr *atd, *nested;
u16 id, first = cb->args[2];
u16 last = map->last_port - map->first_port;
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
if (!test_bit(id, map->members))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EFAULT;
} else
goto nla_put_failure;
}
NLA_PUT_NET16(skb, IPSET_ATTR_PORT,
htons(map->first_port + id));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, atd);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
return 0;
}
static bool
bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct bitmap_port *x = a->data;
const struct bitmap_port *y = b->data;
return x->first_port == y->first_port
&& x->last_port == y->last_port;
}
const struct ip_set_type_variant bitmap_port = {
.kadt = bitmap_port_kadt,
.uadt = bitmap_port_uadt,
.destroy = bitmap_port_destroy,
.flush = bitmap_port_flush,
.head = bitmap_port_head,
.list = bitmap_port_list,
.same_set = bitmap_port_same_set,
};
/* Timeout variant */
struct bitmap_port_timeout {
unsigned long *members; /* the set members */
u16 first_port; /* host byte order, included in range */
u16 last_port; /* host byte order, included in range */
size_t memsize; /* members size */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
static inline bool
bitmap_port_timeout_test(const struct bitmap_port_timeout *map, u16 id)
{
return ip_set_timeout_test(map->members[id]);
}
static int
bitmap_port_timeout_add(const struct bitmap_port_timeout *map,
u16 id, u32 timeout)
{
if (bitmap_port_timeout_test(map, id))
return -IPSET_ERR_EXIST;
map->members[id] = ip_set_timeout_set(timeout);
return 0;
}
static int
bitmap_port_timeout_del(const struct bitmap_port_timeout *map,
u16 id)
{
int ret = -IPSET_ERR_EXIST;
if (bitmap_port_timeout_test(map, id))
ret = 0;
map->members[id] = IPSET_ELEM_UNSET;
return ret;
}
static int
bitmap_port_timeout_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_port_timeout *map = set->data;
u16 port = 0;
if (!get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &port))
return -EINVAL;
port = ntohs(port);
if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
port -= map->first_port;
switch (adt) {
case IPSET_TEST:
return bitmap_port_timeout_test(map, port);
case IPSET_ADD:
return bitmap_port_timeout_add(map, port, map->timeout);
case IPSET_DEL:
return bitmap_port_timeout_del(map, port);
default:
return -EINVAL;
}
}
static int
bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct bitmap_port_timeout *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
u16 id, port_to;
u32 port, timeout = map->timeout; /* wraparound */
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_port_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
if (tb[IPSET_ATTR_PORT])
port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
if (adt == IPSET_TEST)
return bitmap_port_timeout_test(map, port - map->first_port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to) {
swap(port, port_to);
if (port < map->first_port)
return -IPSET_ERR_BITMAP_RANGE;
}
} else
port_to = port;
if (port_to > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT])
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
for (; port <= port_to; port++) {
id = port - map->first_port;
ret = adt == IPSET_ADD
? bitmap_port_timeout_add(map, id, timeout)
: bitmap_port_timeout_del(map, id);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static void
bitmap_port_timeout_destroy(struct ip_set *set)
{
struct bitmap_port_timeout *map = set->data;
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_port_timeout_flush(struct ip_set *set)
{
struct bitmap_port_timeout *map = set->data;
memset(map->members, 0, map->memsize);
}
static int
bitmap_port_timeout_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_port_timeout *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port));
NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT , htonl(map->timeout));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->memsize));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EFAULT;
}
static int
bitmap_port_timeout_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_port_timeout *map = set->data;
struct nlattr *adt, *nested;
u16 id, first = cb->args[2];
u16 last = map->last_port - map->first_port;
const unsigned long *table = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!adt)
return -EFAULT;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
if (!bitmap_port_timeout_test(map, id))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, adt);
return -EFAULT;
} else
goto nla_put_failure;
}
NLA_PUT_NET16(skb, IPSET_ATTR_PORT,
htons(map->first_port + id));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(table[id])));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, adt);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
return 0;
}
static bool
bitmap_port_timeout_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct bitmap_port_timeout *x = a->data;
const struct bitmap_port_timeout *y = b->data;
return x->first_port == y->first_port
&& x->last_port == y->last_port
&& x->timeout == y->timeout;
}
const struct ip_set_type_variant bitmap_port_timeout = {
.kadt = bitmap_port_timeout_kadt,
.uadt = bitmap_port_timeout_uadt,
.destroy = bitmap_port_timeout_destroy,
.flush = bitmap_port_timeout_flush,
.head = bitmap_port_timeout_head,
.list = bitmap_port_timeout_list,
.same_set = bitmap_port_timeout_same_set,
};
static void
bitmap_port_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_port_timeout *map = set->data;
unsigned long *table = map->members;
u32 id; /* wraparound */
u16 last = map->last_port - map->first_port;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (id = 0; id <= last; id++)
if (ip_set_timeout_expired(table[id]))
table[id] = IPSET_ELEM_UNSET;
read_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static inline void
bitmap_port_gc_init(struct ip_set *set)
{
struct bitmap_port_timeout *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = bitmap_port_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
/* Create bitmap:ip type of sets */
static const struct nla_policy
bitmap_port_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static bool
init_map_port(struct ip_set *set, struct bitmap_port *map,
u16 first_port, u16 last_port)
{
map->members = ip_set_alloc(map->memsize, GFP_KERNEL);
if (!map->members)
return false;
map->first_port = first_port;
map->last_port = last_port;
set->data = map;
set->family = AF_UNSPEC;
return true;
}
static int
bitmap_port_create(struct ip_set *set, struct nlattr *head, int len,
u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u16 first_port, last_port;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_port_create_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PORT])
first_port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PORT_TO]) {
last_port = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (first_port > last_port) {
u16 tmp = first_port;
first_port = last_port;
last_port = tmp;
}
} else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_TIMEOUT]) {
struct bitmap_port_timeout *map;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map->memsize = (last_port - first_port + 1)
* sizeof(unsigned long);
if (!init_map_port(set, (struct bitmap_port *) map,
first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_port_timeout;
bitmap_port_gc_init(set);
} else {
struct bitmap_port *map;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map->memsize = bitmap_bytes(0, last_port - first_port);
pr_debug("memsize: %zu", map->memsize);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
set->variant = &bitmap_port;
}
return 0;
}
struct ip_set_type bitmap_port_type = {
.name = "bitmap:port",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_PORT,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.create = bitmap_port_create,
.me = THIS_MODULE,
};
static int __init
bitmap_port_init(void)
{
return ip_set_type_register(&bitmap_port_type);
}
static void __exit
bitmap_port_fini(void)
{
ip_set_type_unregister(&bitmap_port_type);
}
module_init(bitmap_port_init);
module_exit(bitmap_port_fini);

File diff suppressed because it is too large Load Diff

View File

@@ -1,126 +0,0 @@
#ifndef _IP_SET_GETPORT_H
#define _IP_SET_GETPORT_H
#ifdef __KERNEL__
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/ip.h>
#define IPSET_INVALID_PORT 65536
/* We must handle non-linear skbs */
static inline bool
get_port(const struct sk_buff *skb, int protocol, unsigned int protooff,
bool src, u16 *port, u8 *proto)
{
switch (protocol) {
case IPPROTO_TCP: {
struct tcphdr _tcph;
const struct tcphdr *th;
th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph);
if (th == NULL)
/* No choice either */
return false;
*port = src ? th->source : th->dest;
break;
}
case IPPROTO_UDP: {
struct udphdr _udph;
const struct udphdr *uh;
uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph);
if (uh == NULL)
/* No choice either */
return false;
*port = src ? uh->source : uh->dest;
break;
}
case IPPROTO_ICMP: {
struct icmphdr _icmph;
const struct icmphdr *ic;
ic = skb_header_pointer(skb, protooff, sizeof(_icmph), &_icmph);
if (ic == NULL)
return false;
*port = (ic->type << 8) & ic->code;
break;
}
case IPPROTO_ICMPV6: {
struct icmp6hdr _icmph;
const struct icmp6hdr *ic;
ic = skb_header_pointer(skb, protooff, sizeof(_icmph), &_icmph);
if (ic == NULL)
return false;
*port = (ic->icmp6_type << 8) & ic->icmp6_code;
break;
}
default:
break;
}
*proto = protocol;
return true;
}
static inline bool
get_ip4_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto)
{
const struct iphdr *iph = ip_hdr(skb);
unsigned int protooff = ip_hdrlen(skb);
int protocol = iph->protocol;
/* See comments at tcp_match in ip_tables.c */
if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET))
return false;
return get_port(skb, protocol, protooff, src, port, proto);
}
static inline bool
get_ip6_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto)
{
unsigned int protooff = 0;
int protocol;
unsigned short fragoff;
protocol = ipv6_find_hdr(skb, &protooff, -1, &fragoff);
if (protocol <= 0 || fragoff)
return false;
return get_port(skb, protocol, protooff, src, port, proto);
}
static inline bool
get_ip_port(const struct sk_buff *skb, u8 pf, bool src, u16 *port)
{
bool ret;
u8 proto;
switch (pf) {
case AF_INET:
ret = get_ip4_port(skb, src, port, &proto);
case AF_INET6:
ret = get_ip6_port(skb, src, port, &proto);
default:
return false;
}
if (!ret)
return ret;
switch (proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
return true;
default:
return false;
}
}
#endif /* __KERNEL__ */
#endif /*_IP_SET_GETPORT_H*/

View File

@@ -1,15 +0,0 @@
#ifndef _IP_SET_KERNEL_H
#define _IP_SET_KERNEL_H
#ifdef __KERNEL__
#ifdef CONFIG_DEBUG_KERNEL
/* Complete debug messages */
#define pr_fmt(fmt) "%s %s[%i]: " fmt "\n", __FILE__, __func__, __LINE__
#endif
#include <linux/kernel.h>
#endif /* __KERNEL__ */
#endif /*_IP_SET_H */

View File

@@ -1,89 +0,0 @@
#ifndef _IP_SET_SLIST_H
#define _IP_SET_SLIST_H
#include <linux/stddef.h>
#include <linux/prefetch.h>
#include <asm/system.h>
/*
* Single linked lists with a single pointer.
* Mostly useful for hash tables where the two pointer list head
* and list node is too wasteful.
*/
struct slist {
struct slist *next;
};
#define SLIST(name) struct slist name = { .next = NULL }
#define INIT_SLIST(ptr) ((ptr)->next = NULL)
#define slist_entry(ptr, type, member) container_of(ptr, type, member)
#define slist_for_each(pos, head) \
for (pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)
#define slist_for_each_prev(prev, pos, head) \
for (prev = head, pos = (head)->next; \
pos && ({ prefetch(pos->next); 1; }); \
prev = pos, pos = pos->next)
#define slist_for_each_safe(pos, n, head) \
for (pos = (head)->next; pos && ({ n = pos->next; 1; }); \
pos = n)
/**
* slist_for_each_entry - iterate over list of given type
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct slist to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the slist within the struct.
*/
#define slist_for_each_entry(tpos, pos, head, member) \
for (pos = (head)->next; \
pos && ({ prefetch(pos->next); 1; }) && \
({ tpos = slist_entry(pos, typeof(*tpos), member); 1; });\
pos = pos->next)
/**
* slist_for_each_entry_continue - iterate over a hlist continuing
* after current point
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct slist to use as a loop cursor.
* @member: the name of the slist within the struct.
*/
#define slist_for_each_entry_continue(tpos, pos, member) \
for (pos = (pos)->next; \
pos && ({ prefetch(pos->next); 1; }) && \
({ tpos = slist_entry(pos, typeof(*tpos), member); 1; });\
pos = pos->next)
/**
* slist_for_each_entry_from - iterate over a hlist continuing
* from current point
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct slist to use as a loop cursor.
* @member: the name of the slist within the struct.
*/
#define slist_for_each_entry_from(tpos, pos, member) \
for (; pos && ({ prefetch(pos->next); 1; }) && \
({ tpos = slist_entry(pos, typeof(*tpos), member); 1; });\
pos = pos->next)
/**
* slist_for_each_entry_safe - iterate over list of given type safe against
* removal of list entry
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct slist to use as a loop cursor.
* @n: another &struct slist to use as temporary storage
* @head: the head for your list.
* @member: the name of the slist within the struct.
*/
#define slist_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->next; \
pos && ({ n = pos->next; 1; }) && \
({ tpos = slist_entry(pos, typeof(*tpos), member); 1; });\
pos = n)
#endif /* _IP_SET_SLIST_H */

View File

@@ -4,4 +4,8 @@ obj-m += xt_set.o
obj-m += ip_set.o ip_set_bitmap_ip.o ip_set_bitmap_ipmac.o
obj-m += ip_set_bitmap_port.o ip_set_hash_ip.o ip_set_hash_ipport.o
obj-m += ip_set_hash_ipportip.o ip_set_hash_ipportnet.o ip_set_hash_net.o
obj-m += ip_set_hash_netport.o ip_set_list_set.o
obj-m += ip_set_hash_netiface.o ip_set_hash_netport.o ip_set_list_set.o
ip_set-y := ip_set_core.o ip_set_getport.o pfxlen.o
EXTRA_CFLAGS += -DLCONFIG_IP_SET_MAX=256

View File

@@ -1,6 +1,7 @@
# -*- Makefile -*-
AM_CFLAGS = ${regular_CFLAGS} ${libmnl_CFLAGS} -Iinclude
AM_CPPFLAGS = -I${srcdir}/include -DNDEBUG
AM_CFLAGS = ${regular_CFLAGS} ${libmnl_CFLAGS}
include ../../Makefile.extra
@@ -16,8 +17,9 @@ ipset_SOURCES = src/ipset.c src/errcode.c src/ui.c src/ipset_bitmap_ip.c \
src/ipset_bitmap_ipmac.c src/ipset_bitmap_port.c \
src/ipset_hash_ip.c src/ipset_hash_ipport.c \
src/ipset_hash_ipportip.c src/ipset_hash_ipportnet.c \
src/ipset_hash_net.c src/ipset_hash_netport.c \
src/ipset_hash_net.c src/ipset_hash_netiface.c \
src/ipset_hash_netport.c \
src/ipset_list_set.c
ipset_LDADD = libipset.la
man_MANS = ipset.8
man_MANS = src/ipset.8

88
extensions/ipset-6/README Normal file
View File

@@ -0,0 +1,88 @@
This is the ipset source tree. Follow the next steps to install ipset.
If you upgrade from an earlier 5.x release, please read the UPGRADE
instructions too.
0. You need the source tree of your kernel (version >= 2.6.34)
and it have to be configured with ip6tables support enabled,
modules compiled. Please apply the netlink.patch against your kernel
tree, which adds the new subsystem identifier for ipset.
Recompile and install the patched kernel and its modules. Please note,
you have to run the patched kernel for ipset to work.
The ipset source code depends on the libmnl library so the library
must be installed. You can download the libmnl library from
git://git.netfilter.org/libmnl.git
1. Initialize the compiling environment for ipset. The packages automake,
autoconf and libtool are required.
% ./autogen.sh
2. Run `./configure` and then compile the ipset binary and the kernel
modules.
Configure parameters can be used to to override the default path
to the kernel source tree (/lib/modules/`uname -r`/build),
the maximum number of sets (256), the default hash sizes (1024).
See `./configure --help`.
% ./configure
% make
% make modules
3. Install the binary and the kernel modules
# make install
# make modules_install
After installing the modules, you can run the testsuite as well.
Please note, several assumptions must be met for the testsuite:
- no sets defined
- iptables/ip6tables rules are not set up
- the destination for kernel logs is /var/log/kern.log
- the networks 10.255.255.0/24 and 1002:1002:1002:1002::/64
are not in use
- sendip utility is installed
# make tests
4. Cleanup the source tree
% make clean
% make modules_clean
That's it!
Read the ipset(8) and iptables(8), ip6tables(8) manpages on how to use
ipset and its match and target from iptables.
Compatibilities and incompatibilities:
- The ipset 6.x userspace utility contains a backward compatibility
interface to support the commandline syntax of ipset 4.x.
The commandline syntax of ipset 6.x is fully compatible with 5.x.
- The ipset 6.x userspace utility can't talk to the kernel part of ipset 5.x
or 4.x.
- The ipset 6.x kernel part can't talk to the userspace utility from
ipset 5.x or 4.x.
- The ipset 6.x kernel part can work together with the set match and SET
target from iptables 1.4.7 and below, however if you need the IPv6 support
from ipset 6.x, then you have to use iptables 1.4.8 or above.
The ipset 6.x can interpret the commandline syntax of ipset 4.x, however
some internal changes mean different behaviour:
- The "--matchunset" flag for the macipmap type is ignored and not used
anymore.
- The "--probes" and "--resize" parameters of the hash types are ignored
and not used anymore.
- The "--from", "--to" and "--network" parameters of the ipporthash,
ipportiphash and ipportnethash types are ignored and not used anymore.
- The hash types are not resized when new entries are added by the SET
target. If you use a set together with the SET target, create it with
the proper size because it won't be resized automatically.
- The iptree, iptreemap types are not implemented in ipset 6.x. The types
are automatically substituted with the hash:ip type.

View File

@@ -0,0 +1 @@
5.4.1-genl

View File

@@ -44,12 +44,15 @@ enum ipset_opt {
IPSET_OPT_NAMEREF,
IPSET_OPT_IP2,
IPSET_OPT_CIDR2,
IPSET_OPT_IP2_TO,
IPSET_OPT_PROTO,
IPSET_OPT_IFACE,
/* Swap/rename to */
IPSET_OPT_SETNAME2,
/* Flags */
IPSET_OPT_EXIST,
IPSET_OPT_BEFORE,
IPSET_OPT_PHYSDEV,
/* Internal options */
IPSET_OPT_FLAGS = 48, /* IPSET_FLAG_EXIST| */
IPSET_OPT_CADT_FLAGS, /* IPSET_FLAG_BEFORE| */
@@ -65,7 +68,7 @@ enum ipset_opt {
#define IPSET_FLAGS_ALL (~0LL)
#define IPSET_CREATE_FLAGS \
( IPSET_FLAG(IPSET_OPT_FAMILY) \
(IPSET_FLAG(IPSET_OPT_FAMILY) \
| IPSET_FLAG(IPSET_OPT_TYPENAME)\
| IPSET_FLAG(IPSET_OPT_TYPE) \
| IPSET_FLAG(IPSET_OPT_IP) \
@@ -83,7 +86,7 @@ enum ipset_opt {
| IPSET_FLAG(IPSET_OPT_SIZE))
#define IPSET_ADT_FLAGS \
( IPSET_FLAG(IPSET_OPT_IP) \
(IPSET_FLAG(IPSET_OPT_IP) \
| IPSET_FLAG(IPSET_OPT_IP_TO) \
| IPSET_FLAG(IPSET_OPT_CIDR) \
| IPSET_FLAG(IPSET_OPT_PORT) \
@@ -95,8 +98,10 @@ enum ipset_opt {
| IPSET_FLAG(IPSET_OPT_IP2) \
| IPSET_FLAG(IPSET_OPT_CIDR2) \
| IPSET_FLAG(IPSET_OPT_PROTO) \
| IPSET_FLAG(IPSET_OPT_IFACE) \
| IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\
| IPSET_FLAG(IPSET_OPT_BEFORE))
| IPSET_FLAG(IPSET_OPT_BEFORE) \
| IPSET_FLAG(IPSET_OPT_PHYSDEV))
struct ipset_data;
@@ -109,7 +114,7 @@ extern bool ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt);
extern int ipset_data_set(struct ipset_data *data, enum ipset_opt opt,
const void *value);
extern const void * ipset_data_get(const struct ipset_data *data,
extern const void *ipset_data_get(const struct ipset_data *data,
enum ipset_opt opt);
static inline bool
@@ -119,13 +124,13 @@ ipset_data_test(const struct ipset_data *data, enum ipset_opt opt)
}
/* Shortcuts */
extern const char * ipset_data_setname(const struct ipset_data *data);
extern const char *ipset_data_setname(const struct ipset_data *data);
extern uint8_t ipset_data_family(const struct ipset_data *data);
extern uint8_t ipset_data_cidr(const struct ipset_data *data);
extern uint64_t ipset_data_flags(const struct ipset_data *data);
extern void ipset_data_reset(struct ipset_data *data);
extern struct ipset_data * ipset_data_init(void);
extern struct ipset_data *ipset_data_init(void);
extern void ipset_data_fini(struct ipset_data *data);
extern size_t ipset_data_sizeof(enum ipset_opt opt, uint8_t family);

View File

@@ -12,7 +12,7 @@
#include <sys/socket.h>
#include <linux/netlink.h>
#define D(fmt, args...) \
fprintf(stderr, "%s: %s: " fmt "\n", __FILE__, __FUNCTION__ , ## args)
fprintf(stderr, "%s: %s: " fmt "\n", __FILE__, __func__ , ## args)
#define IF_D(test, fmt, args...) \
if (test) \
D(fmt , ## args)

View File

@@ -9,8 +9,8 @@
#include <stdint.h> /* uintxx_t */
extern const char * id_to_icmp(uint8_t id);
extern const char * icmp_to_name(uint8_t type, uint8_t code);
extern const char *id_to_icmp(uint8_t id);
extern const char *icmp_to_name(uint8_t type, uint8_t code);
extern int name_to_icmp(const char *str, uint16_t *typecode);
#endif /* LIBIPSET_ICMP_H */

View File

@@ -9,8 +9,8 @@
#include <stdint.h> /* uintxx_t */
extern const char * id_to_icmpv6(uint8_t id);
extern const char * icmpv6_to_name(uint8_t type, uint8_t code);
extern const char *id_to_icmpv6(uint8_t id);
extern const char *icmpv6_to_name(uint8_t type, uint8_t code);
extern int name_to_icmpv6(const char *str, uint16_t *typecode);
#endif /* LIBIPSET_ICMPV6_H */

View File

@@ -4,7 +4,7 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -12,7 +12,7 @@
*/
/* The protocol version */
#define IPSET_PROTOCOL 5
#define IPSET_PROTOCOL 0x60
/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
@@ -104,6 +104,8 @@ enum {
IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2,
IPSET_ATTR_IP2_TO,
IPSET_ATTR_IFACE,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
@@ -118,7 +120,7 @@ enum {
/* Error codes */
enum ipset_errno {
IPSET_ERR_PRIVATE = 128,
IPSET_ERR_PRIVATE = 4096,
IPSET_ERR_PROTOCOL,
IPSET_ERR_FIND_TYPE,
IPSET_ERR_MAX_SETS,
@@ -135,19 +137,25 @@ enum ipset_errno {
IPSET_ERR_IPADDR_IPV6,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 160,
IPSET_ERR_TYPE_SPECIFIC = 4352,
};
/* Flags at command level */
enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
IPSET_FLAG_BIT_LIST_SETNAME = 1,
IPSET_FLAG_LIST_SETNAME = (1 << IPSET_FLAG_BIT_LIST_SETNAME),
IPSET_FLAG_BIT_LIST_HEADER = 2,
IPSET_FLAG_LIST_HEADER = (1 << IPSET_FLAG_BIT_LIST_HEADER),
};
/* Flags at CADT attribute level */
enum ipset_cadt_flags {
IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE),
IPSET_FLAG_BIT_PHYSDEV = 1,
IPSET_FLAG_PHYSDEV = (1 << IPSET_FLAG_BIT_PHYSDEV),
};
/* Commands with settype-specific attributes */

View File

@@ -11,6 +11,10 @@ enum {
IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO,
/* Range not supported */
IPSET_ERR_HASH_RANGE_UNSUPPORTED,
/* Invalid range */
IPSET_ERR_HASH_RANGE,
};
#endif /* __IP_SET_HASH_H */

View File

@@ -17,6 +17,7 @@
#define IPSET_PROTO_SEPARATOR ":"
struct ipset_session;
struct ipset_arg;
typedef int (*ipset_parsefn)(struct ipset_session *s,
enum ipset_opt opt, const char *str);
@@ -59,6 +60,8 @@ extern int ipset_parse_ipnet(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_ip4_single6(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_ip4_net6(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_name(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_before(struct ipset_session *session,
@@ -77,6 +80,8 @@ extern int ipset_parse_flag(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_typename(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_iface(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_output(struct ipset_session *session,
int opt, const char *str);
extern int ipset_parse_ignored(struct ipset_session *session,
@@ -84,8 +89,8 @@ extern int ipset_parse_ignored(struct ipset_session *session,
extern int ipset_parse_elem(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_call_parser(struct ipset_session *session,
ipset_parsefn parse, const char *optstr,
enum ipset_opt optional, const char *str);
const struct ipset_arg *arg,
const char *str);
/* Compatibility parser functions */
extern int ipset_parse_iptimeout(struct ipset_session *session,

View File

@@ -12,7 +12,7 @@
{.ip6 = { \
__constant_htonl(a), __constant_htonl(b), \
__constant_htonl(c), __constant_htonl(d), \
}}
} }
/*
* This table works for both IPv4 and IPv6;

View File

@@ -37,6 +37,9 @@ extern int ipset_print_name(char *buf, unsigned int len,
extern int ipset_print_port(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_iface(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_proto(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);

View File

@@ -21,9 +21,14 @@ struct ipset_session;
struct ipset_data;
struct ipset_handle;
extern struct ipset_data * ipset_session_data(const struct ipset_session *session);
extern struct ipset_handle * ipset_session_handle(const struct ipset_session *session);
extern const struct ipset_type * ipset_saved_type(const struct ipset_session *session);
extern struct ipset_data *
ipset_session_data(const struct ipset_session *session);
extern struct ipset_handle *
ipset_session_handle(const struct ipset_session *session);
extern const struct ipset_type *
ipset_saved_type(const struct ipset_session *session);
extern void ipset_session_lineno(struct ipset_session *session,
uint32_t lineno);
enum ipset_err_type {
IPSET_ERROR,
@@ -46,8 +51,8 @@ extern int ipset_session_report(struct ipset_session *session,
})
extern void ipset_session_report_reset(struct ipset_session *session);
extern const char * ipset_session_error(const struct ipset_session *session);
extern const char * ipset_session_warning(const struct ipset_session *session);
extern const char *ipset_session_error(const struct ipset_session *session);
extern const char *ipset_session_warning(const struct ipset_session *session);
#define ipset_session_data_set(session, opt, value) \
ipset_data_set(ipset_session_data(session), opt, value)
@@ -64,6 +69,10 @@ enum ipset_envopt {
IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE),
IPSET_ENV_BIT_EXIST = 3,
IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST),
IPSET_ENV_BIT_LIST_SETNAME = 4,
IPSET_ENV_LIST_SETNAME = (1 << IPSET_ENV_BIT_LIST_SETNAME),
IPSET_ENV_BIT_LIST_HEADER = 5,
IPSET_ENV_LIST_HEADER = (1 << IPSET_ENV_BIT_LIST_HEADER),
};
extern int ipset_envopt_parse(struct ipset_session *session,
@@ -88,7 +97,9 @@ extern int ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd,
typedef int (*ipset_outfn)(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
extern struct ipset_session * ipset_session_init(ipset_outfn outfn);
extern struct ipset_session *ipset_session_init(ipset_outfn outfn);
extern int ipset_session_fini(struct ipset_session *session);
extern void ipset_debug_msg(const char *dir, void *buffer, int len);
#endif /* LIBIPSET_SESSION_H */

View File

@@ -70,7 +70,7 @@ struct ipset_elem {
* but for the readability the full list is supported.
*/
struct ipset_type {
char name[IPSET_MAXNAMELEN]; /* type name */
const char *name;
uint8_t revision; /* revision number */
uint8_t family; /* supported family */
uint8_t dimension; /* elem dimension */
@@ -97,13 +97,14 @@ extern int ipset_cache_swap(const char *from, const char *to);
extern int ipset_cache_init(void);
extern void ipset_cache_fini(void);
extern const struct ipset_type * ipset_type_get(struct ipset_session *session,
enum ipset_cmd cmd);
extern const struct ipset_type * ipset_type_check(struct ipset_session *session);
extern const struct ipset_type *
ipset_type_get(struct ipset_session *session, enum ipset_cmd cmd);
extern const struct ipset_type *
ipset_type_check(struct ipset_session *session);
extern int ipset_type_add(struct ipset_type *type);
extern const struct ipset_type * ipset_types(void);
extern const char * ipset_typename_resolve(const char *str);
extern const struct ipset_type *ipset_types(void);
extern const char *ipset_typename_resolve(const char *str);
extern bool ipset_match_typename(const char *str,
const struct ipset_type *t);

View File

@@ -11,10 +11,10 @@
#include <netinet/in.h> /* struct in[6]_addr */
/* String equality tests */
#define STREQ(a,b) (strcmp(a,b) == 0)
#define STRNEQ(a,b,n) (strncmp(a,b,n) == 0)
#define STRCASEQ(a,b) (strcasecmp(a,b) == 0)
#define STRNCASEQ(a,b,n) (strncasecmp(a,b,n) == 0)
#define STREQ(a, b) (strcmp(a, b) == 0)
#define STRNEQ(a, b, n) (strncmp(a, b, n) == 0)
#define STRCASEQ(a, b) (strcasecmp(a, b) == 0)
#define STRNCASEQ(a, b, n) (strncasecmp(a, b, n) == 0)
/* Stringify tokens */
#define _STR(c) #c
@@ -25,6 +25,11 @@
#define MAX(a, b) (a > b ? a : b)
#define UNUSED __attribute__ ((unused))
#ifdef NDEBUG
#define ASSERT_UNUSED UNUSED
#else
#define ASSERT_UNUSED
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))

View File

@@ -4,15 +4,17 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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.
*/
#include <linux/netlink.h>
/* The protocol version */
#define IPSET_PROTOCOL 5
#define IPSET_PROTOCOL 0x60
/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
@@ -104,6 +106,8 @@ enum {
IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2,
IPSET_ATTR_IP2_TO,
IPSET_ATTR_IFACE,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
@@ -118,7 +122,7 @@ enum {
/* Error codes */
enum ipset_errno {
IPSET_ERR_PRIVATE = 128,
IPSET_ERR_PRIVATE = 4096,
IPSET_ERR_PROTOCOL,
IPSET_ERR_FIND_TYPE,
IPSET_ERR_MAX_SETS,
@@ -135,19 +139,25 @@ enum ipset_errno {
IPSET_ERR_IPADDR_IPV6,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 160,
IPSET_ERR_TYPE_SPECIFIC = 4352,
};
/* Flags at command level */
enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
IPSET_FLAG_BIT_LIST_SETNAME = 1,
IPSET_FLAG_LIST_SETNAME = (1 << IPSET_FLAG_BIT_LIST_SETNAME),
IPSET_FLAG_BIT_LIST_HEADER = 2,
IPSET_FLAG_LIST_HEADER = (1 << IPSET_FLAG_BIT_LIST_HEADER),
};
/* Flags at CADT attribute level */
enum ipset_cadt_flags {
IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE),
IPSET_FLAG_BIT_PHYSDEV = 1,
IPSET_FLAG_PHYSDEV = (1 << IPSET_FLAG_BIT_PHYSDEV),
};
/* Commands with settype-specific attributes */
@@ -165,6 +175,7 @@ enum ipset_adt {
#include <linux/ipv6.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/x_tables.h>
#include <linux/vmalloc.h>
#include <net/netlink.h>
@@ -206,6 +217,8 @@ enum ip_set_feature {
IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG),
IPSET_TYPE_NAME_FLAG = 4,
IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG),
IPSET_TYPE_IFACE_FLAG = 5,
IPSET_TYPE_IFACE = (1 << IPSET_TYPE_IFACE_FLAG),
/* Strictly speaking not a feature, but a flag for dumping:
* this settype must be dumped last */
IPSET_DUMP_LAST_FLAG = 7,
@@ -214,7 +227,17 @@ enum ip_set_feature {
struct ip_set;
typedef int (*ipset_adtfn)(struct ip_set *set, void *value, u32 timeout);
typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
u32 timeout, u32 flags);
/* Kernel API function options */
struct ip_set_adt_opt {
u8 family; /* Actual protocol family */
u8 dim; /* Dimension of match/target */
u8 flags; /* Direction and negation flags */
u32 cmdflags; /* Command-like flags */
u32 timeout; /* Timeout value */
};
/* Set type, variant-specific part */
struct ip_set_type_variant {
@@ -223,14 +246,15 @@ struct ip_set_type_variant {
* zero for no match/success to add/delete
* positive for matching element */
int (*kadt)(struct ip_set *set, const struct sk_buff * skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt);
/* Userspace: test/add/del entries
* returns negative error code,
* zero for no match/success to add/delete
* positive for matching element */
int (*uadt)(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags);
int (*uadt)(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
/* Low level add/del/test functions */
ipset_adtfn adt[IPSET_ADT_MAX];
@@ -268,17 +292,21 @@ struct ip_set_type {
u8 dimension;
/* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */
u8 family;
/* Type revision */
u8 revision;
/* Type revisions */
u8 revision_min, revision_max;
/* Create set */
int (*create)(struct ip_set *set,
struct nlattr *head, int len, u32 flags);
int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags);
/* Attribute policies */
const struct nla_policy create_policy[IPSET_ATTR_CREATE_MAX + 1];
const struct nla_policy adt_policy[IPSET_ATTR_ADT_MAX + 1];
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me;
};
/* register and unregister set type */
extern int ip_set_type_register(struct ip_set_type *set_type);
extern void ip_set_type_unregister(struct ip_set_type *set_type);
@@ -289,13 +317,15 @@ struct ip_set {
/* Lock protecting the set data */
rwlock_t lock;
/* References to the set */
atomic_t ref;
u32 ref;
/* The core set type */
const struct ip_set_type *type;
struct ip_set_type *type;
/* The type variant doing the real job */
const struct ip_set_type_variant *variant;
/* The actual INET family of the set */
u8 family;
/* The type revision */
u8 revision;
/* The type specific data */
void *data;
};
@@ -303,50 +333,39 @@ struct ip_set {
/* register and unregister set references */
extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set);
extern void ip_set_put_byindex(ip_set_id_t index);
extern const char * ip_set_name_byindex(ip_set_id_t index);
extern const char *ip_set_name_byindex(ip_set_id_t index);
extern ip_set_id_t ip_set_nfnl_get(const char *name);
extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index);
extern void ip_set_nfnl_put(ip_set_id_t index);
/* API for iptables set match, and SET target */
extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb,
u8 family, u8 dim, u8 flags);
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt);
extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb,
u8 family, u8 dim, u8 flags);
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt);
extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb,
u8 family, u8 dim, u8 flags);
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt);
/* Allocate members */
static inline void *
ip_set_alloc(size_t size, gfp_t gfp_mask)
/* Utility functions */
extern void *ip_set_alloc(size_t size);
extern void ip_set_free(void *members);
extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr);
extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr);
static inline int
ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr)
{
void *members = NULL;
__be32 ip;
int ret = ip_set_get_ipaddr4(nla, &ip);
if (size < KMALLOC_MAX_SIZE)
members = kzalloc(size, gfp_mask | __GFP_NOWARN);
if (members) {
pr_debug("%p: allocated with kmalloc", members);
return members;
}
members = __vmalloc(size, gfp_mask | __GFP_ZERO, PAGE_KERNEL);
if (!members)
return NULL;
pr_debug("%p: allocated with vmalloc", members);
return members;
}
static inline void
ip_set_free(void *members)
{
pr_debug("%p: free with %s", members,
is_vmalloc_addr(members) ? "vfree" : "kfree");
if (is_vmalloc_addr(members))
vfree(members);
else
kfree(members);
if (ret)
return ret;
*ipaddr = ntohl(ip);
return 0;
}
/* Ignore IPSET_ERR_EXIST errors if asked to do so? */
@@ -356,92 +375,47 @@ ip_set_eexist(int ret, u32 flags)
return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST);
}
/* Check the NLA_F_NET_BYTEORDER flag */
static inline bool
ip_set_attr_netorder(struct nlattr *tb[], int type)
{
return tb[type] && (tb[type]->nla_type & NLA_F_NET_BYTEORDER);
}
static inline bool
ip_set_optattr_netorder(struct nlattr *tb[], int type)
{
return !tb[type] || (tb[type]->nla_type & NLA_F_NET_BYTEORDER);
}
/* Useful converters */
static inline u32
ip_set_get_h32(const struct nlattr *attr)
{
u32 value = nla_get_u32(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohl(value) : value;
return ntohl(nla_get_be32(attr));
}
static inline u16
ip_set_get_h16(const struct nlattr *attr)
{
u16 value = nla_get_u16(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohs(value) : value;
}
static inline u32
ip_set_get_n32(const struct nlattr *attr)
{
u32 value = nla_get_u32(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htonl(value);
}
static inline u16
ip_set_get_n16(const struct nlattr *attr)
{
u16 value = nla_get_u16(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htons(value);
}
static const struct nla_policy ipaddr_policy[IPSET_ATTR_IPADDR_MAX + 1] = {
[IPSET_ATTR_IPADDR_IPV4] = { .type = NLA_U32 },
[IPSET_ATTR_IPADDR_IPV6] = { .type = NLA_BINARY,
.len = sizeof(struct in6_addr) },
};
static inline int
ip_set_get_ipaddr4(struct nlattr *attr[], int type, u32 *ipaddr)
{
struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1] = {};
if (!attr[type])
return -IPSET_ERR_PROTOCOL;
if (nla_parse(tb, IPSET_ATTR_IPADDR_MAX,
nla_data(attr[type]), nla_len(attr[type]),
ipaddr_policy))
return -IPSET_ERR_PROTOCOL;
if (!tb[IPSET_ATTR_IPADDR_IPV4])
return -IPSET_ERR_IPADDR_IPV4;
*ipaddr = ip_set_get_n32(tb[IPSET_ATTR_IPADDR_IPV4]);
return 0;
}
static inline int
ip_set_get_ipaddr6(struct nlattr *attr[], int type, union nf_inet_addr *ipaddr)
{
struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1] = {};
if (!attr[type])
return -IPSET_ERR_PROTOCOL;
if (nla_parse(tb, IPSET_ATTR_IPADDR_MAX,
nla_data(attr[type]), nla_len(attr[type]),
ipaddr_policy))
return -IPSET_ERR_PROTOCOL;
if (!tb[IPSET_ATTR_IPADDR_IPV6])
return -IPSET_ERR_IPADDR_IPV6;
memcpy(ipaddr, nla_data(tb[IPSET_ATTR_IPADDR_IPV6]),
sizeof(struct in6_addr));
return 0;
return ntohs(nla_get_be16(attr));
}
#define ipset_nest_start(skb, attr) nla_nest_start(skb, attr | NLA_F_NESTED)
#define ipset_nest_end(skb, start) nla_nest_end(skb, start)
#define NLA_PUT_NET32(skb, type, value) \
NLA_PUT_BE32(skb, type | NLA_F_NET_BYTEORDER, value)
#define NLA_PUT_NET16(skb, type, value) \
NLA_PUT_BE16(skb, type | NLA_F_NET_BYTEORDER, value)
#ifndef NLA_PUT_NET16
#define NLA_PUT_NET16(skb, attrtype, value) \
NLA_PUT_BE16(skb, attrtype | NLA_F_NET_BYTEORDER, value)
#endif
#ifndef NLA_PUT_NET32
#define NLA_PUT_NET32(skb, attrtype, value) \
NLA_PUT_BE32(skb, attrtype | NLA_F_NET_BYTEORDER, value)
#endif
#ifndef NLA_PUT_NET64
#define NLA_PUT_NET64(skb, attrtype, value) \
NLA_PUT_BE64(skb, attrtype | NLA_F_NET_BYTEORDER, value)
#endif
#define NLA_PUT_IPADDR4(skb, type, ipaddr) \
do { \
@@ -465,14 +439,14 @@ do { \
} while (0)
/* Get address from skbuff */
static inline u32
static inline __be32
ip4addr(const struct sk_buff *skb, bool src)
{
return src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
}
static inline void
ip4addrptr(const struct sk_buff *skb, bool src, u32 *addr)
ip4addrptr(const struct sk_buff *skb, bool src, __be32 *addr)
{
*addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
}
@@ -491,15 +465,6 @@ bitmap_bytes(u32 a, u32 b)
return 4 * ((((b - a + 8) / 8) + 3) / 4);
}
/* Prefixlen maps */
extern const union nf_inet_addr prefixlen_netmask_map[];
extern const union nf_inet_addr prefixlen_hostmask_map[];
#define NETMASK(n) prefixlen_netmask_map[n].ip
#define NETMASK6(n) prefixlen_netmask_map[n].ip6
#define HOSTMASK(n) prefixlen_hostmask_map[n].ip
#define HOSTMASK6(n) prefixlen_hostmask_map[n].ip6
/* Interface to iptables/ip6tables */
#define SO_IP_SET 83

View File

@@ -5,6 +5,11 @@
#include "jhash.h"
#include "ip_set_timeout.h"
#define CONCAT(a, b, c) a##b##c
#define TOKEN(a, b, c) CONCAT(a, b, c)
#define type_pf_next TOKEN(TYPE, PF, _elem)
/* Hashing which uses arrays to resolve clashing. The hash table is resized
* (doubled) when searching becomes too long.
* Internally jhash is used with the assumption that the size of the
@@ -38,7 +43,7 @@ struct htable {
struct hbucket bucket[0]; /* hashtable buckets */
};
#define hbucket(h, i) &((h)->bucket[i])
#define hbucket(h, i) (&((h)->bucket[i]))
/* Book-keeping of the prefixes added to the set */
struct ip_set_hash_nets {
@@ -54,16 +59,20 @@ struct ip_set_hash {
u32 initval; /* random jhash init value */
u32 timeout; /* timeout value, if enabled */
struct timer_list gc; /* garbage collection when timeout enabled */
struct type_pf_next next; /* temporary storage for uadd */
#ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask; /* netmask value for subnets to store */
#endif
#ifdef IP_SET_HASH_WITH_RBTREE
struct rb_root rbtree;
#endif
#ifdef IP_SET_HASH_WITH_NETS
struct ip_set_hash_nets nets[0]; /* book-keeping of prefixes */
#endif
};
/* Compute htable_bits from the user input parameter hashsize */
static inline u8
static u8
htable_bits(u32 hashsize)
{
/* Assume that hashsize == 2^htable_bits */
@@ -81,14 +90,14 @@ htable_bits(u32 hashsize)
/* Network cidr size book keeping when the hash stores different
* sized networks */
static inline void
static void
add_cidr(struct ip_set_hash *h, u8 cidr, u8 host_mask)
{
u8 i;
++h->nets[cidr-1].nets;
pr_debug("add_cidr added %u: %u", cidr, h->nets[cidr-1].nets);
pr_debug("add_cidr added %u: %u\n", cidr, h->nets[cidr-1].nets);
if (h->nets[cidr-1].nets > 1)
return;
@@ -103,14 +112,14 @@ add_cidr(struct ip_set_hash *h, u8 cidr, u8 host_mask)
h->nets[i].cidr = cidr;
}
static inline void
static void
del_cidr(struct ip_set_hash *h, u8 cidr, u8 host_mask)
{
u8 i;
--h->nets[cidr-1].nets;
pr_debug("del_cidr deleted %u: %u", cidr, h->nets[cidr-1].nets);
pr_debug("del_cidr deleted %u: %u\n", cidr, h->nets[cidr-1].nets);
if (h->nets[cidr-1].nets != 0)
return;
@@ -142,7 +151,7 @@ ahash_destroy(struct htable *t)
}
/* Calculate the actual memory size of the set data */
static inline size_t
static size_t
ahash_memsize(const struct ip_set_hash *h, size_t dsize, u8 host_mask)
{
u32 i;
@@ -194,6 +203,9 @@ ip_set_hash_destroy(struct ip_set *set)
del_timer_sync(&h->gc);
ahash_destroy(h->table);
#ifdef IP_SET_HASH_WITH_RBTREE
rbtree_destroy(&h->rbtree);
#endif
kfree(h);
set->data = NULL;
@@ -217,6 +229,7 @@ ip_set_hash_destroy(struct ip_set *set)
#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask)
#define type_pf_data_list TOKEN(TYPE, PF, _data_list)
#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist)
#define type_pf_data_next TOKEN(TYPE, PF, _data_next)
#define type_pf_elem TOKEN(TYPE, PF, _elem)
#define type_pf_telem TOKEN(TYPE, PF, _telem)
@@ -311,8 +324,7 @@ retry:
/* In case we have plenty of memory :-) */
return -IPSET_ERR_HASH_FULL;
t = ip_set_alloc(sizeof(*t)
+ jhash_size(htable_bits) * sizeof(struct hbucket),
GFP_KERNEL);
+ jhash_size(htable_bits) * sizeof(struct hbucket));
if (!t)
return -ENOMEM;
t->htable_bits = htable_bits;
@@ -340,17 +352,20 @@ retry:
/* Give time to other readers of the set */
synchronize_rcu_bh();
pr_debug("set %s resized from %u (%p) to %u (%p)", set->name,
pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name,
orig->htable_bits, orig, t->htable_bits, t);
ahash_destroy(orig);
return 0;
}
static void
type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d);
/* Add an element to a hash and update the internal counters when succeeded,
* otherwise report the proper error code. */
static int
type_pf_add(struct ip_set *set, void *value, u32 timeout)
type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct ip_set_hash *h = set->data;
struct htable *t;
@@ -373,8 +388,11 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout)
}
ret = type_pf_elem_add(n, value);
if (ret != 0)
if (ret != 0) {
if (ret == -EAGAIN)
type_pf_data_next(h, d);
goto out;
}
#ifdef IP_SET_HASH_WITH_NETS
add_cidr(h, d->cidr, HOST_MASK);
@@ -389,7 +407,7 @@ out:
* and free up space if possible.
*/
static int
type_pf_del(struct ip_set *set, void *value, u32 timeout)
type_pf_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct ip_set_hash *h = set->data;
struct htable *t = h->table;
@@ -436,7 +454,7 @@ type_pf_del(struct ip_set *set, void *value, u32 timeout)
/* Special test function which takes into account the different network
* sizes added to the set */
static inline int
static int
type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
{
struct ip_set_hash *h = set->data;
@@ -447,7 +465,7 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
u32 key;
u8 host_mask = SET_HOST_MASK(set->family);
pr_debug("test by nets");
pr_debug("test by nets\n");
for (; j < host_mask && h->nets[j].cidr; j++) {
type_pf_data_netmask(d, h->nets[j].cidr);
key = HKEY(d, h->initval, t->htable_bits);
@@ -464,7 +482,7 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
/* Test whether the element is added to the set */
static int
type_pf_test(struct ip_set *set, void *value, u32 timeout)
type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct ip_set_hash *h = set->data;
struct htable *t = h->table;
@@ -516,8 +534,7 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb)
if (h->netmask != HOST_MASK)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask);
#endif
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize));
if (with_timeout(h->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout));
@@ -525,7 +542,7 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb)
return 0;
nla_put_failure:
return -EFAULT;
return -EMSGSIZE;
}
/* Reply a LIST/SAVE request: dump the elements of the specified set */
@@ -545,21 +562,21 @@ type_pf_list(const struct ip_set *set,
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
pr_debug("list hash set %s", set->name);
return -EMSGSIZE;
pr_debug("list hash set %s\n", set->name);
for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) {
incomplete = skb_tail_pointer(skb);
n = hbucket(t, cb->args[2]);
pr_debug("cb->args[2]: %lu, t %p n %p", cb->args[2], t, n);
pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n);
for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i);
pr_debug("list hash %lu hbucket %p i %u, data %p",
pr_debug("list hash %lu hbucket %p i %u, data %p\n",
cb->args[2], n, i, data);
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (cb->args[2] == first) {
nla_nest_cancel(skb, atd);
return -EFAULT;
return -EMSGSIZE;
} else
goto nla_put_failure;
}
@@ -581,16 +598,18 @@ nla_put_failure:
pr_warning("Can't list set %s: one bucket does not fit into "
"a message. Please report it!\n", set->name);
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
static int
type_pf_kadt(struct ip_set *set, const struct sk_buff * skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt);
static int
type_pf_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags);
type_pf_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
static const struct ip_set_type_variant type_pf_variant = {
.kadt = type_pf_kadt,
@@ -686,7 +705,7 @@ type_pf_expire(struct ip_set_hash *h)
for (j = 0; j < n->pos; j++) {
data = ahash_tdata(n, j);
if (type_pf_data_expired(data)) {
pr_debug("expired %u/%u", i, j);
pr_debug("expired %u/%u\n", i, j);
#ifdef IP_SET_HASH_WITH_NETS
del_cidr(h, data->cidr, HOST_MASK);
#endif
@@ -701,7 +720,7 @@ type_pf_expire(struct ip_set_hash *h)
if (n->pos + AHASH_INIT_SIZE < n->size) {
void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
* sizeof(struct type_pf_telem),
GFP_KERNEL);
GFP_ATOMIC);
if (!tmp)
/* Still try to delete expired elements */
continue;
@@ -742,8 +761,7 @@ retry:
/* In case we have plenty of memory :-) */
return -IPSET_ERR_HASH_FULL;
t = ip_set_alloc(sizeof(*t)
+ jhash_size(htable_bits) * sizeof(struct hbucket),
GFP_KERNEL);
+ jhash_size(htable_bits) * sizeof(struct hbucket));
if (!t)
return -ENOMEM;
t->htable_bits = htable_bits;
@@ -778,7 +796,7 @@ retry:
}
static int
type_pf_tadd(struct ip_set *set, void *value, u32 timeout)
type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct ip_set_hash *h = set->data;
struct htable *t = h->table;
@@ -786,6 +804,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout)
struct hbucket *n;
struct type_pf_elem *data;
int ret = 0, i, j = AHASH_MAX_SIZE + 1;
bool flag_exist = flags & IPSET_FLAG_EXIST;
u32 key;
if (h->elements >= h->maxelem)
@@ -801,14 +820,14 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout)
for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i);
if (type_pf_data_equal(data, d)) {
if (type_pf_data_expired(data))
if (type_pf_data_expired(data) || flag_exist)
j = i;
else {
ret = -IPSET_ERR_EXIST;
goto out;
}
} else if (j == AHASH_MAX_SIZE + 1
&& type_pf_data_expired(data))
} else if (j == AHASH_MAX_SIZE + 1 &&
type_pf_data_expired(data))
j = i;
}
if (j != AHASH_MAX_SIZE + 1) {
@@ -822,8 +841,11 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout)
goto out;
}
ret = type_pf_elem_tadd(n, d, timeout);
if (ret != 0)
if (ret != 0) {
if (ret == -EAGAIN)
type_pf_data_next(h, d);
goto out;
}
#ifdef IP_SET_HASH_WITH_NETS
add_cidr(h, d->cidr, HOST_MASK);
@@ -835,13 +857,13 @@ out:
}
static int
type_pf_tdel(struct ip_set *set, void *value, u32 timeout)
type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct ip_set_hash *h = set->data;
struct htable *t = h->table;
const struct type_pf_elem *d = value;
struct hbucket *n;
int i, ret = 0;
int i;
struct type_pf_elem *data;
u32 key;
@@ -852,7 +874,7 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout)
if (!type_pf_data_equal(data, d))
continue;
if (type_pf_data_expired(data))
ret = -IPSET_ERR_EXIST;
return -IPSET_ERR_EXIST;
if (i != n->pos - 1)
/* Not last one */
type_pf_data_copy(data, ahash_tdata(n, n->pos - 1));
@@ -881,7 +903,7 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout)
}
#ifdef IP_SET_HASH_WITH_NETS
static inline int
static int
type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
{
struct ip_set_hash *h = set->data;
@@ -907,7 +929,7 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
#endif
static int
type_pf_ttest(struct ip_set *set, void *value, u32 timeout)
type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct ip_set_hash *h = set->data;
struct htable *t = h->table;
@@ -946,21 +968,21 @@ type_pf_tlist(const struct ip_set *set,
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
return -EMSGSIZE;
for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) {
incomplete = skb_tail_pointer(skb);
n = hbucket(t, cb->args[2]);
for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i);
pr_debug("list %p %u", n, i);
pr_debug("list %p %u\n", n, i);
if (type_pf_data_expired(data))
continue;
pr_debug("do list %p %u", n, i);
pr_debug("do list %p %u\n", n, i);
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (cb->args[2] == first) {
nla_nest_cancel(skb, atd);
return -EFAULT;
return -EMSGSIZE;
} else
goto nla_put_failure;
}
@@ -982,6 +1004,7 @@ nla_put_failure:
pr_warning("Can't list set %s: one bucket does not fit into "
"a message. Please report it!\n", set->name);
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
@@ -1008,7 +1031,7 @@ type_pf_gc(unsigned long ul_set)
struct ip_set *set = (struct ip_set *) ul_set;
struct ip_set_hash *h = set->data;
pr_debug("called");
pr_debug("called\n");
write_lock_bh(&set->lock);
type_pf_expire(h);
write_unlock_bh(&set->lock);
@@ -1017,7 +1040,7 @@ type_pf_gc(unsigned long ul_set)
add_timer(&h->gc);
}
static inline void
static void
type_pf_gc_init(struct ip_set *set)
{
struct ip_set_hash *h = set->data;
@@ -1027,7 +1050,7 @@ type_pf_gc_init(struct ip_set *set)
h->gc.function = type_pf_gc;
h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
add_timer(&h->gc);
pr_debug("gc initialized, run in every %u",
pr_debug("gc initialized, run in every %u\n",
IPSET_GC_PERIOD(h->timeout));
}

View File

@@ -1,6 +1,6 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -9,12 +9,10 @@
/* Kernel module implementing an IP set type: the bitmap:ip type */
#include "ip_set_kernel.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/netlink.h>
@@ -23,6 +21,7 @@
#include <net/netlink.h>
#include <net/tcp.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_bitmap.h"
#define IP_SET_BITMAP_TIMEOUT
@@ -33,8 +32,7 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("bitmap:ip type of IP sets");
MODULE_ALIAS("ip_set_bitmap:ip");
/* Base variant */
/* Type structure */
struct bitmap_ip {
void *members; /* the set members */
u32 first_ip; /* host byte order, included in range */
@@ -43,32 +41,45 @@ struct bitmap_ip {
u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */
u8 netmask; /* subnet netmask */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
/* Base variant */
static inline u32
ip_to_id(const struct bitmap_ip *map, u32 ip)
ip_to_id(const struct bitmap_ip *m, u32 ip)
{
return ((ip & HOSTMASK(map->netmask)) - map->first_ip)/map->hosts;
return ((ip & ip_set_hostmask(m->netmask)) - m->first_ip)/m->hosts;
}
static inline int
bitmap_ip_test(const struct bitmap_ip *map, u32 id)
static int
bitmap_ip_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
const struct bitmap_ip *map = set->data;
u16 id = *(u16 *)value;
return !!test_bit(id, map->members);
}
static inline int
bitmap_ip_add(struct bitmap_ip *map, u32 id)
static int
bitmap_ip_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ip *map = set->data;
u16 id = *(u16 *)value;
if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
}
static inline int
bitmap_ip_del(struct bitmap_ip *map, u32 id)
static int
bitmap_ip_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ip *map = set->data;
u16 id = *(u16 *)value;
if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST;
@@ -76,75 +87,193 @@ bitmap_ip_del(struct bitmap_ip *map, u32 id)
}
static int
bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
bitmap_ip_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ip *map = set->data;
struct nlattr *atd, *nested;
u32 id, first = cb->args[2];
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EMSGSIZE;
for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2];
if (!test_bit(id, map->members))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, atd);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
/* Timeout variant */
static int
bitmap_ip_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
const struct bitmap_ip *map = set->data;
const unsigned long *members = map->members;
u16 id = *(u16 *)value;
return ip_set_timeout_test(members[id]);
}
static int
bitmap_ip_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ip *map = set->data;
unsigned long *members = map->members;
u16 id = *(u16 *)value;
if (ip_set_timeout_test(members[id]) && !(flags & IPSET_FLAG_EXIST))
return -IPSET_ERR_EXIST;
members[id] = ip_set_timeout_set(timeout);
return 0;
}
static int
bitmap_ip_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ip *map = set->data;
unsigned long *members = map->members;
u16 id = *(u16 *)value;
int ret = -IPSET_ERR_EXIST;
if (ip_set_timeout_test(members[id]))
ret = 0;
members[id] = IPSET_ELEM_UNSET;
return ret;
}
static int
bitmap_ip_tlist(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ip *map = set->data;
struct nlattr *adt, *nested;
u32 id, first = cb->args[2];
const unsigned long *members = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!adt)
return -EMSGSIZE;
for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2];
if (!ip_set_timeout_test(members[id]))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, adt);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(members[id])));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, adt);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
static int
bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct bitmap_ip *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip;
ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
ip = ip_to_id(map, ip);
switch (adt) {
case IPSET_TEST:
return bitmap_ip_test(map, ip);
case IPSET_ADD:
return bitmap_ip_add(map, ip);
case IPSET_DEL:
return bitmap_ip_del(map, ip);
default:
return -EINVAL;
}
return adtfn(set, &ip, opt_timeout(opt, map), opt->cmdflags);
}
static const struct nla_policy bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct bitmap_ip *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
u32 timeout = map->timeout;
u32 ip, ip_to, id;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_ip_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
ip = ntohl(ip);
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
/* Set was defined without timeout support:
* don't ignore the attribute silently */
if (tb[IPSET_ATTR_TIMEOUT])
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(map->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST)
return bitmap_ip_test(map, ip_to_id(map, ip));
if (adt == IPSET_TEST) {
id = ip_to_id(map, ip);
return adtfn(set, &id, timeout, flags);
}
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to) {
swap(ip, ip_to);
if (ip < map->first_ip)
@@ -155,8 +284,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
ip_set_mask_from_to(ip, ip_to, cidr);
} else
ip_to = ip;
@@ -165,8 +293,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
for (; !before(ip_to, ip); ip += map->hosts) {
id = ip_to_id(map, ip);
ret = adt == IPSET_ADD ? bitmap_ip_add(map, id)
: bitmap_ip_del(map, id);
ret = adtfn(set, &id, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@@ -181,6 +308,9 @@ bitmap_ip_destroy(struct ip_set *set)
{
struct bitmap_ip *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
@@ -208,53 +338,16 @@ bitmap_ip_head(struct ip_set *set, struct sk_buff *skb)
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
if (map->netmask != 32)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask);
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->memsize));
if (with_timeout(map->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EFAULT;
}
static int
bitmap_ip_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ip *map = set->data;
struct nlattr *atd, *nested;
u32 id, first = cb->args[2];
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2];
if (!bitmap_ip_test(map, id))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EFAULT;
} else
goto nla_put_failure;
}
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, atd);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
return 0;
return -EMSGSIZE;
}
static bool
@@ -263,14 +356,20 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
const struct bitmap_ip *x = a->data;
const struct bitmap_ip *y = b->data;
return x->first_ip == y->first_ip
&& x->last_ip == y->last_ip
&& x->netmask == y->netmask;
return x->first_ip == y->first_ip &&
x->last_ip == y->last_ip &&
x->netmask == y->netmask &&
x->timeout == y->timeout;
}
static const struct ip_set_type_variant bitmap_ip = {
.kadt = bitmap_ip_kadt,
.uadt = bitmap_ip_uadt,
.adt = {
[IPSET_ADD] = bitmap_ip_add,
[IPSET_DEL] = bitmap_ip_del,
[IPSET_TEST] = bitmap_ip_test,
},
.destroy = bitmap_ip_destroy,
.flush = bitmap_ip_flush,
.head = bitmap_ip_head,
@@ -278,259 +377,26 @@ static const struct ip_set_type_variant bitmap_ip = {
.same_set = bitmap_ip_same_set,
};
/* Timeout variant */
struct bitmap_ip_timeout {
unsigned long *members; /* the set members */
u32 first_ip; /* host byte order, included in range */
u32 last_ip; /* host byte order, included in range */
u32 elements; /* number of max elements in the set */
u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */
u8 netmask; /* subnet netmask */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
static inline bool
bitmap_ip_timeout_test(const struct bitmap_ip_timeout *map, u32 id)
{
return ip_set_timeout_test(map->members[id]);
}
static inline int
bitmap_ip_timeout_add(struct bitmap_ip_timeout *map,
u32 id, u32 timeout)
{
if (bitmap_ip_timeout_test(map, id))
return -IPSET_ERR_EXIST;
map->members[id] = ip_set_timeout_set(timeout);
return 0;
}
static inline int
bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, u32 id)
{
int ret = -IPSET_ERR_EXIST;
if (bitmap_ip_timeout_test(map, id))
ret = 0;
map->members[id] = IPSET_ELEM_UNSET;
return ret;
}
static int
bitmap_ip_timeout_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_ip_timeout *map = set->data;
u32 ip;
ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
ip = ip_to_id((const struct bitmap_ip *)map, ip);
switch (adt) {
case IPSET_TEST:
return bitmap_ip_timeout_test(map, ip);
case IPSET_ADD:
return bitmap_ip_timeout_add(map, ip, map->timeout);
case IPSET_DEL:
return bitmap_ip_timeout_del(map, ip);
default:
return -EINVAL;
}
}
static int
bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct bitmap_ip_timeout *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
u32 ip, ip_to, id, timeout = map->timeout;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_ip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip);
if (ret)
return ret;
ip = ntohl(ip);
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
if (adt == IPSET_TEST)
return bitmap_ip_timeout_test(map,
ip_to_id((const struct bitmap_ip *)map, ip));
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to) {
swap(ip, ip_to);
if (ip < map->first_ip)
return -IPSET_ERR_BITMAP_RANGE;
}
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
if (ip_to > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT])
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
for (; !before(ip_to, ip); ip += map->hosts) {
id = ip_to_id((const struct bitmap_ip *)map, ip);
ret = adt == IPSET_ADD
? bitmap_ip_timeout_add(map, id, timeout)
: bitmap_ip_timeout_del(map, id);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static void
bitmap_ip_timeout_destroy(struct ip_set *set)
{
struct bitmap_ip_timeout *map = set->data;
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_ip_timeout_flush(struct ip_set *set)
{
struct bitmap_ip_timeout *map = set->data;
memset(map->members, IPSET_ELEM_UNSET, map->memsize);
}
static int
bitmap_ip_timeout_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ip_timeout *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip));
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
if (map->netmask != 32)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->memsize));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EFAULT;
}
static int
bitmap_ip_timeout_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ip_timeout *map = set->data;
struct nlattr *adt, *nested;
u32 id, first = cb->args[2];
const unsigned long *table = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!adt)
return -EFAULT;
for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2];
if (!bitmap_ip_timeout_test(map, id))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, adt);
return -EFAULT;
} else
goto nla_put_failure;
}
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(table[id])));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, adt);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
return 0;
}
static bool
bitmap_ip_timeout_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct bitmap_ip_timeout *x = a->data;
const struct bitmap_ip_timeout *y = b->data;
return x->first_ip == y->first_ip
&& x->last_ip == y->last_ip
&& x->netmask == y->netmask
&& x->timeout == y->timeout;
}
static const struct ip_set_type_variant bitmap_ip_timeout = {
.kadt = bitmap_ip_timeout_kadt,
.uadt = bitmap_ip_timeout_uadt,
.destroy = bitmap_ip_timeout_destroy,
.flush = bitmap_ip_timeout_flush,
.head = bitmap_ip_timeout_head,
.list = bitmap_ip_timeout_list,
.same_set = bitmap_ip_timeout_same_set,
static const struct ip_set_type_variant bitmap_tip = {
.kadt = bitmap_ip_kadt,
.uadt = bitmap_ip_uadt,
.adt = {
[IPSET_ADD] = bitmap_ip_tadd,
[IPSET_DEL] = bitmap_ip_tdel,
[IPSET_TEST] = bitmap_ip_ttest,
},
.destroy = bitmap_ip_destroy,
.flush = bitmap_ip_flush,
.head = bitmap_ip_head,
.list = bitmap_ip_tlist,
.same_set = bitmap_ip_same_set,
};
static void
bitmap_ip_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_ip_timeout *map = set->data;
struct bitmap_ip *map = set->data;
unsigned long *table = map->members;
u32 id;
@@ -546,10 +412,10 @@ bitmap_ip_gc(unsigned long ul_set)
add_timer(&map->gc);
}
static inline void
static void
bitmap_ip_gc_init(struct ip_set *set)
{
struct bitmap_ip_timeout *map = set->data;
struct bitmap_ip *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
@@ -560,21 +426,12 @@ bitmap_ip_gc_init(struct ip_set *set)
/* Create bitmap:ip type of sets */
static const struct nla_policy
bitmap_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static bool
init_map_ip(struct ip_set *set, struct bitmap_ip *map,
u32 first_ip, u32 last_ip,
u32 elements, u32 hosts, u8 netmask)
{
map->members = ip_set_alloc(map->memsize, GFP_KERNEL);
map->members = ip_set_alloc(map->memsize);
if (!map->members)
return false;
map->first_ip = first_ip;
@@ -582,6 +439,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
map->elements = elements;
map->hosts = hosts;
map->netmask = netmask;
map->timeout = IPSET_NO_TIMEOUT;
set->data = map;
set->family = AF_INET;
@@ -590,28 +448,25 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
}
static int
bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
u32 flags)
bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct bitmap_ip *map;
u32 first_ip, last_ip, hosts, elements;
u8 netmask = 32;
int ret;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_ip_create_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &first_ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip);
if (ret)
return ret;
first_ip = ntohl(first_ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &last_ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &last_ip);
if (ret)
return ret;
last_ip = htonl(last_ip);
if (first_ip > last_ip) {
u32 tmp = first_ip;
@@ -623,7 +478,7 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR;
last_ip = first_ip | ~HOSTMASK(cidr);
ip_set_mask_from_to(first_ip, last_ip, cidr);
} else
return -IPSET_ERR_PROTOCOL;
@@ -633,8 +488,8 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
if (netmask > 32)
return -IPSET_ERR_INVALID_NETMASK;
first_ip &= HOSTMASK(netmask);
last_ip |= ~HOSTMASK(netmask);
first_ip &= ip_set_hostmask(netmask);
last_ip |= ~ip_set_hostmask(netmask);
}
if (netmask == 32) {
@@ -646,50 +501,40 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
mask = range_to_mask(first_ip, last_ip, &mask_bits);
if ((!mask && (first_ip || last_ip != 0xFFFFFFFF))
|| netmask <= mask_bits)
if ((!mask && (first_ip || last_ip != 0xFFFFFFFF)) ||
netmask <= mask_bits)
return -IPSET_ERR_BITMAP_RANGE;
pr_debug("mask_bits %u, netmask %u", mask_bits, netmask);
pr_debug("mask_bits %u, netmask %u\n", mask_bits, netmask);
hosts = 2 << (32 - netmask - 1);
elements = 2 << (netmask - mask_bits - 1);
}
if (elements > IPSET_BITMAP_MAX_RANGE + 1)
return -IPSET_ERR_BITMAP_RANGE_SIZE;
pr_debug("hosts %u, elements %u", hosts, elements);
if (tb[IPSET_ATTR_TIMEOUT]) {
struct bitmap_ip_timeout *map;
pr_debug("hosts %u, elements %u\n", hosts, elements);
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->memsize = elements * sizeof(unsigned long);
if (!init_map_ip(set, (struct bitmap_ip *)map,
first_ip, last_ip,
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_ip_timeout;
set->variant = &bitmap_tip;
bitmap_ip_gc_init(set);
} else {
struct bitmap_ip *map;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map->memsize = bitmap_bytes(0, elements - 1);
if (!init_map_ip(set, map,
first_ip, last_ip,
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
@@ -706,8 +551,23 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_INET,
.revision = 0,
.revision_min = 0,
.revision_max = 0,
.create = bitmap_ip_create,
.create_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -1,7 +1,7 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -10,21 +10,18 @@
/* Kernel module implementing an IP set type: the bitmap:ip,mac type */
#include "ip_set_kernel.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <net/netlink.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_bitmap.h"
@@ -94,15 +91,15 @@ bitmap_expired(const struct bitmap_ipmac *map, u32 id)
static inline int
bitmap_ipmac_exist(const struct ipmac_telem *elem)
{
return elem->match == MAC_UNSET
|| (elem->match == MAC_FILLED
&& !ip_set_timeout_expired(elem->timeout));
return elem->match == MAC_UNSET ||
(elem->match == MAC_FILLED &&
!ip_set_timeout_expired(elem->timeout));
}
/* Base variant */
static int
bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout)
bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
@@ -113,14 +110,14 @@ bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout)
/* Trigger kernel to fill out the ethernet address */
return -EAGAIN;
case MAC_FILLED:
return data->ether == NULL
|| compare_ether_addr(data->ether, elem->ether) == 0;
return data->ether == NULL ||
compare_ether_addr(data->ether, elem->ether) == 0;
}
return 0;
}
static int
bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout)
bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
@@ -149,7 +146,7 @@ bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout)
}
static int
bitmap_ipmac_del(struct ip_set *set, void *value, u32 timeout)
bitmap_ipmac_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
@@ -175,7 +172,7 @@ bitmap_ipmac_list(const struct ip_set *set,
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
elem = bitmap_ipmac_elem(map, id);
@@ -185,7 +182,7 @@ bitmap_ipmac_list(const struct ip_set *set,
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EFAULT;
return -EMSGSIZE;
} else
goto nla_put_failure;
}
@@ -205,13 +202,17 @@ bitmap_ipmac_list(const struct ip_set *set,
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
/* Timeout variant */
static int
bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout)
bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
@@ -222,23 +223,24 @@ bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout)
/* Trigger kernel to fill out the ethernet address */
return -EAGAIN;
case MAC_FILLED:
return (data->ether == NULL
|| compare_ether_addr(data->ether, elem->ether) == 0)
&& !bitmap_expired(map, data->id);
return (data->ether == NULL ||
compare_ether_addr(data->ether, elem->ether) == 0) &&
!bitmap_expired(map, data->id);
}
return 0;
}
static int
bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout)
bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
bool flag_exist = flags & IPSET_FLAG_EXIST;
switch (elem->match) {
case MAC_UNSET:
if (!data->ether)
if (!(data->ether || flag_exist))
/* Already added without ethernet address */
return -IPSET_ERR_EXIST;
/* Fill the MAC address and activate the timer */
@@ -250,7 +252,7 @@ bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout)
elem->timeout = ip_set_timeout_set(timeout);
break;
case MAC_FILLED:
if (!bitmap_expired(map, data->id))
if (!(bitmap_expired(map, data->id) || flag_exist))
return -IPSET_ERR_EXIST;
/* Fall through */
case MAC_EMPTY:
@@ -272,7 +274,7 @@ bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout)
}
static int
bitmap_ipmac_tdel(struct ip_set *set, void *value, u32 timeout)
bitmap_ipmac_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
@@ -298,7 +300,7 @@ bitmap_ipmac_tlist(const struct ip_set *set,
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
elem = bitmap_ipmac_elem(map, id);
@@ -308,7 +310,7 @@ bitmap_ipmac_tlist(const struct ip_set *set,
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EFAULT;
return -EMSGSIZE;
} else
goto nla_put_failure;
}
@@ -331,62 +333,57 @@ bitmap_ipmac_tlist(const struct ip_set *set,
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
return 0;
return -EMSGSIZE;
}
static int
bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data;
data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
/* MAC can be src only */
if (!(opt->flags & IPSET_DIM_TWO_SRC))
return 0;
data.id = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
/* Backward compatibility: we don't check the second flag */
if (skb_mac_header(skb) < skb->head
|| (skb_mac_header(skb) + ETH_HLEN) > skb->data)
if (skb_mac_header(skb) < skb->head ||
(skb_mac_header(skb) + ETH_HLEN) > skb->data)
return -EINVAL;
data.id -= map->first_ip;
data.ether = eth_hdr(skb)->h_source;
return adtfn(set, &data, map->timeout);
return adtfn(set, &data, opt_timeout(opt, map), opt->cmdflags);
}
static const struct nla_policy
bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_ETHER] = { .type = NLA_BINARY, .len = ETH_ALEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct bitmap_ipmac *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data;
u32 timeout = map->timeout;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_ipmac_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.id);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &data.id);
if (ret)
return ret;
data.id = ntohl(data.id);
if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
@@ -404,7 +401,7 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
data.id -= map->first_ip;
ret = adtfn(set, &data, timeout);
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
@@ -443,8 +440,7 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb)
goto nla_put_failure;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip));
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map)
+ (map->last_ip - map->first_ip + 1) * map->dsize));
@@ -454,7 +450,7 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb)
return 0;
nla_put_failure:
return -EFAULT;
return -EMSGSIZE;
}
static bool
@@ -463,12 +459,12 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
const struct bitmap_ipmac *x = a->data;
const struct bitmap_ipmac *y = b->data;
return x->first_ip == y->first_ip
&& x->last_ip == y->last_ip
&& x->timeout == y->timeout;
return x->first_ip == y->first_ip &&
x->last_ip == y->last_ip &&
x->timeout == y->timeout;
}
const struct ip_set_type_variant bitmap_ipmac = {
static const struct ip_set_type_variant bitmap_ipmac = {
.kadt = bitmap_ipmac_kadt,
.uadt = bitmap_ipmac_uadt,
.adt = {
@@ -483,7 +479,7 @@ const struct ip_set_type_variant bitmap_ipmac = {
.same_set = bitmap_ipmac_same_set,
};
const struct ip_set_type_variant bitmap_tipmac = {
static const struct ip_set_type_variant bitmap_tipmac = {
.kadt = bitmap_ipmac_kadt,
.uadt = bitmap_ipmac_uadt,
.adt = {
@@ -511,8 +507,8 @@ bitmap_ipmac_gc(unsigned long ul_set)
read_lock_bh(&set->lock);
for (id = 0; id <= last; id++) {
elem = bitmap_ipmac_elem(map, id);
if (elem->match == MAC_FILLED
&& ip_set_timeout_expired(elem->timeout))
if (elem->match == MAC_FILLED &&
ip_set_timeout_expired(elem->timeout))
elem->match = MAC_EMPTY;
}
read_unlock_bh(&set->lock);
@@ -521,7 +517,7 @@ bitmap_ipmac_gc(unsigned long ul_set)
add_timer(&map->gc);
}
static inline void
static void
bitmap_ipmac_gc_init(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
@@ -535,19 +531,11 @@ bitmap_ipmac_gc_init(struct ip_set *set)
/* Create bitmap:ip,mac type of sets */
static const struct nla_policy
bitmap_ipmac_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static bool
init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
u32 first_ip, u32 last_ip)
{
map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize,
GFP_KERNEL);
map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize);
if (!map->members)
return false;
map->first_ip = first_ip;
@@ -561,28 +549,25 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
}
static int
bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 first_ip, last_ip, elements;
struct bitmap_ipmac *map;
int ret;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_ipmac_create_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &first_ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip);
if (ret)
return ret;
first_ip = ntohl(first_ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &last_ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &last_ip);
if (ret)
return ret;
last_ip = ntohl(last_ip);
if (first_ip > last_ip) {
u32 tmp = first_ip;
@@ -594,7 +579,7 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR;
last_ip = first_ip | ~HOSTMASK(cidr);
ip_set_mask_from_to(first_ip, last_ip, cidr);
} else
return -IPSET_ERR_PROTOCOL;
@@ -633,14 +618,28 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
return 0;
}
struct ip_set_type bitmap_ipmac_type = {
static struct ip_set_type bitmap_ipmac_type = {
.name = "bitmap:ip,mac",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_MAC,
.dimension = IPSET_DIM_TWO,
.family = AF_INET,
.revision = 0,
.revision_min = 0,
.revision_max = 0,
.create = bitmap_ipmac_create,
.create_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_ETHER] = { .type = NLA_BINARY,
.len = ETH_ALEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -0,0 +1,517 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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.
*/
/* Kernel module implementing an IP set type: the bitmap:port type */
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/netlink.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <net/netlink.h>
#include "ip_set.h"
#include "ip_set_bitmap.h"
#include "ip_set_getport.h"
#define IP_SET_BITMAP_TIMEOUT
#include "ip_set_timeout.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("bitmap:port type of IP sets");
MODULE_ALIAS("ip_set_bitmap:port");
/* Type structure */
struct bitmap_port {
void *members; /* the set members */
u16 first_port; /* host byte order, included in range */
u16 last_port; /* host byte order, included in range */
size_t memsize; /* members size */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
/* Base variant */
static int
bitmap_port_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
const struct bitmap_port *map = set->data;
u16 id = *(u16 *)value;
return !!test_bit(id, map->members);
}
static int
bitmap_port_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_port *map = set->data;
u16 id = *(u16 *)value;
if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
}
static int
bitmap_port_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_port *map = set->data;
u16 id = *(u16 *)value;
if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
}
static int
bitmap_port_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_port *map = set->data;
struct nlattr *atd, *nested;
u16 id, first = cb->args[2];
u16 last = map->last_port - map->first_port;
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
if (!test_bit(id, map->members))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
NLA_PUT_NET16(skb, IPSET_ATTR_PORT,
htons(map->first_port + id));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, atd);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
/* Timeout variant */
static int
bitmap_port_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
const struct bitmap_port *map = set->data;
const unsigned long *members = map->members;
u16 id = *(u16 *)value;
return ip_set_timeout_test(members[id]);
}
static int
bitmap_port_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_port *map = set->data;
unsigned long *members = map->members;
u16 id = *(u16 *)value;
if (ip_set_timeout_test(members[id]) && !(flags & IPSET_FLAG_EXIST))
return -IPSET_ERR_EXIST;
members[id] = ip_set_timeout_set(timeout);
return 0;
}
static int
bitmap_port_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_port *map = set->data;
unsigned long *members = map->members;
u16 id = *(u16 *)value;
int ret = -IPSET_ERR_EXIST;
if (ip_set_timeout_test(members[id]))
ret = 0;
members[id] = IPSET_ELEM_UNSET;
return ret;
}
static int
bitmap_port_tlist(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_port *map = set->data;
struct nlattr *adt, *nested;
u16 id, first = cb->args[2];
u16 last = map->last_port - map->first_port;
const unsigned long *members = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!adt)
return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
if (!ip_set_timeout_test(members[id]))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, adt);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
NLA_PUT_NET16(skb, IPSET_ATTR_PORT,
htons(map->first_port + id));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(members[id])));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, adt);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
static int
bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct bitmap_port *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
__be16 __port;
u16 port = 0;
if (!ip_set_get_ip_port(skb, opt->family,
opt->flags & IPSET_DIM_ONE_SRC, &__port))
return -EINVAL;
port = ntohs(__port);
if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
port -= map->first_port;
return adtfn(set, &port, opt_timeout(opt, map), opt->cmdflags);
}
static int
bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct bitmap_port *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 timeout = map->timeout;
u32 port; /* wraparound */
u16 id, port_to;
int ret = 0;
if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(map->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST) {
id = port - map->first_port;
return adtfn(set, &id, timeout, flags);
}
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to) {
swap(port, port_to);
if (port < map->first_port)
return -IPSET_ERR_BITMAP_RANGE;
}
} else
port_to = port;
if (port_to > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
for (; port <= port_to; port++) {
id = port - map->first_port;
ret = adtfn(set, &id, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static void
bitmap_port_destroy(struct ip_set *set)
{
struct bitmap_port *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_port_flush(struct ip_set *set)
{
struct bitmap_port *map = set->data;
memset(map->members, 0, map->memsize);
}
static int
bitmap_port_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_port *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port));
NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->memsize));
if (with_timeout(map->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EMSGSIZE;
}
static bool
bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct bitmap_port *x = a->data;
const struct bitmap_port *y = b->data;
return x->first_port == y->first_port &&
x->last_port == y->last_port &&
x->timeout == y->timeout;
}
static const struct ip_set_type_variant bitmap_port = {
.kadt = bitmap_port_kadt,
.uadt = bitmap_port_uadt,
.adt = {
[IPSET_ADD] = bitmap_port_add,
[IPSET_DEL] = bitmap_port_del,
[IPSET_TEST] = bitmap_port_test,
},
.destroy = bitmap_port_destroy,
.flush = bitmap_port_flush,
.head = bitmap_port_head,
.list = bitmap_port_list,
.same_set = bitmap_port_same_set,
};
static const struct ip_set_type_variant bitmap_tport = {
.kadt = bitmap_port_kadt,
.uadt = bitmap_port_uadt,
.adt = {
[IPSET_ADD] = bitmap_port_tadd,
[IPSET_DEL] = bitmap_port_tdel,
[IPSET_TEST] = bitmap_port_ttest,
},
.destroy = bitmap_port_destroy,
.flush = bitmap_port_flush,
.head = bitmap_port_head,
.list = bitmap_port_tlist,
.same_set = bitmap_port_same_set,
};
static void
bitmap_port_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_port *map = set->data;
unsigned long *table = map->members;
u32 id; /* wraparound */
u16 last = map->last_port - map->first_port;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (id = 0; id <= last; id++)
if (ip_set_timeout_expired(table[id]))
table[id] = IPSET_ELEM_UNSET;
read_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static void
bitmap_port_gc_init(struct ip_set *set)
{
struct bitmap_port *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = bitmap_port_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
/* Create bitmap:ip type of sets */
static bool
init_map_port(struct ip_set *set, struct bitmap_port *map,
u16 first_port, u16 last_port)
{
map->members = ip_set_alloc(map->memsize);
if (!map->members)
return false;
map->first_port = first_port;
map->last_port = last_port;
map->timeout = IPSET_NO_TIMEOUT;
set->data = map;
set->family = AF_UNSPEC;
return true;
}
static int
bitmap_port_create(struct ip_set *set, struct nlattr *tb[],
u32 flags)
{
struct bitmap_port *map;
u16 first_port, last_port;
if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
first_port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
last_port = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (first_port > last_port) {
u16 tmp = first_port;
first_port = last_port;
last_port = tmp;
}
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->memsize = (last_port - first_port + 1)
* sizeof(unsigned long);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_tport;
bitmap_port_gc_init(set);
} else {
map->memsize = bitmap_bytes(0, last_port - first_port);
pr_debug("memsize: %zu\n", map->memsize);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
set->variant = &bitmap_port;
}
return 0;
}
static struct ip_set_type bitmap_port_type = {
.name = "bitmap:port",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_PORT,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision_min = 0,
.revision_max = 0,
.create = bitmap_port_create,
.create_policy = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};
static int __init
bitmap_port_init(void)
{
return ip_set_type_register(&bitmap_port_type);
}
static void __exit
bitmap_port_fini(void)
{
ip_set_type_unregister(&bitmap_port_type);
}
module_init(bitmap_port_init);
module_exit(bitmap_port_fini);

View File

@@ -0,0 +1,155 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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.
*/
/* Get Layer-4 data from the packets */
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/sctp.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include "ip_set_getport.h"
/* We must handle non-linear skbs */
static bool
get_port(const struct sk_buff *skb, int protocol, unsigned int protooff,
bool src, __be16 *port, u8 *proto)
{
switch (protocol) {
case IPPROTO_TCP: {
struct tcphdr _tcph;
const struct tcphdr *th;
th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph);
if (th == NULL)
/* No choice either */
return false;
*port = src ? th->source : th->dest;
break;
}
case IPPROTO_SCTP: {
sctp_sctphdr_t _sh;
const sctp_sctphdr_t *sh;
sh = skb_header_pointer(skb, protooff, sizeof(_sh), &_sh);
if (sh == NULL)
/* No choice either */
return false;
*port = src ? sh->source : sh->dest;
break;
}
case IPPROTO_UDP:
case IPPROTO_UDPLITE: {
struct udphdr _udph;
const struct udphdr *uh;
uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph);
if (uh == NULL)
/* No choice either */
return false;
*port = src ? uh->source : uh->dest;
break;
}
case IPPROTO_ICMP: {
struct icmphdr _ich;
const struct icmphdr *ic;
ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich);
if (ic == NULL)
return false;
*port = (__force __be16)htons((ic->type << 8) | ic->code);
break;
}
case IPPROTO_ICMPV6: {
struct icmp6hdr _ich;
const struct icmp6hdr *ic;
ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich);
if (ic == NULL)
return false;
*port = (__force __be16)
htons((ic->icmp6_type << 8) | ic->icmp6_code);
break;
}
default:
break;
}
*proto = protocol;
return true;
}
bool
ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
__be16 *port, u8 *proto)
{
const struct iphdr *iph = ip_hdr(skb);
unsigned int protooff = ip_hdrlen(skb);
int protocol = iph->protocol;
/* See comments at tcp_match in ip_tables.c */
if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET))
return false;
return get_port(skb, protocol, protooff, src, port, proto);
}
EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
bool
ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
__be16 *port, u8 *proto)
{
int protoff;
u8 nexthdr;
nexthdr = ipv6_hdr(skb)->nexthdr;
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr);
if (protoff < 0)
return false;
return get_port(skb, nexthdr, protoff, src, port, proto);
}
EXPORT_SYMBOL_GPL(ip_set_get_ip6_port);
#endif
bool
ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
{
bool ret;
u8 proto;
switch (pf) {
case AF_INET:
ret = ip_set_get_ip4_port(skb, src, port, &proto);
break;
case AF_INET6:
ret = ip_set_get_ip6_port(skb, src, port, &proto);
break;
default:
return false;
}
if (!ret)
return ret;
switch (proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
return true;
default:
return false;
}
}
EXPORT_SYMBOL_GPL(ip_set_get_ip_port);

View File

@@ -0,0 +1,33 @@
#ifndef _IP_SET_GETPORT_H
#define _IP_SET_GETPORT_H
extern bool ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
__be16 *port, u8 *proto);
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
extern bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
__be16 *port, u8 *proto);
#else
static inline bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
__be16 *port, u8 *proto)
{
return false;
}
#endif
extern bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src,
__be16 *port);
static inline bool ip_set_proto_with_ports(u8 proto)
{
switch (proto) {
case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
return true;
}
return false;
}
#endif /*_IP_SET_GETPORT_H*/

View File

@@ -11,6 +11,10 @@ enum {
IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO,
/* Range not supported */
IPSET_ERR_HASH_RANGE_UNSUPPORTED,
/* Invalid range */
IPSET_ERR_HASH_RANGE,
};
#ifdef __KERNEL__

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -7,15 +7,11 @@
/* Kernel module implementing an IP set type: the hash:ip type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
@@ -23,6 +19,7 @@
#include <net/tcp.h>
#include <linux/netfilter.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_hash.h"
@@ -45,12 +42,12 @@ hash_ip_same_set(const struct ip_set *a, const struct ip_set *b);
/* Member elements without timeout */
struct hash_ip4_elem {
u32 ip;
__be32 ip;
};
/* Member elements with timeout support */
struct hash_ip4_telem {
u32 ip;
__be32 ip;
unsigned long timeout;
};
@@ -73,12 +70,6 @@ hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src)
dst->ip = src->ip;
}
static inline void
hash_ip4_data_swap(struct hash_ip4_elem *dst, struct hash_ip4_elem *src)
{
swap(dst->ip, src->ip);
}
/* Zero valued IP addresses cannot be stored */
static inline void
hash_ip4_data_zero_out(struct hash_ip4_elem *elem)
@@ -96,7 +87,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data)
{
const struct hash_ip4_telem *tdata =
@@ -117,54 +108,51 @@ nla_put_failure:
#define HOST_MASK 32
#include "ip_set_ahash.h"
static inline void
hash_ip4_data_next(struct ip_set_hash *h, const struct hash_ip4_elem *d)
{
h->next.ip = ntohl(d->ip);
}
static int
hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip;
__be32 ip;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip);
ip &= NETMASK(h->netmask);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
ip &= ip_set_netmask(h->netmask);
if (ip == 0)
return -EINVAL;
return adtfn(set, &ip, h->timeout);
return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags);
}
static const struct nla_policy hash_ip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip, nip, ip_to, hosts, timeout = h->timeout;
u32 ip, ip_to, hosts, timeout = h->timeout;
__be32 nip;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip4_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
ip &= NETMASK(h->netmask);
if (ip == 0)
return -IPSET_ERR_HASH_ELEM;
ip &= ip_set_hostmask(h->netmask);
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -172,15 +160,17 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST)
return adtfn(set, &ip, timeout);
if (adt == IPSET_TEST) {
nip = htonl(ip);
if (nip == 0)
return -IPSET_ERR_HASH_ELEM;
return adtfn(set, &nip, timeout, flags);
}
ip = ntohl(ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to)
swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) {
@@ -188,16 +178,19 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
ip_set_mask_from_to(ip, ip_to, cidr);
} else
ip_to = ip;
hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
if (retried)
ip = h->next.ip;
for (; !before(ip_to, ip); ip += hosts) {
nip = htonl(ip);
ret = adtfn(set, &nip, timeout);
if (nip == 0)
return -IPSET_ERR_HASH_ELEM;
ret = adtfn(set, &nip, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@@ -214,9 +207,9 @@ hash_ip_same_set(const struct ip_set *a, const struct ip_set *b)
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout
&& x->netmask == y->netmask;
return x->maxelem == y->maxelem &&
x->timeout == y->timeout &&
x->netmask == y->netmask;
}
/* The type variant functions: IPv6 */
@@ -249,16 +242,6 @@ hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src)
ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
}
static inline void
hash_ip6_data_swap(struct hash_ip6_elem *dst, struct hash_ip6_elem *src)
{
struct in6_addr tmp;
ipv6_addr_copy(&tmp, &dst->ip.in6);
ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
ipv6_addr_copy(&src->ip.in6, &tmp);
}
static inline void
hash_ip6_data_zero_out(struct hash_ip6_elem *elem)
{
@@ -268,13 +251,13 @@ hash_ip6_data_zero_out(struct hash_ip6_elem *elem)
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= NETMASK6(prefix)[0];
ip->ip6[1] &= NETMASK6(prefix)[1];
ip->ip6[2] &= NETMASK6(prefix)[2];
ip->ip6[3] &= NETMASK6(prefix)[3];
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
}
static inline bool
static bool
hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data)
{
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
@@ -284,7 +267,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data)
{
const struct hash_ip6_telem *e =
@@ -306,20 +289,26 @@ nla_put_failure:
#define HOST_MASK 128
#include "ip_set_ahash.h"
static inline void
hash_ip6_data_next(struct ip_set_hash *h, const struct hash_ip6_elem *d)
{
}
static int
hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip.in6);
ip6_netmask(&ip, h->netmask);
if (ipv6_addr_any(&ip.in6))
return -EINVAL;
return adtfn(set, &ip, h->timeout);
return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags);
}
static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
@@ -329,24 +318,25 @@ static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
};
static int
hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip6_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
@@ -360,27 +350,16 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
ret = adtfn(set, &ip, timeout);
ret = adtfn(set, &ip, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
static const struct nla_policy
hash_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
};
static int
hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 netmask, hbits;
struct ip_set_hash *h;
@@ -388,11 +367,12 @@ hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
netmask = set->family == AF_INET ? 32 : 128;
pr_debug("Create set %s with family %s",
pr_debug("Create set %s with family %s\n",
set->name, set->family == AF_INET ? "inet" : "inet6");
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ip_create_policy))
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
@@ -407,9 +387,9 @@ hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (tb[IPSET_ATTR_NETMASK]) {
netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
if ((set->family == AF_INET && netmask > 32)
|| (set->family == AF_INET6 && netmask > 128)
|| netmask == 0)
if ((set->family == AF_INET && netmask > 32) ||
(set->family == AF_INET6 && netmask > 128) ||
netmask == 0)
return -IPSET_ERR_INVALID_NETMASK;
}
@@ -425,8 +405,7 @@ hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
+ jhash_size(hbits) * sizeof(struct hbucket));
if (!h->table) {
kfree(h);
return -ENOMEM;
@@ -450,7 +429,7 @@ hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
? &hash_ip4_variant : &hash_ip6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
@@ -463,8 +442,24 @@ static struct ip_set_type hash_ip_type __read_mostly = {
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.revision_min = 0,
.revision_max = 0,
.create = hash_ip_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -7,15 +7,11 @@
/* Kernel module implementing an IP set type: the hash:ip,port type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
@@ -23,6 +19,7 @@
#include <net/tcp.h>
#include <linux/netfilter.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_getport.h"
@@ -46,16 +43,16 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b);
/* Member elements without timeout */
struct hash_ipport4_elem {
u32 ip;
u16 port;
__be32 ip;
__be16 port;
u8 proto;
u8 padding;
};
/* Member elements with timeout support */
struct hash_ipport4_telem {
u32 ip;
u16 port;
__be32 ip;
__be16 port;
u8 proto;
u8 padding;
unsigned long timeout;
@@ -65,9 +62,9 @@ static inline bool
hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
const struct hash_ipport4_elem *ip2)
{
return ip1->ip == ip2->ip
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
return ip1->ip == ip2->ip &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto;
}
static inline bool
@@ -85,22 +82,13 @@ hash_ipport4_data_copy(struct hash_ipport4_elem *dst,
dst->proto = src->proto;
}
static inline void
hash_ipport4_data_swap(struct hash_ipport4_elem *dst,
struct hash_ipport4_elem *src)
{
swap(dst->ip, src->ip);
swap(dst->port, src->port);
swap(dst->proto, src->proto);
}
static inline void
hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem)
{
elem->proto = 0;
}
static inline bool
static bool
hash_ipport4_data_list(struct sk_buff *skb,
const struct hash_ipport4_elem *data)
{
@@ -113,7 +101,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_ipport4_data_tlist(struct sk_buff *skb,
const struct hash_ipport4_elem *data)
{
@@ -136,80 +124,73 @@ nla_put_failure:
#define HOST_MASK 32
#include "ip_set_ahash.h"
static inline void
hash_ipport4_data_next(struct ip_set_hash *h,
const struct hash_ipport4_elem *d)
{
h->next.ip = ntohl(d->ip);
h->next.port = ntohs(d->port);
}
static int
hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static const struct nla_policy
hash_ipport_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
u32 ip, ip_to, p, port, port_to;
u32 ip, ip_to, p = 0, port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipport_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
if (!(with_ports || data.proto == IPPROTO_ICMP))
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -217,20 +198,18 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR]
|| tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout);
if (adt == IPSET_TEST ||
!(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to)
swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) {
@@ -238,30 +217,32 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
ip_set_mask_from_to(ip, ip_to, cidr);
} else
ip_to = ip;
port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = port = ntohs(data.port);
if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
} else
port_to = port;
}
for (; !before(ip_to, ip); ip++)
for (p = port; p <= port_to; p++) {
if (retried)
ip = h->next.ip;
for (; !before(ip_to, ip); ip++) {
p = retried && ip == h->next.ip ? h->next.port : port;
for (; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
}
return ret;
}
@@ -272,22 +253,22 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b)
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout;
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
struct hash_ipport6_elem {
union nf_inet_addr ip;
u16 port;
__be16 port;
u8 proto;
u8 padding;
};
struct hash_ipport6_telem {
union nf_inet_addr ip;
u16 port;
__be16 port;
u8 proto;
u8 padding;
unsigned long timeout;
@@ -297,9 +278,9 @@ static inline bool
hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
const struct hash_ipport6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto;
}
static inline bool
@@ -315,24 +296,13 @@ hash_ipport6_data_copy(struct hash_ipport6_elem *dst,
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipport6_data_swap(struct hash_ipport6_elem *dst,
struct hash_ipport6_elem *src)
{
struct hash_ipport6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem)
{
elem->proto = 0;
}
static inline bool
static bool
hash_ipport6_data_list(struct sk_buff *skb,
const struct hash_ipport6_elem *data)
{
@@ -345,7 +315,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_ipport6_data_tlist(struct sk_buff *skb,
const struct hash_ipport6_elem *data)
{
@@ -370,68 +340,74 @@ nla_put_failure:
#define HOST_MASK 128
#include "ip_set_ahash.h"
static inline void
hash_ipport6_data_next(struct ip_set_hash *h,
const struct hash_ipport6_elem *d)
{
h->next.port = ntohs(d->port);
}
static int
hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { };
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { };
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipport_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
if (!(with_ports || data.proto == IPPROTO_ICMPV6))
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -439,10 +415,8 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
@@ -451,9 +425,11 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port > port_to)
swap(port, port_to);
if (retried)
port = h->next.port;
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@@ -465,20 +441,9 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
/* Create hash:ip type of sets */
static const struct nla_policy
hash_ipport_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
@@ -486,8 +451,9 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipport_create_policy))
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
@@ -510,8 +476,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
+ jhash_size(hbits) * sizeof(struct hbucket));
if (!h->table) {
kfree(h);
return -ENOMEM;
@@ -535,7 +500,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
? &hash_ipport4_variant : &hash_ipport6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
@@ -548,8 +513,27 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC,
.revision = 0,
.revision_min = 0,
.revision_max = 1, /* SCTP and UDPLITE support added */
.create = hash_ipport_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -7,15 +7,11 @@
/* Kernel module implementing an IP set type: the hash:ip,port,ip type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
@@ -23,6 +19,7 @@
#include <net/tcp.h>
#include <linux/netfilter.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_getport.h"
@@ -46,18 +43,18 @@ hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b);
/* Member elements without timeout */
struct hash_ipportip4_elem {
u32 ip;
u32 ip2;
u16 port;
__be32 ip;
__be32 ip2;
__be16 port;
u8 proto;
u8 padding;
};
/* Member elements with timeout support */
struct hash_ipportip4_telem {
u32 ip;
u32 ip2;
u16 port;
__be32 ip;
__be32 ip2;
__be16 port;
u8 proto;
u8 padding;
unsigned long timeout;
@@ -67,10 +64,10 @@ static inline bool
hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
const struct hash_ipportip4_elem *ip2)
{
return ip1->ip == ip2->ip
&& ip1->ip2 == ip2->ip2
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
return ip1->ip == ip2->ip &&
ip1->ip2 == ip2->ip2 &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto;
}
static inline bool
@@ -86,24 +83,13 @@ hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst,
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportip4_data_swap(struct hash_ipportip4_elem *dst,
struct hash_ipportip4_elem *src)
{
struct hash_ipportip4_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem)
{
elem->proto = 0;
}
static inline bool
static bool
hash_ipportip4_data_list(struct sk_buff *skb,
const struct hash_ipportip4_elem *data)
{
@@ -117,7 +103,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_ipportip4_data_tlist(struct sk_buff *skb,
const struct hash_ipportip4_elem *data)
{
@@ -141,86 +127,78 @@ nla_put_failure:
#define HOST_MASK 32
#include "ip_set_ahash.h"
static inline void
hash_ipportip4_data_next(struct ip_set_hash *h,
const struct hash_ipportip4_elem *d)
{
h->next.ip = ntohl(d->ip);
h->next.port = ntohs(d->port);
}
static int
hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static const struct nla_policy
hash_ipportip_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
u32 ip, ip_to, p, port, port_to;
u32 ip, ip_to, p = 0, port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportip_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
if (ret)
return ret;
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP2, &data.ip2);
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
if (!(with_ports || data.proto == IPPROTO_ICMP))
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -228,20 +206,18 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR]
|| tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout);
if (adt == IPSET_TEST ||
!(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to)
swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) {
@@ -249,30 +225,32 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
ip_set_mask_from_to(ip, ip_to, cidr);
} else
ip_to = ip;
port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = port = ntohs(data.port);
if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
} else
port_to = port;
}
for (; !before(ip_to, ip); ip++)
for (p = port; p <= port_to; p++) {
if (retried)
ip = h->next.ip;
for (; !before(ip_to, ip); ip++) {
p = retried && ip == h->next.ip ? h->next.port : port;
for (; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
}
return ret;
}
@@ -283,8 +261,8 @@ hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b)
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout;
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
@@ -292,7 +270,7 @@ hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b)
struct hash_ipportip6_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
__be16 port;
u8 proto;
u8 padding;
};
@@ -300,7 +278,7 @@ struct hash_ipportip6_elem {
struct hash_ipportip6_telem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
__be16 port;
u8 proto;
u8 padding;
unsigned long timeout;
@@ -310,10 +288,10 @@ static inline bool
hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
const struct hash_ipportip6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto;
}
static inline bool
@@ -329,24 +307,13 @@ hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst,
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportip6_data_swap(struct hash_ipportip6_elem *dst,
struct hash_ipportip6_elem *src)
{
struct hash_ipportip6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem)
{
elem->proto = 0;
}
static inline bool
static bool
hash_ipportip6_data_list(struct sk_buff *skb,
const struct hash_ipportip6_elem *data)
{
@@ -360,7 +327,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_ipportip6_data_tlist(struct sk_buff *skb,
const struct hash_ipportip6_elem *data)
{
@@ -386,73 +353,79 @@ nla_put_failure:
#define HOST_MASK 128
#include "ip_set_ahash.h"
static inline void
hash_ipportip6_data_next(struct ip_set_hash *h,
const struct hash_ipportip6_elem *d)
{
h->next.port = ntohs(d->port);
}
static int
hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { };
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { };
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportip_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
if (ret)
return ret;
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP2, &data.ip2);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
if (!(with_ports || data.proto == IPPROTO_ICMPV6))
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -460,10 +433,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
@@ -472,9 +443,11 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port > port_to)
swap(port, port_to);
if (retried)
port = h->next.port;
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@@ -486,20 +459,9 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
/* Create hash:ip type of sets */
static const struct nla_policy
hash_ipportip_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_ipportip_create(struct ip_set *set, struct nlattr *head,
int len, u32 flags)
hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
@@ -507,8 +469,9 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head,
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipportip_create_policy))
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
@@ -531,8 +494,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head,
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
+ jhash_size(hbits) * sizeof(struct hbucket));
if (!h->table) {
kfree(h);
return -ENOMEM;
@@ -556,7 +518,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head,
? &hash_ipportip4_variant : &hash_ipportip6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
@@ -569,8 +531,27 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC,
.revision = 0,
.revision_min = 0,
.revision_max = 1, /* SCTP and UDPLITE support added */
.create = hash_ipportip_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -7,15 +7,11 @@
/* Kernel module implementing an IP set type: the hash:ip,port,net type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
@@ -23,6 +19,7 @@
#include <net/tcp.h>
#include <linux/netfilter.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_getport.h"
@@ -46,18 +43,18 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b);
/* Member elements without timeout */
struct hash_ipportnet4_elem {
u32 ip;
u32 ip2;
u16 port;
__be32 ip;
__be32 ip2;
__be16 port;
u8 cidr;
u8 proto;
};
/* Member elements with timeout support */
struct hash_ipportnet4_telem {
u32 ip;
u32 ip2;
u16 port;
__be32 ip;
__be32 ip2;
__be16 port;
u8 cidr;
u8 proto;
unsigned long timeout;
@@ -67,11 +64,11 @@ static inline bool
hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
const struct hash_ipportnet4_elem *ip2)
{
return ip1->ip == ip2->ip
&& ip1->ip2 == ip2->ip2
&& ip1->cidr == ip2->cidr
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
return ip1->ip == ip2->ip &&
ip1->ip2 == ip2->ip2 &&
ip1->cidr == ip2->cidr &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto;
}
static inline bool
@@ -87,21 +84,10 @@ hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportnet4_data_swap(struct hash_ipportnet4_elem *dst,
struct hash_ipportnet4_elem *src)
{
struct hash_ipportnet4_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
{
elem->ip2 &= NETMASK(cidr);
elem->ip2 &= ip_set_netmask(cidr);
elem->cidr = cidr;
}
@@ -111,7 +97,7 @@ hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem)
elem->proto = 0;
}
static inline bool
static bool
hash_ipportnet4_data_list(struct sk_buff *skb,
const struct hash_ipportnet4_elem *data)
{
@@ -126,7 +112,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_ipportnet4_data_tlist(struct sk_buff *skb,
const struct hash_ipportnet4_elem *data)
{
@@ -154,102 +140,94 @@ nla_put_failure:
#define HOST_MASK 32
#include "ip_set_ahash.h"
static inline void
hash_ipportnet4_data_next(struct ip_set_hash *h,
const struct hash_ipportnet4_elem *d)
{
h->next.ip = ntohl(d->ip);
h->next.port = ntohs(d->port);
h->next.ip2 = ntohl(d->ip2);
}
static int
hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data =
{ .cidr = h->nets[0].cidr || HOST_MASK };
struct hash_ipportnet4_elem data = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
data.ip2 &= NETMASK(data.cidr);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
data.ip2 &= ip_set_netmask(data.cidr);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static const struct nla_policy
hash_ipportnet_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
u32 ip, ip_to, p, port, port_to;
u32 ip, ip_to, p = 0, port, port_to;
u32 ip2_from = 0, ip2_to, ip2_last, ip2;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportnet_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP2, &data.ip2);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR2])
if (tb[IPSET_ATTR_CIDR2]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!data.cidr)
return -IPSET_ERR_INVALID_CIDR;
data.ip2 &= NETMASK(data.cidr);
}
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
if (!(with_ports || data.proto == IPPROTO_ICMP))
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -257,20 +235,20 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR]
|| tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout);
with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
if (adt == IPSET_TEST ||
!(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
tb[IPSET_ATTR_IP2_TO])) {
data.ip = htonl(ip);
data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to)
swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) {
@@ -278,29 +256,49 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
ip_set_mask_from_to(ip, ip_to, cidr);
}
port = ntohs(data.port);
port_to = port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
} else
port_to = port;
}
if (tb[IPSET_ATTR_IP2_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
if (ret)
return ret;
if (ip2_from > ip2_to)
swap(ip2_from, ip2_to);
if (ip2_from + UINT_MAX == ip2_to)
return -IPSET_ERR_HASH_RANGE;
} else {
ip_set_mask_from_to(ip2_from, ip2_to, data.cidr);
}
for (; !before(ip_to, ip); ip++)
for (p = port; p <= port_to; p++) {
if (retried)
ip = h->next.ip;
for (; !before(ip_to, ip); ip++) {
data.ip = htonl(ip);
p = retried && ip == h->next.ip ? h->next.port : port;
for (; p <= port_to; p++) {
data.port = htons(p);
ret = adtfn(set, &data, timeout);
ip2 = retried && ip == h->next.ip && p == h->next.port
? h->next.ip2 : ip2_from;
while (!after(ip2, ip2_to)) {
data.ip2 = htonl(ip2);
ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
&data.cidr);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
ip2 = ip2_last + 1;
}
}
}
return ret;
}
@@ -312,8 +310,8 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b)
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout;
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
@@ -321,7 +319,7 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b)
struct hash_ipportnet6_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
__be16 port;
u8 cidr;
u8 proto;
};
@@ -329,7 +327,7 @@ struct hash_ipportnet6_elem {
struct hash_ipportnet6_telem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
__be16 port;
u8 cidr;
u8 proto;
unsigned long timeout;
@@ -339,11 +337,11 @@ static inline bool
hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
const struct hash_ipportnet6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0
&& ip1->cidr == ip2->cidr
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 &&
ip1->cidr == ip2->cidr &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto;
}
static inline bool
@@ -359,17 +357,6 @@ hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportnet6_data_swap(struct hash_ipportnet6_elem *dst,
struct hash_ipportnet6_elem *src)
{
struct hash_ipportnet6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
{
@@ -379,10 +366,10 @@ hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= NETMASK6(prefix)[0];
ip->ip6[1] &= NETMASK6(prefix)[1];
ip->ip6[2] &= NETMASK6(prefix)[2];
ip->ip6[3] &= NETMASK6(prefix)[3];
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
}
static inline void
@@ -392,7 +379,7 @@ hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
elem->cidr = cidr;
}
static inline bool
static bool
hash_ipportnet6_data_list(struct sk_buff *skb,
const struct hash_ipportnet6_elem *data)
{
@@ -407,7 +394,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_ipportnet6_data_tlist(struct sk_buff *skb,
const struct hash_ipportnet6_elem *data)
{
@@ -434,55 +421,70 @@ nla_put_failure:
#define HOST_MASK 128
#include "ip_set_ahash.h"
static inline void
hash_ipportnet6_data_next(struct ip_set_hash *h,
const struct hash_ipportnet6_elem *d)
{
h->next.port = ntohs(d->port);
}
static int
hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data =
{ .cidr = h->nets[0].cidr || HOST_MASK };
struct hash_ipportnet6_elem data = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
ip6_netmask(&data.ip2, data.cidr);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportnet_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
if (ret)
return ret;
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP2, &data.ip2);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2);
if (ret)
return ret;
@@ -495,27 +497,21 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
ip6_netmask(&data.ip2, data.cidr);
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
if (!(with_ports || data.proto == IPPROTO_ICMPV6))
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -523,10 +519,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
@@ -535,9 +529,11 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port > port_to)
swap(port, port_to);
if (retried)
port = h->next.port;
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@@ -549,20 +545,9 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
/* Create hash:ip type of sets */
static const struct nla_policy
hash_ipportnet_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
int len, u32 flags)
hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
@@ -570,8 +555,9 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipportnet_create_policy))
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
@@ -596,8 +582,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
+ jhash_size(hbits) * sizeof(struct hbucket));
if (!h->table) {
kfree(h);
return -ENOMEM;
@@ -622,7 +607,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
? &hash_ipportnet4_variant : &hash_ipportnet6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
@@ -635,8 +620,30 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC,
.revision = 0,
.revision_min = 0,
/* 1 SCTP and UDPLITE support added */
.revision_max = 2, /* Range as input support for IPv4 added */
.create = hash_ipportnet_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -7,21 +7,18 @@
/* Kernel module implementing an IP set type: the hash:net type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <linux/netfilter.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_hash.h"
@@ -44,7 +41,7 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
/* Member elements without timeout */
struct hash_net4_elem {
u32 ip;
__be32 ip;
u16 padding0;
u8 padding1;
u8 cidr;
@@ -52,7 +49,7 @@ struct hash_net4_elem {
/* Member elements with timeout support */
struct hash_net4_telem {
u32 ip;
__be32 ip;
u16 padding0;
u8 padding1;
u8 cidr;
@@ -80,18 +77,10 @@ hash_net4_data_copy(struct hash_net4_elem *dst,
dst->cidr = src->cidr;
}
static inline void
hash_net4_data_swap(struct hash_net4_elem *dst,
struct hash_net4_elem *src)
{
swap(dst->ip, src->ip);
swap(dst->cidr, src->cidr);
}
static inline void
hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr)
{
elem->ip &= NETMASK(cidr);
elem->ip &= ip_set_netmask(cidr);
elem->cidr = cidr;
}
@@ -102,7 +91,7 @@ hash_net4_data_zero_out(struct hash_net4_elem *elem)
elem->cidr = 0;
}
static inline bool
static bool
hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
{
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
@@ -113,7 +102,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
{
const struct hash_net4_telem *tdata =
@@ -136,60 +125,62 @@ nla_put_failure:
#define HOST_MASK 32
#include "ip_set_ahash.h"
static inline void
hash_net4_data_next(struct ip_set_hash *h,
const struct hash_net4_elem *d)
{
h->next.ip = ntohl(d->ip);
}
static int
hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
struct hash_net4_elem data = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= NETMASK(data.cidr);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static const struct nla_policy hash_net_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
u32 ip = 0, ip_to, last;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_net_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR])
if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!data.cidr)
return -IPSET_ERR_INVALID_CIDR;
data.ip &= NETMASK(data.cidr);
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -197,9 +188,35 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
ret = adtfn(set, &data, timeout);
if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
data.ip = htonl(ip & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip_to = ip;
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
if (ip_to < ip)
swap(ip, ip_to);
if (ip + UINT_MAX == ip_to)
return -IPSET_ERR_HASH_RANGE;
}
if (retried)
ip = h->next.ip;
while (!after(ip, ip_to)) {
data.ip = htonl(ip);
last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
ip = last + 1;
}
return ret;
}
static bool
@@ -209,8 +226,8 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout;
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
@@ -234,8 +251,8 @@ static inline bool
hash_net6_data_equal(const struct hash_net6_elem *ip1,
const struct hash_net6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ip1->cidr == ip2->cidr;
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->cidr == ip2->cidr;
}
static inline bool
@@ -252,16 +269,6 @@ hash_net6_data_copy(struct hash_net6_elem *dst,
dst->cidr = src->cidr;
}
static inline void
hash_net6_data_swap(struct hash_net6_elem *dst, struct hash_net6_elem *src)
{
struct hash_net6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_net6_data_zero_out(struct hash_net6_elem *elem)
{
@@ -271,10 +278,10 @@ hash_net6_data_zero_out(struct hash_net6_elem *elem)
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= NETMASK6(prefix)[0];
ip->ip6[1] &= NETMASK6(prefix)[1];
ip->ip6[2] &= NETMASK6(prefix)[2];
ip->ip6[3] &= NETMASK6(prefix)[3];
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
}
static inline void
@@ -284,7 +291,7 @@ hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
elem->cidr = cidr;
}
static inline bool
static bool
hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data)
{
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
@@ -295,7 +302,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
{
const struct hash_net6_telem *e =
@@ -318,44 +325,54 @@ nla_put_failure:
#define HOST_MASK 128
#include "ip_set_ahash.h"
static inline void
hash_net6_data_next(struct ip_set_hash *h,
const struct hash_net6_elem *d)
{
}
static int
hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
struct hash_net6_elem data = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_net_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
if (ret)
return ret;
@@ -373,26 +390,16 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
ret = adtfn(set, &data, timeout);
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
static const struct nla_policy
hash_net_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
struct ip_set_hash *h;
u8 hbits;
@@ -400,8 +407,9 @@ hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_net_create_policy))
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
@@ -426,8 +434,7 @@ hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
+ jhash_size(hbits) * sizeof(struct hbucket));
if (!h->table) {
kfree(h);
return -ENOMEM;
@@ -451,7 +458,7 @@ hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
? &hash_net4_variant : &hash_net6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
@@ -464,8 +471,22 @@ static struct ip_set_type hash_net_type __read_mostly = {
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.revision_min = 0,
.revision_max = 1, /* Range as input support for IPv4 added */
.create = hash_net_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -0,0 +1,762 @@
/* Copyright (C) 2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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.
*/
/* Kernel module implementing an IP set type: the hash:net,iface type */
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <linux/rbtree.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <linux/netfilter.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_hash.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("hash:net,iface type of IP sets");
MODULE_ALIAS("ip_set_hash:net,iface");
/* Interface name rbtree */
struct iface_node {
struct rb_node node;
char iface[IFNAMSIZ];
};
#define iface_data(n) (rb_entry(n, struct iface_node, node)->iface)
static inline long
ifname_compare(const char *_a, const char *_b)
{
const long *a = (const long *)_a;
const long *b = (const long *)_b;
BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
if (a[0] != b[0])
return a[0] - b[0];
if (IFNAMSIZ > sizeof(long)) {
if (a[1] != b[1])
return a[1] - b[1];
}
if (IFNAMSIZ > 2 * sizeof(long)) {
if (a[2] != b[2])
return a[2] - b[2];
}
if (IFNAMSIZ > 3 * sizeof(long)) {
if (a[3] != b[3])
return a[3] - b[3];
}
return 0;
}
static void
rbtree_destroy(struct rb_root *root)
{
struct rb_node *p, *n = root->rb_node;
struct iface_node *node;
/* Non-recursive destroy, like in ext3 */
while (n) {
if (n->rb_left) {
n = n->rb_left;
continue;
}
if (n->rb_right) {
n = n->rb_right;
continue;
}
p = rb_parent(n);
node = rb_entry(n, struct iface_node, node);
if (!p)
*root = RB_ROOT;
else if (p->rb_left == n)
p->rb_left = NULL;
else if (p->rb_right == n)
p->rb_right = NULL;
kfree(node);
n = p;
}
}
static int
iface_test(struct rb_root *root, const char **iface)
{
struct rb_node *n = root->rb_node;
while (n) {
const char *d = iface_data(n);
int res = ifname_compare(*iface, d);
if (res < 0)
n = n->rb_left;
else if (res > 0)
n = n->rb_right;
else {
*iface = d;
return 1;
}
}
return 0;
}
static int
iface_add(struct rb_root *root, const char **iface)
{
struct rb_node **n = &(root->rb_node), *p = NULL;
struct iface_node *d;
while (*n) {
char *ifname = iface_data(*n);
int res = ifname_compare(*iface, ifname);
p = *n;
if (res < 0)
n = &((*n)->rb_left);
else if (res > 0)
n = &((*n)->rb_right);
else {
*iface = ifname;
return 0;
}
}
d = kzalloc(sizeof(*d), GFP_ATOMIC);
if (!d)
return -ENOMEM;
strcpy(d->iface, *iface);
rb_link_node(&d->node, p, n);
rb_insert_color(&d->node, root);
*iface = d->iface;
return 0;
}
/* Type specific function prefix */
#define TYPE hash_netiface
static bool
hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_netiface4_same_set hash_netiface_same_set
#define hash_netiface6_same_set hash_netiface_same_set
#define STREQ(a, b) (strcmp(a, b) == 0)
/* The type variant functions: IPv4 */
/* Member elements without timeout */
struct hash_netiface4_elem {
__be32 ip;
const char *iface;
u8 physdev;
u8 cidr;
u16 padding;
};
/* Member elements with timeout support */
struct hash_netiface4_telem {
__be32 ip;
const char *iface;
u8 physdev;
u8 cidr;
u16 padding;
unsigned long timeout;
};
static inline bool
hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1,
const struct hash_netiface4_elem *ip2)
{
return ip1->ip == ip2->ip &&
ip1->cidr == ip2->cidr &&
ip1->physdev == ip2->physdev &&
ip1->iface == ip2->iface;
}
static inline bool
hash_netiface4_data_isnull(const struct hash_netiface4_elem *elem)
{
return elem->cidr == 0;
}
static inline void
hash_netiface4_data_copy(struct hash_netiface4_elem *dst,
const struct hash_netiface4_elem *src) {
dst->ip = src->ip;
dst->cidr = src->cidr;
dst->physdev = src->physdev;
dst->iface = src->iface;
}
static inline void
hash_netiface4_data_netmask(struct hash_netiface4_elem *elem, u8 cidr)
{
elem->ip &= ip_set_netmask(cidr);
elem->cidr = cidr;
}
static inline void
hash_netiface4_data_zero_out(struct hash_netiface4_elem *elem)
{
elem->cidr = 0;
}
static bool
hash_netiface4_data_list(struct sk_buff *skb,
const struct hash_netiface4_elem *data)
{
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
if (flags)
NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
return 0;
nla_put_failure:
return 1;
}
static bool
hash_netiface4_data_tlist(struct sk_buff *skb,
const struct hash_netiface4_elem *data)
{
const struct hash_netiface4_telem *tdata =
(const struct hash_netiface4_telem *)data;
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
if (flags)
NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
return 0;
nla_put_failure:
return 1;
}
#define IP_SET_HASH_WITH_NETS
#define IP_SET_HASH_WITH_RBTREE
#define PF 4
#define HOST_MASK 32
#include "ip_set_ahash.h"
static inline void
hash_netiface4_data_next(struct ip_set_hash *h,
const struct hash_netiface4_elem *d)
{
h->next.ip = ntohl(d->ip);
}
static int
hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem data = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
int ret;
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr);
#define IFACE(dir) (par->dir ? par->dir->name : NULL)
#define PHYSDEV(dir) (nf_bridge->dir ? nf_bridge->dir->name : NULL)
#define SRCDIR (opt->flags & IPSET_DIM_TWO_SRC)
if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
#ifdef CONFIG_BRIDGE_NETFILTER
const struct nf_bridge_info *nf_bridge = skb->nf_bridge;
if (!nf_bridge)
return -EINVAL;
data.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
data.physdev = 1;
#else
data.iface = NULL;
#endif
} else
data.iface = SRCDIR ? IFACE(in) : IFACE(out);
if (!data.iface)
return -EINVAL;
ret = iface_test(&h->rbtree, &data.iface);
if (adt == IPSET_ADD) {
if (!ret) {
ret = iface_add(&h->rbtree, &data.iface);
if (ret)
return ret;
}
} else if (!ret)
return ret;
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem data = { .cidr = HOST_MASK };
u32 ip = 0, ip_to, last;
u32 timeout = h->timeout;
char iface[IFNAMSIZ] = {};
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!tb[IPSET_ATTR_IFACE] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!data.cidr)
return -IPSET_ERR_INVALID_CIDR;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE]));
data.iface = iface;
ret = iface_test(&h->rbtree, &data.iface);
if (adt == IPSET_ADD) {
if (!ret) {
ret = iface_add(&h->rbtree, &data.iface);
if (ret)
return ret;
}
} else if (!ret)
return ret;
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_PHYSDEV)
data.physdev = 1;
}
if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
data.ip = htonl(ip & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
if (ip_to < ip)
swap(ip, ip_to);
if (ip + UINT_MAX == ip_to)
return -IPSET_ERR_HASH_RANGE;
} else {
ip_set_mask_from_to(ip, ip_to, data.cidr);
}
if (retried)
ip = h->next.ip;
while (!after(ip, ip_to)) {
data.ip = htonl(ip);
last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
ip = last + 1;
}
return ret;
}
static bool
hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct ip_set_hash *x = a->data;
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
struct hash_netiface6_elem {
union nf_inet_addr ip;
const char *iface;
u8 physdev;
u8 cidr;
u16 padding;
};
struct hash_netiface6_telem {
union nf_inet_addr ip;
const char *iface;
u8 physdev;
u8 cidr;
u16 padding;
unsigned long timeout;
};
static inline bool
hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1,
const struct hash_netiface6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->cidr == ip2->cidr &&
ip1->physdev == ip2->physdev &&
ip1->iface == ip2->iface;
}
static inline bool
hash_netiface6_data_isnull(const struct hash_netiface6_elem *elem)
{
return elem->cidr == 0;
}
static inline void
hash_netiface6_data_copy(struct hash_netiface6_elem *dst,
const struct hash_netiface6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem)
{
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
}
static inline void
hash_netiface6_data_netmask(struct hash_netiface6_elem *elem, u8 cidr)
{
ip6_netmask(&elem->ip, cidr);
elem->cidr = cidr;
}
static bool
hash_netiface6_data_list(struct sk_buff *skb,
const struct hash_netiface6_elem *data)
{
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
if (flags)
NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
return 0;
nla_put_failure:
return 1;
}
static bool
hash_netiface6_data_tlist(struct sk_buff *skb,
const struct hash_netiface6_elem *data)
{
const struct hash_netiface6_telem *e =
(const struct hash_netiface6_telem *)data;
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
if (flags)
NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout)));
return 0;
nla_put_failure:
return 1;
}
#undef PF
#undef HOST_MASK
#define PF 6
#define HOST_MASK 128
#include "ip_set_ahash.h"
static inline void
hash_netiface6_data_next(struct ip_set_hash *h,
const struct hash_netiface6_elem *d)
{
}
static int
hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface6_elem data = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
int ret;
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
#ifdef CONFIG_BRIDGE_NETFILTER
const struct nf_bridge_info *nf_bridge = skb->nf_bridge;
if (!nf_bridge)
return -EINVAL;
data.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
data.physdev = 1;
#else
data.iface = NULL;
#endif
} else
data.iface = SRCDIR ? IFACE(in) : IFACE(out);
if (!data.iface)
return -EINVAL;
ret = iface_test(&h->rbtree, &data.iface);
if (adt == IPSET_ADD) {
if (!ret) {
ret = iface_add(&h->rbtree, &data.iface);
if (ret)
return ret;
}
} else if (!ret)
return ret;
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface6_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
char iface[IFNAMSIZ] = {};
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!tb[IPSET_ATTR_IFACE] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR])
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!data.cidr)
return -IPSET_ERR_INVALID_CIDR;
ip6_netmask(&data.ip, data.cidr);
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE]));
data.iface = iface;
ret = iface_test(&h->rbtree, &data.iface);
if (adt == IPSET_ADD) {
if (!ret) {
ret = iface_add(&h->rbtree, &data.iface);
if (ret)
return ret;
}
} else if (!ret)
return ret;
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_PHYSDEV)
data.physdev = 1;
}
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
static int
hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
* (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket));
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
h->rbtree = RB_ROOT;
set->data = h;
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == AF_INET
? &hash_netiface4_tvariant : &hash_netiface6_tvariant;
if (set->family == AF_INET)
hash_netiface4_gc_init(set);
else
hash_netiface6_gc_init(set);
} else {
set->variant = set->family == AF_INET
? &hash_netiface4_variant : &hash_netiface6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
return 0;
}
static struct ip_set_type hash_netiface_type __read_mostly = {
.name = "hash:net,iface",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_IFACE,
.dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC,
.revision_min = 0,
.create = hash_netiface_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IFACE] = { .type = NLA_NUL_STRING,
.len = IPSET_MAXNAMELEN - 1 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};
static int __init
hash_netiface_init(void)
{
return ip_set_type_register(&hash_netiface_type);
}
static void __exit
hash_netiface_fini(void)
{
ip_set_type_unregister(&hash_netiface_type);
}
module_init(hash_netiface_init);
module_exit(hash_netiface_fini);

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -7,21 +7,18 @@
/* Kernel module implementing an IP set type: the hash:net,port type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <linux/netfilter.h>
#include "pfxlen.h"
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_getport.h"
@@ -45,16 +42,16 @@ hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
/* Member elements without timeout */
struct hash_netport4_elem {
u32 ip;
u16 port;
__be32 ip;
__be16 port;
u8 proto;
u8 cidr;
};
/* Member elements with timeout support */
struct hash_netport4_telem {
u32 ip;
u16 port;
__be32 ip;
__be16 port;
u8 proto;
u8 cidr;
unsigned long timeout;
@@ -64,10 +61,10 @@ static inline bool
hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
const struct hash_netport4_elem *ip2)
{
return ip1->ip == ip2->ip
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto
&& ip1->cidr == ip2->cidr;
return ip1->ip == ip2->ip &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto &&
ip1->cidr == ip2->cidr;
}
static inline bool
@@ -86,20 +83,10 @@ hash_netport4_data_copy(struct hash_netport4_elem *dst,
dst->cidr = src->cidr;
}
static inline void
hash_netport4_data_swap(struct hash_netport4_elem *dst,
struct hash_netport4_elem *src)
{
swap(dst->ip, src->ip);
swap(dst->port, src->port);
swap(dst->proto, src->proto);
swap(dst->cidr, src->cidr);
}
static inline void
hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr)
{
elem->ip &= NETMASK(cidr);
elem->ip &= ip_set_netmask(cidr);
elem->cidr = cidr;
}
@@ -109,7 +96,7 @@ hash_netport4_data_zero_out(struct hash_netport4_elem *elem)
elem->proto = 0;
}
static inline bool
static bool
hash_netport4_data_list(struct sk_buff *skb,
const struct hash_netport4_elem *data)
{
@@ -123,7 +110,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_netport4_data_tlist(struct sk_buff *skb,
const struct hash_netport4_elem *data)
{
@@ -150,92 +137,87 @@ nla_put_failure:
#define HOST_MASK 32
#include "ip_set_ahash.h"
static inline void
hash_netport4_data_next(struct ip_set_hash *h,
const struct hash_netport4_elem *d)
{
h->next.ip = ntohl(d->ip);
h->next.port = ntohs(d->port);
}
static int
hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = {
.cidr = h->nets[0].cidr || HOST_MASK };
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= NETMASK(data.cidr);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static const struct nla_policy
hash_netport_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_netport4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = { .cidr = HOST_MASK };
u32 port, port_to;
u32 port, port_to, p = 0, ip = 0, ip_to, last;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_netport_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR])
if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!data.cidr)
return -IPSET_ERR_INVALID_CIDR;
data.ip &= NETMASK(data.cidr);
}
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
if (!(with_ports || data.proto == IPPROTO_ICMP))
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -243,27 +225,48 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
data.ip = htonl(ip & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port = port_to = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
if (port_to < port)
swap(port, port_to);
}
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
if (ip_to < ip)
swap(ip, ip_to);
if (ip + UINT_MAX == ip_to)
return -IPSET_ERR_HASH_RANGE;
} else {
ip_set_mask_from_to(ip, ip_to, data.cidr);
}
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
if (retried)
ip = h->next.ip;
while (!after(ip, ip_to)) {
data.ip = htonl(ip);
last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
p = retried && ip == h->next.ip ? h->next.port : port;
for (; p <= port_to; p++) {
data.port = htons(p);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
ip = last + 1;
}
return ret;
}
@@ -274,22 +277,22 @@ hash_netport_same_set(const struct ip_set *a, const struct ip_set *b)
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout;
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
struct hash_netport6_elem {
union nf_inet_addr ip;
u16 port;
__be16 port;
u8 proto;
u8 cidr;
};
struct hash_netport6_telem {
union nf_inet_addr ip;
u16 port;
__be16 port;
u8 proto;
u8 cidr;
unsigned long timeout;
@@ -299,10 +302,10 @@ static inline bool
hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
const struct hash_netport6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto
&& ip1->cidr == ip2->cidr;
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto &&
ip1->cidr == ip2->cidr;
}
static inline bool
@@ -318,17 +321,6 @@ hash_netport6_data_copy(struct hash_netport6_elem *dst,
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_netport6_data_swap(struct hash_netport6_elem *dst,
struct hash_netport6_elem *src)
{
struct hash_netport6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
{
@@ -338,10 +330,10 @@ hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= NETMASK6(prefix)[0];
ip->ip6[1] &= NETMASK6(prefix)[1];
ip->ip6[2] &= NETMASK6(prefix)[2];
ip->ip6[3] &= NETMASK6(prefix)[3];
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
}
static inline void
@@ -351,7 +343,7 @@ hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr)
elem->cidr = cidr;
}
static inline bool
static bool
hash_netport6_data_list(struct sk_buff *skb,
const struct hash_netport6_elem *data)
{
@@ -365,7 +357,7 @@ nla_put_failure:
return 1;
}
static inline bool
static bool
hash_netport6_data_tlist(struct sk_buff *skb,
const struct hash_netport6_elem *data)
{
@@ -391,50 +383,63 @@ nla_put_failure:
#define HOST_MASK 128
#include "ip_set_ahash.h"
static inline void
hash_netport6_data_next(struct ip_set_hash *h,
const struct hash_netport6_elem *d)
{
h->next.port = ntohs(d->port);
}
static int
hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = {
.cidr = h->nets[0].cidr || HOST_MASK };
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
return adtfn(set, &data, h->timeout);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = { .cidr = HOST_MASK };
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_netport_adt_policy))
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
if (ret)
return ret;
@@ -445,27 +450,21 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
ip6_netmask(&data.ip, data.cidr);
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
if (!(with_ports || data.proto == IPPROTO_ICMPV6))
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
@@ -473,10 +472,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
@@ -485,9 +482,11 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port > port_to)
swap(port, port_to);
if (retried)
port = h->next.port;
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@@ -499,20 +498,9 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
/* Create hash:ip type of sets */
static const struct nla_policy
hash_netport_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_netport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
@@ -520,8 +508,9 @@ hash_netport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_netport_create_policy))
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
@@ -546,8 +535,7 @@ hash_netport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
+ jhash_size(hbits) * sizeof(struct hbucket));
if (!h->table) {
kfree(h);
return -ENOMEM;
@@ -571,7 +559,7 @@ hash_netport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
? &hash_netport4_variant : &hash_netport6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
@@ -584,8 +572,28 @@ static struct ip_set_type hash_netport_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC,
.revision = 0,
.revision_min = 0,
/* 1 SCTP and UDPLITE support added */
.revision_max = 2, /* Range as input support for IPv4 added */
.create = hash_netport_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2008-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2008-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -7,7 +7,6 @@
/* Kernel module implementing an IP set type: the list:set type */
#include "ip_set_kernel.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
@@ -44,14 +43,19 @@ struct list_set {
static inline struct set_elem *
list_set_elem(const struct list_set *map, u32 id)
{
return (struct set_elem *)((char *)map->members + id * map->dsize);
return (struct set_elem *)((void *)map->members + id * map->dsize);
}
static inline struct set_telem *
list_set_telem(const struct list_set *map, u32 id)
{
return (struct set_telem *)((void *)map->members + id * map->dsize);
}
static inline bool
list_set_timeout(const struct list_set *map, u32 id)
{
const struct set_telem *elem =
(const struct set_telem *) list_set_elem(map, id);
const struct set_telem *elem = list_set_telem(map, id);
return ip_set_timeout_test(elem->timeout);
}
@@ -59,24 +63,17 @@ list_set_timeout(const struct list_set *map, u32 id)
static inline bool
list_set_expired(const struct list_set *map, u32 id)
{
const struct set_telem *elem =
(const struct set_telem *) list_set_elem(map, id);
const struct set_telem *elem = list_set_telem(map, id);
return ip_set_timeout_expired(elem->timeout);
}
static inline int
list_set_exist(const struct set_telem *elem)
{
return elem->id != IPSET_INVALID_ID
&& !ip_set_timeout_expired(elem->timeout);
}
/* Set list without and with timeout */
static int
list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct list_set *map = set->data;
struct set_elem *elem;
@@ -91,17 +88,17 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
continue;
switch (adt) {
case IPSET_TEST:
ret = ip_set_test(elem->id, skb, pf, dim, flags);
ret = ip_set_test(elem->id, skb, par, opt);
if (ret > 0)
return ret;
break;
case IPSET_ADD:
ret = ip_set_add(elem->id, skb, pf, dim, flags);
ret = ip_set_add(elem->id, skb, par, opt);
if (ret == 0)
return ret;
break;
case IPSET_DEL:
ret = ip_set_del(elem->id, skb, pf, dim, flags);
ret = ip_set_del(elem->id, skb, par, opt);
if (ret == 0)
return ret;
break;
@@ -112,32 +109,35 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
return -EINVAL;
}
static const struct nla_policy list_set_adt_policy[IPSET_ATTR_ADT_MAX+1] = {
[IPSET_ATTR_NAME] = { .type = NLA_STRING,
.len = IPSET_MAXNAMELEN },
[IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
.len = IPSET_MAXNAMELEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
};
static inline bool
next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
static bool
id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
{
const struct set_elem *elem;
if (i + 1 < map->size) {
elem = list_set_elem(map, i + 1);
return !!(elem->id == id
&& !(with_timeout(map->timeout)
&& list_set_expired(map, i + 1)));
if (i < map->size) {
elem = list_set_elem(map, i);
return elem->id == id;
}
return 0;
}
static inline void
static bool
id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id)
{
const struct set_elem *elem;
if (i < map->size) {
elem = list_set_elem(map, i);
return !!(elem->id == id &&
!(with_timeout(map->timeout) &&
list_set_expired(map, i)));
}
return 0;
}
static void
list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
{
struct set_elem *e;
@@ -150,18 +150,18 @@ list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
}
}
static inline void
static void
list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
unsigned long timeout)
{
struct set_telem *e;
for (; i < map->size; i++) {
e = (struct set_telem *)list_set_elem(map, i);
e = list_set_telem(map, i);
swap(e->id, id);
swap(e->timeout, timeout);
if (e->id == IPSET_INVALID_ID)
break;
swap(e->timeout, timeout);
}
}
@@ -175,7 +175,7 @@ list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
/* Last element replaced: e.g. add new,before,last */
ip_set_put_byindex(e->id);
if (with_timeout(map->timeout))
list_elem_tadd(map, i, id, timeout);
list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
else
list_elem_add(map, i, id);
@@ -183,11 +183,11 @@ list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
}
static int
list_set_del(struct list_set *map, ip_set_id_t id, u32 i)
list_set_del(struct list_set *map, u32 i)
{
struct set_elem *a = list_set_elem(map, i), *b;
ip_set_put_byindex(id);
ip_set_put_byindex(a->id);
for (; i < map->size - 1; i++) {
b = list_set_elem(map, i + 1);
@@ -204,13 +204,26 @@ list_set_del(struct list_set *map, ip_set_id_t id, u32 i)
return 0;
}
static void
cleanup_entries(struct list_set *map)
{
struct set_telem *e;
u32 i;
for (i = 0; i < map->size; i++) {
e = list_set_telem(map, i);
if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
list_set_del(map, i);
}
}
static int
list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
list_set_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct list_set *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
bool with_timeout = with_timeout(map->timeout);
bool flag_exist = flags & IPSET_FLAG_EXIST;
int before = 0;
u32 timeout = map->timeout;
ip_set_id_t id, refid = IPSET_INVALID_ID;
@@ -219,14 +232,14 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
u32 i;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
list_set_adt_policy))
if (unlikely(!tb[IPSET_ATTR_NAME] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
if (tb[IPSET_ATTR_NAME]) {
id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
if (id == IPSET_INVALID_ID)
return -IPSET_ERR_NAME;
@@ -235,8 +248,6 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = -IPSET_ERR_LOOP;
goto finish;
}
} else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
@@ -265,33 +276,50 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
}
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (with_timeout && adt != IPSET_TEST)
cleanup_entries(map);
switch (adt) {
case IPSET_TEST:
for (i = 0; i < map->size && !ret; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID
|| (before != 0 && i + 1 >= map->size))
if (elem->id == IPSET_INVALID_ID ||
(before != 0 && i + 1 >= map->size))
break;
else if (with_timeout && list_set_expired(map, i))
continue;
else if (before > 0 && elem->id == id)
ret = next_id_eq(map, i, refid);
ret = id_eq_timeout(map, i + 1, refid);
else if (before < 0 && elem->id == refid)
ret = next_id_eq(map, i, id);
ret = id_eq_timeout(map, i + 1, id);
else if (before == 0 && elem->id == id)
ret = 1;
}
break;
case IPSET_ADD:
for (i = 0; i < map->size && !ret; i++) {
for (i = 0; i < map->size; i++) {
elem = list_set_elem(map, i);
if (elem->id == id
&& !(with_timeout && list_set_expired(map, i)))
if (elem->id != id)
continue;
if (!(with_timeout && flag_exist)) {
ret = -IPSET_ERR_EXIST;
goto finish;
} else {
struct set_telem *e = list_set_telem(map, i);
if ((before > 1 &&
!id_eq(map, i + 1, refid)) ||
(before < 0 &&
(i == 0 || !id_eq(map, i - 1, refid)))) {
ret = -IPSET_ERR_EXIST;
goto finish;
}
e->timeout = ip_set_timeout_set(timeout);
ip_set_put_byindex(id);
ret = 0;
goto finish;
}
}
if (ret == -IPSET_ERR_EXIST)
break;
ret = -IPSET_ERR_LIST_FULL;
for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
elem = list_set_elem(map, i);
@@ -300,9 +328,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
: list_set_add(map, i, id, timeout);
else if (elem->id != refid)
continue;
else if (with_timeout && list_set_expired(map, i))
ret = -IPSET_ERR_REF_EXIST;
else if (before)
else if (before > 0)
ret = list_set_add(map, i, id, timeout);
else if (i + 1 < map->size)
ret = list_set_add(map, i + 1, id, timeout);
@@ -316,17 +342,13 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = before != 0 ? -IPSET_ERR_REF_EXIST
: -IPSET_ERR_EXIST;
break;
} else if (with_timeout && list_set_expired(map, i))
continue;
else if (elem->id == id
&& (before == 0
|| (before > 0
&& next_id_eq(map, i, refid))))
ret = list_set_del(map, id, i);
else if (before < 0
&& elem->id == refid
&& next_id_eq(map, i, id))
ret = list_set_del(map, id, i + 1);
} else if (elem->id == id &&
(before == 0 ||
(before > 0 && id_eq(map, i + 1, refid))))
ret = list_set_del(map, i);
else if (elem->id == refid &&
before < 0 && id_eq(map, i + 1, id))
ret = list_set_del(map, i + 1);
}
break;
default:
@@ -383,15 +405,14 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size));
if (with_timeout(map->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->size * map->dsize));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EFAULT;
return -EMSGSIZE;
}
static int
@@ -405,7 +426,7 @@ list_set_list(const struct ip_set *set,
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
return -EMSGSIZE;
for (; cb->args[2] < map->size; cb->args[2]++) {
i = cb->args[2];
e = list_set_elem(map, i);
@@ -417,7 +438,7 @@ list_set_list(const struct ip_set *set,
if (!nested) {
if (i == first) {
nla_nest_cancel(skb, atd);
return -EFAULT;
return -EMSGSIZE;
} else
goto nla_put_failure;
}
@@ -440,6 +461,10 @@ finish:
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(i == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
@@ -449,8 +474,8 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b)
const struct list_set *x = a->data;
const struct list_set *y = b->data;
return x->size == y->size
&& x->timeout == y->timeout;
return x->size == y->size &&
x->timeout == y->timeout;
}
static const struct ip_set_type_variant list_set = {
@@ -468,25 +493,16 @@ list_set_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct list_set *map = set->data;
struct set_telem *e;
u32 i;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (i = map->size - 1; i >= 0; i--) {
e = (struct set_telem *) list_set_elem(map, i);
if (e->id != IPSET_INVALID_ID
&& list_set_expired(map, i))
list_set_del(map, e->id, i);
}
read_unlock_bh(&set->lock);
write_lock_bh(&set->lock);
cleanup_entries(map);
write_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static inline void
static void
list_set_gc_init(struct ip_set *set)
{
struct list_set *map = set->data;
@@ -500,13 +516,7 @@ list_set_gc_init(struct ip_set *set)
/* Create list:set type of sets */
static const struct nla_policy
list_set_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_SIZE] = { .type = NLA_U32 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static inline bool
static bool
init_list_set(struct ip_set *set, u32 size, size_t dsize,
unsigned long timeout)
{
@@ -532,14 +542,12 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize,
}
static int
list_set_create(struct ip_set *set, struct nlattr *head, int len,
u32 flags)
list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 size = IP_SET_LIST_DEFAULT_SIZE;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
list_set_create_policy))
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_SIZE])
@@ -568,8 +576,22 @@ static struct ip_set_type list_set_type __read_mostly = {
.features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.revision_min = 0,
.revision_max = 0,
.create = list_set_create,
.create_policy = {
[IPSET_ATTR_SIZE] = { .type = NLA_U32 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_NAME] = { .type = NLA_STRING,
.len = IPSET_MAXNAMELEN },
[IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
.len = IPSET_MAXNAMELEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};

View File

@@ -1,7 +1,7 @@
#ifndef _IP_SET_TIMEOUT_H
#define _IP_SET_TIMEOUT_H
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -22,6 +22,9 @@
#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT)
#define opt_timeout(opt, map) \
(with_timeout((opt)->timeout) ? (opt)->timeout : (map)->timeout)
static inline unsigned int
ip_set_timeout_uget(struct nlattr *tb)
{
@@ -43,17 +46,17 @@ ip_set_timeout_uget(struct nlattr *tb)
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
return timeout != IPSET_ELEM_UNSET
&& (timeout == IPSET_ELEM_PERMANENT
|| time_after(timeout, jiffies));
return timeout != IPSET_ELEM_UNSET &&
(timeout == IPSET_ELEM_PERMANENT ||
time_is_after_jiffies(timeout));
}
static inline bool
ip_set_timeout_expired(unsigned long timeout)
{
return timeout != IPSET_ELEM_UNSET
&& timeout != IPSET_ELEM_PERMANENT
&& time_before(timeout, jiffies);
return timeout != IPSET_ELEM_UNSET &&
timeout != IPSET_ELEM_PERMANENT &&
time_is_before_jiffies(timeout);
}
static inline unsigned long
@@ -64,7 +67,7 @@ ip_set_timeout_set(u32 timeout)
if (!timeout)
return IPSET_ELEM_PERMANENT;
t = timeout * HZ + jiffies;
t = msecs_to_jiffies(timeout * 1000) + jiffies;
if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT)
/* Bingo! */
t++;
@@ -75,7 +78,8 @@ ip_set_timeout_set(u32 timeout)
static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
return timeout == IPSET_ELEM_PERMANENT ? 0 :
jiffies_to_msecs(timeout - jiffies)/1000;
}
#else
@@ -88,15 +92,15 @@ ip_set_timeout_get(unsigned long timeout)
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT
|| time_after(timeout, jiffies);
return timeout == IPSET_ELEM_PERMANENT ||
time_is_after_jiffies(timeout);
}
static inline bool
ip_set_timeout_expired(unsigned long timeout)
{
return timeout != IPSET_ELEM_PERMANENT
&& time_before(timeout, jiffies);
return timeout != IPSET_ELEM_PERMANENT &&
time_is_before_jiffies(timeout);
}
static inline unsigned long
@@ -107,7 +111,7 @@ ip_set_timeout_set(u32 timeout)
if (!timeout)
return IPSET_ELEM_PERMANENT;
t = timeout * HZ + jiffies;
t = msecs_to_jiffies(timeout * 1000) + jiffies;
if (t == IPSET_ELEM_PERMANENT)
/* Bingo! :-) */
t++;
@@ -118,7 +122,8 @@ ip_set_timeout_set(u32 timeout)
static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
return timeout == IPSET_ELEM_PERMANENT ? 0 :
jiffies_to_msecs(timeout - jiffies)/1000;
}
#endif /* ! IP_SET_BITMAP_TIMEOUT */

View File

@@ -7,6 +7,7 @@
#include <assert.h> /* assert */
#include <arpa/inet.h> /* ntoh* */
#include <net/ethernet.h> /* ETH_ALEN */
#include <net/if.h> /* IFNAMSIZ */
#include <sys/socket.h> /* AF_ */
#include <stdlib.h> /* malloc, free */
#include <string.h> /* memset */
@@ -66,11 +67,13 @@ struct ipset_data {
/* ADT/LIST/SAVE */
struct {
union nf_inet_addr ip2;
union nf_inet_addr ip2_to;
uint8_t cidr2;
uint8_t proto;
char ether[ETH_ALEN];
char name[IPSET_MAXNAMELEN];
char nameref[IPSET_MAXNAMELEN];
char iface[IFNAMSIZ];
} adt;
};
};
@@ -289,12 +292,20 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
return -1;
copy_addr(data->family, &data->adt.ip2, value);
break;
case IPSET_OPT_IP2_TO:
if (!(data->family == AF_INET || data->family == AF_INET6))
return -1;
copy_addr(data->family, &data->adt.ip2_to, value);
break;
case IPSET_OPT_CIDR2:
data->adt.cidr2 = *(const uint8_t *) value;
break;
case IPSET_OPT_PROTO:
data->adt.proto = *(const uint8_t *) value;
break;
case IPSET_OPT_IFACE:
ipset_strlcpy(data->adt.iface, value, IFNAMSIZ);
break;
/* Swap/rename */
case IPSET_OPT_SETNAME2:
ipset_strlcpy(data->setname2, value, IPSET_MAXNAMELEN);
@@ -306,6 +317,9 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
case IPSET_OPT_BEFORE:
cadt_flag_type_attr(data, opt, IPSET_FLAG_BEFORE);
break;
case IPSET_OPT_PHYSDEV:
cadt_flag_type_attr(data, opt, IPSET_FLAG_PHYSDEV);
break;
case IPSET_OPT_FLAGS:
data->flags = *(const uint32_t *)value;
break;
@@ -401,10 +415,14 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
return data->adt.nameref;
case IPSET_OPT_IP2:
return &data->adt.ip2;
case IPSET_OPT_IP2_TO:
return &data->adt.ip2_to;
case IPSET_OPT_CIDR2:
return &data->adt.cidr2;
case IPSET_OPT_PROTO:
return &data->adt.proto;
case IPSET_OPT_IFACE:
return &data->adt.iface;
/* Swap/rename */
case IPSET_OPT_SETNAME2:
return data->setname2;
@@ -414,6 +432,7 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
return &data->flags;
case IPSET_OPT_CADT_FLAGS:
case IPSET_OPT_BEFORE:
case IPSET_OPT_PHYSDEV:
return &data->cadt_flags;
default:
return NULL;
@@ -436,6 +455,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
case IPSET_OPT_IP:
case IPSET_OPT_IP_TO:
case IPSET_OPT_IP2:
case IPSET_OPT_IP2_TO:
return family == AF_INET ? sizeof(uint32_t)
: sizeof(struct in6_addr);
case IPSET_OPT_PORT:
@@ -463,8 +483,9 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
return sizeof(uint8_t);
case IPSET_OPT_ETHER:
return ETH_ALEN;
/* Flags counted once */
/* Flags doesn't counted once :-( */
case IPSET_OPT_BEFORE:
case IPSET_OPT_PHYSDEV:
return sizeof(uint32_t);
default:
return 0;

View File

@@ -0,0 +1,288 @@
/* Copyright 2011 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* 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.
*/
#include <arpa/inet.h> /* inet_ntop */
#include <libmnl/libmnl.h> /* libmnl backend */
struct ipset_attrname {
const char *name;
};
static const struct ipset_attrname cmdattr2name[] = {
[IPSET_ATTR_PROTOCOL] = { .name = "PROTOCOL" },
[IPSET_ATTR_SETNAME] = { .name = "SETNAME" },
[IPSET_ATTR_TYPENAME] = { .name = "TYPENAME" },
[IPSET_ATTR_REVISION] = { .name = "REVISION" },
[IPSET_ATTR_FAMILY] = { .name = "FAMILY" },
[IPSET_ATTR_FLAGS] = { .name = "FLAGS" },
[IPSET_ATTR_DATA] = { .name = "DATA" },
[IPSET_ATTR_ADT] = { .name = "ADT" },
[IPSET_ATTR_LINENO] = { .name = "LINENO" },
[IPSET_ATTR_PROTOCOL_MIN] = { .name = "PROTO_MIN" },
};
static const struct ipset_attrname createattr2name[] = {
[IPSET_ATTR_IP] = { .name = "IP" },
[IPSET_ATTR_IP_TO] = { .name = "IP_TO" },
[IPSET_ATTR_CIDR] = { .name = "CIDR" },
[IPSET_ATTR_PORT] = { .name = "PORT" },
[IPSET_ATTR_PORT_TO] = { .name = "PORT_TO" },
[IPSET_ATTR_TIMEOUT] = { .name = "TIMEOUT" },
[IPSET_ATTR_PROTO] = { .name = "PROTO" },
[IPSET_ATTR_CADT_FLAGS] = { .name = "CADT_FLAGS" },
[IPSET_ATTR_CADT_LINENO] = { .name = "CADT_LINENO" },
[IPSET_ATTR_GC] = { .name = "GC" },
[IPSET_ATTR_HASHSIZE] = { .name = "HASHSIZE" },
[IPSET_ATTR_MAXELEM] = { .name = "MAXELEM" },
[IPSET_ATTR_NETMASK] = { .name = "NETMASK" },
[IPSET_ATTR_PROBES] = { .name = "PROBES" },
[IPSET_ATTR_RESIZE] = { .name = "RESIZE" },
[IPSET_ATTR_SIZE] = { .name = "SIZE" },
[IPSET_ATTR_ELEMENTS] = { .name = "ELEMENTS" },
[IPSET_ATTR_REFERENCES] = { .name = "REFERENCES" },
[IPSET_ATTR_MEMSIZE] = { .name = "MEMSIZE" },
};
static const struct ipset_attrname adtattr2name[] = {
[IPSET_ATTR_IP] = { .name = "IP" },
[IPSET_ATTR_IP_TO] = { .name = "IP_TO" },
[IPSET_ATTR_CIDR] = { .name = "CIDR" },
[IPSET_ATTR_PORT] = { .name = "PORT" },
[IPSET_ATTR_PORT_TO] = { .name = "PORT_TO" },
[IPSET_ATTR_TIMEOUT] = { .name = "TIMEOUT" },
[IPSET_ATTR_PROTO] = { .name = "PROTO" },
[IPSET_ATTR_CADT_FLAGS] = { .name = "CADT_FLAGS" },
[IPSET_ATTR_CADT_LINENO] = { .name = "CADT_LINENO" },
[IPSET_ATTR_ETHER] = { .name = "ETHER" },
[IPSET_ATTR_NAME] = { .name = "NAME" },
[IPSET_ATTR_NAMEREF] = { .name = "NAMEREF" },
[IPSET_ATTR_IP2] = { .name = "IP2" },
[IPSET_ATTR_CIDR2] = { .name = "CIDR2" },
[IPSET_ATTR_IP2_TO] = { .name = "IP2_TO" },
[IPSET_ATTR_IFACE] = { .name = "IFACE" },
};
static void
debug_cadt_attrs(int max, const struct ipset_attr_policy *policy,
const struct ipset_attrname attr2name[],
struct nlattr *nla[])
{
uint32_t v;
int i;
fprintf(stderr, "\t\t%s attributes:\n",
policy == create_attrs ? "CREATE" : "ADT");
for (i = IPSET_ATTR_UNSPEC + 1; i <= max; i++) {
if (!nla[i])
continue;
switch (policy[i].type) {
case MNL_TYPE_U8:
v = *(uint8_t *) mnl_attr_get_payload(nla[i]);
fprintf(stderr, "\t\t%s: %u\n",
attr2name[i].name, v);
break;
case MNL_TYPE_U16:
v = *(uint16_t *) mnl_attr_get_payload(nla[i]);
fprintf(stderr, "\t\t%s: %u\n",
attr2name[i].name, ntohs(v));
break;
case MNL_TYPE_U32:
v = *(uint32_t *) mnl_attr_get_payload(nla[i]);
fprintf(stderr, "\t\t%s: %u\n",
attr2name[i].name, ntohl(v));
break;
case MNL_TYPE_NUL_STRING:
fprintf(stderr, "\t\t%s: %s\n",
attr2name[i].name,
(const char *) mnl_attr_get_payload(nla[i]));
break;
case MNL_TYPE_NESTED: {
struct nlattr *ipattr[IPSET_ATTR_IPADDR_MAX+1] = {};
char addr[INET6_ADDRSTRLEN];
void *d;
if (mnl_attr_parse_nested(nla[i], ipaddr_attr_cb,
ipattr) < 0) {
fprintf(stderr,
"\t\tIPADDR: cannot validate "
"and parse attributes\n");
continue;
}
if (ipattr[IPSET_ATTR_IPADDR_IPV4]) {
d = mnl_attr_get_payload(
ipattr[IPSET_ATTR_IPADDR_IPV4]);
inet_ntop(AF_INET, d, addr, INET6_ADDRSTRLEN);
fprintf(stderr, "\t\t%s: %s\n",
attr2name[i].name, addr);
} else if (ipattr[IPSET_ATTR_IPADDR_IPV6]) {
d = mnl_attr_get_payload(
ipattr[IPSET_ATTR_IPADDR_IPV6]);
inet_ntop(AF_INET6, d, addr, INET6_ADDRSTRLEN);
fprintf(stderr, "\t\t%s: %s\n",
attr2name[i].name, addr);
}
break;
}
default:
fprintf(stderr, "\t\t%s: unresolved!\n",
attr2name[i].name);
}
}
}
static void
debug_cmd_attrs(int cmd, struct nlattr *nla[])
{
struct nlattr *adt[IPSET_ATTR_ADT_MAX+1] = {};
struct nlattr *cattr[IPSET_ATTR_CREATE_MAX+1] = {};
uint32_t v;
int i;
fprintf(stderr, "\tCommand attributes:\n");
for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CMD_MAX; i++) {
if (!nla[i])
continue;
switch (cmd_attrs[i].type) {
case MNL_TYPE_U8:
v = *(uint8_t *) mnl_attr_get_payload(nla[i]);
fprintf(stderr, "\t%s: %u\n",
cmdattr2name[i].name, v);
break;
case MNL_TYPE_U16:
v = *(uint16_t *) mnl_attr_get_payload(nla[i]);
fprintf(stderr, "\t%s: %u\n",
cmdattr2name[i].name, ntohs(v));
break;
case MNL_TYPE_U32:
v = *(uint32_t *) mnl_attr_get_payload(nla[i]);
fprintf(stderr, "\t%s: %u\n",
cmdattr2name[i].name, ntohl(v));
break;
case MNL_TYPE_NUL_STRING:
fprintf(stderr, "\t%s: %s\n",
cmdattr2name[i].name,
(const char *) mnl_attr_get_payload(nla[i]));
break;
case MNL_TYPE_NESTED:
if (i == IPSET_ATTR_DATA) {
switch (cmd) {
case IPSET_CMD_ADD:
case IPSET_CMD_DEL:
case IPSET_CMD_TEST:
if (mnl_attr_parse_nested(nla[i],
adt_attr_cb, adt) < 0) {
fprintf(stderr,
"\tADT: cannot validate "
"and parse attributes\n");
continue;
}
debug_cadt_attrs(IPSET_ATTR_ADT_MAX,
adt_attrs,
adtattr2name,
adt);
break;
default:
if (mnl_attr_parse_nested(nla[i],
create_attr_cb,
cattr) < 0) {
fprintf(stderr,
"\tCREATE: cannot validate "
"and parse attributes\n");
continue;
}
debug_cadt_attrs(IPSET_ATTR_CREATE_MAX,
create_attrs,
createattr2name,
cattr);
}
} else {
struct nlattr *tb;
mnl_attr_for_each_nested(tb, nla[i]) {
memset(adt, 0, sizeof(adt));
if (mnl_attr_parse_nested(tb,
adt_attr_cb, adt) < 0) {
fprintf(stderr,
"\tADT: cannot validate "
"and parse attributes\n");
continue;
}
debug_cadt_attrs(IPSET_ATTR_ADT_MAX,
adt_attrs,
adtattr2name,
adt);
}
}
break;
default:
fprintf(stderr, "\t%s: unresolved!\n",
cmdattr2name[i].name);
}
}
}
void
ipset_debug_msg(const char *dir, void *buffer, int len)
{
const struct nlmsghdr *nlh = buffer;
struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
int cmd, nfmsglen = MNL_ALIGN(sizeof(struct nfgenmsg));
debug = 0;
while (mnl_nlmsg_ok(nlh, len)) {
switch (nlh->nlmsg_type) {
case NLMSG_NOOP:
case NLMSG_DONE:
case NLMSG_OVERRUN:
fprintf(stderr, "Message header: %s msg %s\n"
"\tlen %d\n"
"\tseq %u\n",
dir,
nlh->nlmsg_type == NLMSG_NOOP ? "NOOP" :
nlh->nlmsg_type == NLMSG_DONE ? "DONE" :
"OVERRUN",
len, nlh->nlmsg_seq);
goto next_msg;
case NLMSG_ERROR: {
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
fprintf(stderr, "Message header: %s msg ERROR\n"
"\tlen %d\n"
"\terrcode %d\n"
"\tseq %u\n",
dir, len, err->error, nlh->nlmsg_seq);
goto next_msg;
}
default:
;
}
cmd = ipset_get_nlmsg_type(nlh);
fprintf(stderr, "Message header: %s cmd %s (%d)\n"
"\tlen %d\n"
"\tflag %s\n"
"\tseq %u\n",
dir,
cmd <= IPSET_CMD_NONE ? "NONE!" :
cmd >= IPSET_CMD_MAX ? "MAX!" : cmd2name[cmd], cmd,
len,
!(nlh->nlmsg_flags & NLM_F_EXCL) ? "EXIST" : "none",
nlh->nlmsg_seq);
if (cmd <= IPSET_CMD_NONE || cmd >= IPSET_CMD_MAX)
goto next_msg;
memset(nla, 0, sizeof(nla));
if (mnl_attr_parse(nlh, nfmsglen,
cmd_attr_cb, nla) < MNL_CB_STOP) {
fprintf(stderr, "\tcannot validate "
"and parse attributes\n");
goto next_msg;
}
debug_cmd_attrs(cmd, nla);
next_msg:
nlh = mnl_nlmsg_next(nlh, &len);
}
debug = 1;
}

View File

@@ -49,17 +49,18 @@ static const struct icmp_names icmp_typecodes[] = {
{ "address-mask-reply", 18, 0 },
};
const char * id_to_icmp(uint8_t id)
const char *id_to_icmp(uint8_t id)
{
return id < ARRAY_SIZE(icmp_typecodes) ? icmp_typecodes[id].name : NULL;
}
const char * icmp_to_name(uint8_t type, uint8_t code)
const char *icmp_to_name(uint8_t type, uint8_t code)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
if (icmp_typecodes[i].type == type && icmp_typecodes[i].code == code)
if (icmp_typecodes[i].type == type &&
icmp_typecodes[i].code == code)
return icmp_typecodes[i].name;
return NULL;
@@ -71,7 +72,8 @@ int name_to_icmp(const char *str, uint16_t *typecode)
for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
if (STRNCASEQ(icmp_typecodes[i].name, str, strlen(str))) {
*typecode = (icmp_typecodes[i].type << 8) | icmp_typecodes[i].code;
*typecode = (icmp_typecodes[i].type << 8) |
icmp_typecodes[i].code;
return 0;
}

View File

@@ -36,17 +36,19 @@ static const struct icmpv6_names icmpv6_typecodes[] = {
{ "redirect", 137, 0 },
};
const char * id_to_icmpv6(uint8_t id)
const char *id_to_icmpv6(uint8_t id)
{
return id < ARRAY_SIZE(icmpv6_typecodes) ? icmpv6_typecodes[id].name : NULL;
return id < ARRAY_SIZE(icmpv6_typecodes) ?
icmpv6_typecodes[id].name : NULL;
}
const char * icmpv6_to_name(uint8_t type, uint8_t code)
const char *icmpv6_to_name(uint8_t type, uint8_t code)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
if (icmpv6_typecodes[i].type == type && icmpv6_typecodes[i].code == code)
if (icmpv6_typecodes[i].type == type &&
icmpv6_typecodes[i].code == code)
return icmpv6_typecodes[i].name;
return NULL;
@@ -58,7 +60,8 @@ int name_to_icmpv6(const char *str, uint16_t *typecode)
for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
if (STRNCASEQ(icmpv6_typecodes[i].name, str, strlen(str))) {
*typecode = (icmpv6_typecodes[i].type << 8) | icmpv6_typecodes[i].code;
*typecode = (icmpv6_typecodes[i].type << 8) |
icmpv6_typecodes[i].code;
return 0;
}

View File

@@ -38,13 +38,14 @@ struct ipset_handle {
/* Netlink flags of the commands */
static const uint16_t cmdflags[] = {
[IPSET_CMD_CREATE-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE|NLM_F_EXCL,
[IPSET_CMD_CREATE-1] = NLM_F_REQUEST|NLM_F_ACK|
NLM_F_CREATE|NLM_F_EXCL,
[IPSET_CMD_DESTROY-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_FLUSH-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_RENAME-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_SWAP-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_LIST-1] = NLM_F_REQUEST,
[IPSET_CMD_SAVE-1] = NLM_F_REQUEST,
[IPSET_CMD_LIST-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_SAVE-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_ADD-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
[IPSET_CMD_DEL-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
[IPSET_CMD_TEST-1] = NLM_F_REQUEST|NLM_F_ACK,
@@ -83,7 +84,6 @@ ipset_mnl_fill_hdr(struct ipset_handle *handle, enum ipset_cmd cmd,
nlh->nlmsg_flags = NLM_F_REQUEST;
if (cmdflags[cmd-1] & NLM_F_ACK)
nlh->nlmsg_flags |= NLM_F_ACK;
nlh->nlmsg_seq = handle->seq = time(NULL);
ghdr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
ghdr->cmd = cmd;
@@ -102,19 +102,24 @@ ipset_mnl_query(struct ipset_handle *handle, void *buffer, size_t len)
assert(handle);
assert(buffer);
nlh->nlmsg_seq = ++handle->seq;
#ifdef IPSET_DEBUG
ipset_debug_msg("sent", nlh, nlh->nlmsg_len);
#endif
if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0)
return -ECOMM;
D("message sent");
ret = mnl_socket_recvfrom(handle->h, buffer, len);
D("message received, ret: %d", ret);
#ifdef IPSET_DEBUG
ipset_debug_msg("received", buffer, ret);
#endif
while (ret > 0) {
ret = mnl_cb_run2(buffer, ret,
handle->seq, handle->portid,
handle->cb_ctl[NLMSG_MIN_TYPE],
handle->data,
handle->cb_ctl, NLMSG_MIN_TYPE);
D("nfln_cb_run2, ret: %d", ret);
D("nfln_cb_run2, ret: %d, errno %d", ret, errno);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(handle->h, buffer, len);
@@ -225,6 +230,7 @@ ipset_mnl_init(mnl_cb_t *cb_ctl, void *data)
handle->portid = mnl_socket_get_portid(handle->h);
handle->cb_ctl = cb_ctl;
handle->data = data;
handle->seq = time(NULL);
if (ipset_mnl_getid(handle, false) < 0)
goto close_nl;

View File

@@ -12,6 +12,7 @@
#include <sys/types.h> /* getaddrinfo */
#include <sys/socket.h> /* getaddrinfo, AF_ */
#include <net/ethernet.h> /* ETH_ALEN */
#include <net/if.h> /* IFNAMSIZ */
#include <netinet/in.h> /* IPPROTO_ */
#include <libipset/debug.h> /* D() */
@@ -47,11 +48,13 @@ ipset_strchr(const char *str, const char *sep)
assert(str);
assert(sep);
for (; *sep != '\0'; sep++)
if ((match = strchr(str, sep[0])) != NULL
&& str[0] != sep[0]
&& str[strlen(str)-1] != sep[0])
for (; *sep != '\0'; sep++) {
match = strchr(str, sep[0]);
if (match != NULL &&
str[0] != sep[0] &&
str[strlen(str)-1] != sep[0])
return match;
}
return NULL;
}
@@ -178,9 +181,9 @@ ipset_parse_ether(struct ipset_session *session,
number = strtol(str + i * 3, &end, 16);
if (end == str + i * 3 + 2
&& (*end == ':' || *end == '\0')
&& number >= 0 && number <= 255)
if (end == str + i * 3 + 2 &&
(*end == ':' || *end == '\0') &&
number >= 0 && number <= 255)
ether[i] = number;
else
goto error;
@@ -198,9 +201,9 @@ static int
parse_portname(struct ipset_session *session, const char *str,
uint16_t *port, const char *proto)
{
struct servent *service;
struct servent *service = getservbyname(str, proto);
if ((service = getservbyname(str, proto)) != NULL) {
if (service != NULL) {
*port = ntohs((uint16_t) service->s_port);
return 0;
}
@@ -232,8 +235,8 @@ ipset_parse_port(struct ipset_session *session,
assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
assert(str);
if ((err = string_to_u16(session, str, &port)) == 0
|| (err = parse_portname(session, str, &port, proto)) == 0)
if ((err = string_to_u16(session, str, &port)) == 0 ||
(err = parse_portname(session, str, &port, proto)) == 0)
err = ipset_session_data_set(session, opt, &port);
if (!err)
@@ -384,11 +387,12 @@ parse_icmp_typecode(struct ipset_session *session,
if (a == NULL) {
free(saved);
return ipset_err(session,
"Cannot parse %s as an %s type/code.", str, family);
"Cannot parse %s as an %s type/code.",
str, family);
}
*a++ = '\0';
if ((err = string_to_u8(session, a, &type)) != 0
|| (err = string_to_u8(session, tmp, &code)) != 0)
if ((err = string_to_u8(session, a, &type)) != 0 ||
(err = string_to_u8(session, tmp, &code)) != 0)
goto error;
typecode = (type << 8) | code;
@@ -500,30 +504,32 @@ ipset_parse_proto_port(struct ipset_session *session,
p = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
switch (p) {
case IPPROTO_TCP:
proto = tmp;
tmp = a;
goto parse_port;
case IPPROTO_SCTP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
proto = tmp;
tmp = a;
goto parse_port;
case IPPROTO_ICMP:
if (family != AF_INET) {
syntax_err("Protocol ICMP can be used with family INET only");
syntax_err("Protocol ICMP can be used "
"with family INET only");
goto error;
}
err = ipset_parse_icmp(session, opt, a);
break;
case IPPROTO_ICMPV6:
if (family != AF_INET6) {
syntax_err("Protocol ICMPv6 can be used with family INET6 only");
syntax_err("Protocol ICMPv6 can be used "
"with family INET6 only");
goto error;
}
err = ipset_parse_icmpv6(session, opt, a);
break;
default:
if (!STREQ(a, "0")) {
syntax_err("Protocol %s can be used with pseudo port value 0 only.");
syntax_err("Protocol %s can be used "
"with pseudo port value 0 only.");
goto error;
}
ipset_data_flags_set(data, IPSET_FLAG(opt));
@@ -634,13 +640,21 @@ get_addrinfo(struct ipset_session *session,
continue;
if (found == 0) {
if (family == AF_INET) {
/* Workaround: direct cast increases required alignment on Sparc */
const struct sockaddr_in *saddr = (void *)i->ai_addr;
err = ipset_session_data_set(session, opt, &saddr->sin_addr);
/* Workaround: direct cast increases
* required alignment on Sparc
*/
const struct sockaddr_in *saddr =
(void *)i->ai_addr;
err = ipset_session_data_set(session,
opt, &saddr->sin_addr);
} else {
/* Workaround: direct cast increases required alignment on Sparc */
const struct sockaddr_in6 *saddr = (void *)i->ai_addr;
err = ipset_session_data_set(session, opt, &saddr->sin6_addr);
/* Workaround: direct cast increases
* required alignment on Sparc
*/
const struct sockaddr_in6 *saddr =
(void *)i->ai_addr;
err = ipset_session_data_set(session,
opt, &saddr->sin6_addr);
}
} else if (found == 1) {
ipset_warn(session,
@@ -668,8 +682,15 @@ parse_ipaddr(struct ipset_session *session,
char *saved = strdup(str);
char *a, *tmp = saved;
struct addrinfo *info;
enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR
: IPSET_OPT_CIDR2;
enum ipset_opt copt, opt2;
if (opt == IPSET_OPT_IP) {
copt = IPSET_OPT_CIDR;
opt2 = IPSET_OPT_IP_TO;
} else {
copt = IPSET_OPT_CIDR2;
opt2 = IPSET_OPT_IP2_TO;
}
if (tmp == NULL)
return ipset_err(session,
@@ -679,8 +700,8 @@ parse_ipaddr(struct ipset_session *session,
/* IP/mask */
*a++ = '\0';
if ((err = string_to_cidr(session, a, 0, m, &m)) != 0
|| (err = ipset_session_data_set(session, copt, &m)) != 0)
if ((err = string_to_cidr(session, a, 0, m, &m)) != 0 ||
(err = ipset_session_data_set(session, copt, &m)) != 0)
goto out;
} else if ((a = range_separator(tmp)) != NULL) {
/* IP-IP */
@@ -688,11 +709,11 @@ parse_ipaddr(struct ipset_session *session,
D("range %s", a);
range++;
}
if ((aerr = get_addrinfo(session, opt, tmp, &info, family)) != 0
|| !range)
if ((aerr = get_addrinfo(session, opt, tmp, &info, family)) != 0 ||
!range)
goto out;
freeaddrinfo(info);
aerr = get_addrinfo(session, IPSET_OPT_IP_TO, a, &info, family);
aerr = get_addrinfo(session, opt2, a, &info, family);
out:
if (aerr != EINVAL)
@@ -711,6 +732,14 @@ enum ipaddr_type {
IPADDR_RANGE,
};
static inline bool
cidr_hostaddr(const char *str, uint8_t family)
{
char *a = cidr_separator(str);
return family == AF_INET ? STREQ(a, "/32") : STREQ(a, "/128");
}
static int
parse_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str, enum ipaddr_type addrtype)
@@ -725,9 +754,10 @@ parse_ip(struct ipset_session *session,
switch (addrtype) {
case IPADDR_PLAIN:
if (range_separator(str) || cidr_separator(str))
return syntax_err("plain IP address must be supplied: %s",
str);
if (range_separator(str) ||
(cidr_separator(str) && !cidr_hostaddr(str, family)))
return syntax_err("plain IP address must be supplied: "
"%s", str);
break;
case IPADDR_NET:
if (!cidr_separator(str) || range_separator(str))
@@ -791,9 +821,9 @@ ipset_parse_single_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
assert(session);
assert(opt == IPSET_OPT_IP
|| opt == IPSET_OPT_IP_TO
|| opt == IPSET_OPT_IP2);
assert(opt == IPSET_OPT_IP ||
opt == IPSET_OPT_IP_TO ||
opt == IPSET_OPT_IP2);
assert(str);
return parse_ip(session, opt, str, IPADDR_PLAIN);
@@ -836,7 +866,7 @@ ipset_parse_net(struct ipset_session *session,
*/
int
ipset_parse_range(struct ipset_session *session,
enum ipset_opt opt, const char *str)
enum ipset_opt opt ASSERT_UNUSED, const char *str)
{
assert(session);
assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
@@ -867,8 +897,8 @@ ipset_parse_netrange(struct ipset_session *session,
assert(str);
if (!(range_separator(str) || cidr_separator(str)))
return syntax_err("IP/cidr or IP-IP range must be specified: %s",
str);
return syntax_err("IP/cidr or IP-IP range must be specified: "
"%s", str);
return parse_ip(session, opt, str, IPADDR_ANY);
}
@@ -894,8 +924,8 @@ ipset_parse_iprange(struct ipset_session *session,
assert(str);
if (cidr_separator(str))
return syntax_err("IP address or IP-IP range must be specified: %s",
str);
return syntax_err("IP address or IP-IP range must be "
"specified: %s", str);
return parse_ip(session, opt, str, IPADDR_ANY);
}
@@ -965,6 +995,46 @@ ipset_parse_ip4_single6(struct ipset_session *session,
}
/**
* ipset_parse_ip4_net6 - parse IPv4|IPv6 address or address/cidr pattern
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
*
* Parse string as an IPv4|IPv6 address or address/cidr pattern. For IPv4,
* address range is valid too.
* If family is not set yet in the data blob, INET is assumed.
* The values are stored in the data blob of the session.
*
* FIXME: if the hostname resolves to multiple addresses,
* the first one is used only.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_parse_ip4_net6(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
struct ipset_data *data;
uint8_t family;
assert(session);
assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
assert(str);
data = ipset_session_data(session);
family = ipset_data_family(data);
if (family == AF_UNSPEC) {
family = AF_INET;
ipset_data_set(data, IPSET_OPT_FAMILY, &family);
}
return family == AF_INET ? parse_ip(session, opt, str, IPADDR_ANY)
: ipset_parse_ipnet(session, opt, str);
}
/**
* ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout
* @session: session structure
@@ -1020,7 +1090,7 @@ do { \
if (strlen(str) > IPSET_MAXNAMELEN - 1) { \
if (saved != NULL) \
free(saved); \
return syntax_err("setname '%s' is longer than %u characters", \
return syntax_err("setname '%s' is longer than %u characters",\
str, IPSET_MAXNAMELEN - 1); \
} \
} while (0)
@@ -1067,8 +1137,8 @@ ipset_parse_name_compat(struct ipset_session *session,
*a++ = '\0';
if ((b = elem_separator(a)) != NULL)
*b++ = '\0';
if (b == NULL
|| !(STREQ(a, "before") || STREQ(a, "after"))) {
if (b == NULL ||
!(STREQ(a, "before") || STREQ(a, "after"))) {
err = ipset_err(session, "you must specify elements "
"as setname%s[before|after]%ssetname",
sep, sep);
@@ -1109,9 +1179,9 @@ ipset_parse_setname(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
assert(session);
assert(opt == IPSET_SETNAME
|| opt == IPSET_OPT_NAME
|| opt == IPSET_OPT_SETNAME2);
assert(opt == IPSET_SETNAME ||
opt == IPSET_OPT_NAME ||
opt == IPSET_OPT_SETNAME2);
assert(str);
check_setname(str, NULL);
@@ -1313,7 +1383,7 @@ ipset_parse_flag(struct ipset_session *session,
*/
int
ipset_parse_typename(struct ipset_session *session,
enum ipset_opt opt, const char *str)
enum ipset_opt opt ASSERT_UNUSED, const char *str)
{
const struct ipset_type *type;
const char *typename;
@@ -1339,6 +1409,41 @@ ipset_parse_typename(struct ipset_session *session,
return ipset_session_data_set(session, IPSET_OPT_TYPE, type);
}
/**
* ipset_parse_iface - parse string as an interface name
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
*
* Parse string as an interface name, optionally with 'physdev:' prefix.
* The value is stored in the data blob of the session.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_parse_iface(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
struct ipset_data *data;
int offset = 0, err = 0;
assert(session);
assert(opt == IPSET_OPT_IFACE);
assert(str);
data = ipset_session_data(session);
if (STREQ(str, "physdev:")) {
offset = 8;
err = ipset_data_set(data, IPSET_OPT_PHYSDEV, str);
}
if (strlen(str + offset) > IFNAMSIZ - 1)
return syntax_err("interface name '%s' is longer "
"than %u characters",
str + offset, IFNAMSIZ - 1);
return ipset_data_set(data, opt, str + offset);
}
/**
* ipset_parse_output - parse output format name
* @session: session structure
@@ -1387,7 +1492,8 @@ ipset_parse_ignored(struct ipset_session *session,
if (!ipset_data_ignored(ipset_session_data(session), opt))
ipset_warn(session,
"Option %s is ignored. Please upgrade your syntax.", str);
"Option %s is ignored. "
"Please upgrade your syntax.", str);
return 0;
}
@@ -1407,15 +1513,14 @@ ipset_parse_ignored(struct ipset_session *session,
*/
int
ipset_call_parser(struct ipset_session *session,
ipset_parsefn parse, const char *optstr,
enum ipset_opt opt, const char *str)
const struct ipset_arg *arg,
const char *str)
{
if (ipset_data_flags_test(ipset_session_data(session),
IPSET_FLAG(opt)))
syntax_err("%s already specified", optstr);
IPSET_FLAG(arg->opt)))
syntax_err("%s already specified", arg->name[0]);
return parse(session, opt, parse == ipset_parse_ignored
? optstr : str);
return arg->parse(session, arg->opt, str);
}
#define parse_elem(s, t, d, str) \

View File

@@ -12,6 +12,7 @@
#include <sys/socket.h> /* inet_ntop */
#include <arpa/inet.h> /* inet_ntop */
#include <net/ethernet.h> /* ETH_ALEN */
#include <net/if.h> /* IFNAMSIZ */
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_* */
@@ -89,7 +90,8 @@ ipset_print_ether(char *buf, unsigned int len,
*/
int
ipset_print_family(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
const struct ipset_data *data,
enum ipset_opt opt ASSERT_UNUSED,
uint8_t env UNUSED)
{
uint8_t family;
@@ -141,30 +143,56 @@ ipset_print_type(char *buf, unsigned int len,
return snprintf(buf, len, "%s", type->name);
}
#define GETNAMEINFO(family, f, n) \
static inline int \
__getnameinfo##f(char *buf, unsigned int len, \
int flags, const union nf_inet_addr *addr) \
{ \
struct sockaddr_in##n saddr; \
int err; \
\
memset(&saddr, 0, sizeof(saddr)); \
in##f##cpy(&saddr.sin##n##_addr, &addr->in##n); \
saddr.sin##n##_family = family; \
\
err = getnameinfo((const struct sockaddr *)&saddr, \
sizeof(saddr), \
buf, len, NULL, 0, flags); \
\
if (err == EAI_AGAIN && !(flags & NI_NUMERICHOST)) \
err = getnameinfo((const struct sockaddr *)&saddr, \
sizeof(saddr), \
buf, len, NULL, 0, \
flags | NI_NUMERICHOST); \
D("getnameinfo err: %i, errno %i", err, errno); \
return (err == 0 ? (int)strlen(buf) : \
(err == EAI_OVERFLOW || err == EAI_SYSTEM) ? (int)len : -1);\
static inline int
__getnameinfo4(char *buf, unsigned int len,
int flags, const union nf_inet_addr *addr)
{
struct sockaddr_in saddr;
int err;
memset(&saddr, 0, sizeof(saddr));
in4cpy(&saddr.sin_addr, &addr->in);
saddr.sin_family = AF_INET;
err = getnameinfo((const struct sockaddr *)&saddr,
sizeof(saddr),
buf, len, NULL, 0, flags);
if (!(flags & NI_NUMERICHOST) &&
(err == EAI_AGAIN || (err == 0 && strchr(buf, '-') != NULL)))
err = getnameinfo((const struct sockaddr *)&saddr,
sizeof(saddr),
buf, len, NULL, 0,
flags | NI_NUMERICHOST);
D("getnameinfo err: %i, errno %i", err, errno);
return (err == 0 ? (int)strlen(buf) :
(err == EAI_OVERFLOW || err == EAI_SYSTEM) ? (int)len : -1);
}
static inline int
__getnameinfo6(char *buf, unsigned int len,
int flags, const union nf_inet_addr *addr)
{
struct sockaddr_in6 saddr;
int err;
memset(&saddr, 0, sizeof(saddr));
in6cpy(&saddr.sin6_addr, &addr->in6);
saddr.sin6_family = AF_INET6;
err = getnameinfo((const struct sockaddr *)&saddr,
sizeof(saddr),
buf, len, NULL, 0, flags);
if (!(flags & NI_NUMERICHOST) &&
(err == EAI_AGAIN || (err == 0 && strchr(buf, '-') != NULL)))
err = getnameinfo((const struct sockaddr *)&saddr,
sizeof(saddr),
buf, len, NULL, 0,
flags | NI_NUMERICHOST);
D("getnameinfo err: %i, errno %i", err, errno);
return (err == 0 ? (int)strlen(buf) :
(err == EAI_OVERFLOW || err == EAI_SYSTEM) ? (int)len : -1);
}
#define SNPRINTF_IP(mask, f) \
@@ -187,10 +215,8 @@ snprintf_ipv##f(char *buf, unsigned int len, int flags, \
return offset; \
}
GETNAMEINFO(AF_INET, 4, )
SNPRINTF_IP(32, 4)
GETNAMEINFO(AF_INET6, 6, 6)
SNPRINTF_IP(128, 6)
/**
@@ -228,7 +254,7 @@ ipset_print_ip(char *buf, unsigned int len,
D("CIDR: %u", cidr);
} else
cidr = family == AF_INET6 ? 128 : 32;
flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
flags = (env & IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
ip = ipset_data_get(data, opt);
assert(ip);
@@ -285,9 +311,9 @@ ipset_print_ipaddr(char *buf, unsigned int len,
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_IP
|| opt == IPSET_OPT_IP_TO
|| opt == IPSET_OPT_IP2);
assert(opt == IPSET_OPT_IP ||
opt == IPSET_OPT_IP_TO ||
opt == IPSET_OPT_IP2);
family = ipset_data_family(data);
cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
@@ -295,7 +321,7 @@ ipset_print_ipaddr(char *buf, unsigned int len,
cidr = *(const uint8_t *) ipset_data_get(data, cidropt);
else
cidr = family == AF_INET6 ? 128 : 32;
flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
flags = (env & IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
ip = ipset_data_get(data, opt);
assert(ip);
@@ -410,7 +436,8 @@ ipset_print_name(char *buf, unsigned int len,
*/
int
ipset_print_port(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
const struct ipset_data *data,
enum ipset_opt opt ASSERT_UNUSED,
uint8_t env UNUSED)
{
const uint16_t *port;
@@ -440,6 +467,45 @@ ipset_print_port(char *buf, unsigned int len,
return offset;
}
/**
* ipset_print_iface - print interface element string
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print interface element string to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_iface(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
const char *name;
int size, offset = 0;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_IFACE);
if (len < IFNAMSIZ + strlen("physdev:"))
return -1;
if (ipset_data_test(data, IPSET_OPT_PHYSDEV)) {
size = snprintf(buf, len, "physdev:");
SNPRINTF_FAILURE(size, len, offset);
}
name = ipset_data_get(data, opt);
assert(name);
size = snprintf(buf, len, "%s", name);
SNPRINTF_FAILURE(size, len, offset);
return offset;
}
/**
* ipset_print_proto - print protocol name
* @buf: printing buffer
@@ -454,7 +520,8 @@ ipset_print_port(char *buf, unsigned int len,
*/
int
ipset_print_proto(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
const struct ipset_data *data,
enum ipset_opt opt ASSERT_UNUSED,
uint8_t env UNUSED)
{
const struct protoent *protoent;
@@ -490,7 +557,8 @@ ipset_print_proto(char *buf, unsigned int len,
*/
int
ipset_print_icmp(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
const struct ipset_data *data,
enum ipset_opt opt ASSERT_UNUSED,
uint8_t env UNUSED)
{
const char *name;
@@ -506,7 +574,8 @@ ipset_print_icmp(char *buf, unsigned int len,
if (name != NULL)
return snprintf(buf, len, "%s", name);
else
return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF);
return snprintf(buf, len, "%u/%u",
typecode >> 8, typecode & 0xFF);
}
/**
@@ -523,7 +592,8 @@ ipset_print_icmp(char *buf, unsigned int len,
*/
int
ipset_print_icmpv6(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
const struct ipset_data *data,
enum ipset_opt opt ASSERT_UNUSED,
uint8_t env UNUSED)
{
const char *name;
@@ -539,7 +609,8 @@ ipset_print_icmpv6(char *buf, unsigned int len,
if (name != NULL)
return snprintf(buf, len, "%s", name);
else
return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF);
return snprintf(buf, len, "%u/%u",
typecode >> 8, typecode & 0xFF);
}
/**
@@ -556,7 +627,8 @@ ipset_print_icmpv6(char *buf, unsigned int len,
*/
int
ipset_print_proto_port(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
const struct ipset_data *data,
enum ipset_opt opt ASSERT_UNUSED,
uint8_t env UNUSED)
{
int size, offset = 0;
@@ -578,7 +650,9 @@ ipset_print_proto_port(char *buf, unsigned int len,
switch (proto) {
case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
break;
case IPPROTO_ICMP:
return ipset_print_icmp(buf + offset, len, data,
@@ -636,9 +710,9 @@ ipset_print_elem(char *buf, unsigned int len,
SNPRINTF_FAILURE(size, len, offset);
IF_D(ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt),
"print second elem");
if (type->dimension == IPSET_DIM_ONE
|| (type->last_elem_optional
&& !ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt)))
if (type->dimension == IPSET_DIM_ONE ||
(type->last_elem_optional &&
!ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt)))
return offset;
size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
@@ -646,9 +720,9 @@ ipset_print_elem(char *buf, unsigned int len,
size = type->elem[IPSET_DIM_TWO].print(buf + offset, len, data,
type->elem[IPSET_DIM_TWO].opt, env);
SNPRINTF_FAILURE(size, len, offset);
if (type->dimension == IPSET_DIM_TWO
|| (type->last_elem_optional
&& !ipset_data_test(data, type->elem[IPSET_DIM_THREE].opt)))
if (type->dimension == IPSET_DIM_TWO ||
(type->last_elem_optional &&
!ipset_data_test(data, type->elem[IPSET_DIM_THREE].opt)))
return offset;
size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
@@ -722,6 +796,9 @@ ipset_print_data(char *buf, unsigned int len,
case IPSET_OPT_PORT:
size = ipset_print_port(buf, len, data, opt, env);
break;
case IPSET_OPT_IFACE:
size = ipset_print_iface(buf, len, data, opt, env);
break;
case IPSET_OPT_GC:
case IPSET_OPT_HASHSIZE:
case IPSET_OPT_MAXELEM:

View File

@@ -13,6 +13,7 @@
#include <string.h> /* str* */
#include <unistd.h> /* getpagesize */
#include <net/ethernet.h> /* ETH_ALEN */
#include <net/if.h> /* IFNAMSIZ */
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* IPSET_OPT_* */
@@ -37,6 +38,7 @@ struct ipset_session {
/* Command state */
enum ipset_cmd cmd; /* Current command */
uint32_t lineno; /* Current lineno in restore mode */
uint32_t printed_set; /* Printed sets so far */
char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */
const struct ipset_type *saved_type; /* Saved type */
struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */
@@ -100,6 +102,19 @@ ipset_saved_type(const struct ipset_session *session)
return session->saved_type;
}
/**
* ipset_session_lineno - set session lineno
* @session: session structure
*
* Set session lineno to report parser errors correctly.
*/
void
ipset_session_lineno(struct ipset_session *session, uint32_t lineno)
{
assert(session);
session->lineno = lineno;
}
/*
* Environment options
*/
@@ -125,6 +140,8 @@ ipset_envopt_parse(struct ipset_session *session, int opt,
case IPSET_ENV_QUIET:
case IPSET_ENV_RESOLVE:
case IPSET_ENV_EXIST:
case IPSET_ENV_LIST_SETNAME:
case IPSET_ENV_LIST_HEADER:
session->envopts |= opt;
return 0;
default:
@@ -183,7 +200,7 @@ ipset_session_output(struct ipset_session *session,
*
* Returns -1.
*/
int __attribute__((format(printf,3,4)))
int __attribute__((format(printf, 3, 4)))
ipset_session_report(struct ipset_session *session,
enum ipset_err_type type,
const char *fmt, ...)
@@ -455,6 +472,15 @@ static const struct ipset_attr_policy adt_attrs[] = {
.type = MNL_TYPE_U8,
.opt = IPSET_OPT_CIDR2,
},
[IPSET_ATTR_IP2_TO] = {
.type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP2_TO,
},
[IPSET_ATTR_IFACE] = {
.type = MNL_TYPE_NUL_STRING,
.opt = IPSET_OPT_IFACE,
.len = IFNAMSIZ,
},
};
static const struct ipset_attr_policy ipaddr_attrs[] = {
@@ -467,6 +493,10 @@ static const struct ipset_attr_policy ipaddr_attrs[] = {
},
};
#ifdef IPSET_DEBUG
static int debug = 1;
#endif
static int
generic_data_attr_cb(const struct nlattr *attr, void *data,
int attr_max, const struct ipset_attr_policy *policy)
@@ -474,18 +504,18 @@ generic_data_attr_cb(const struct nlattr *attr, void *data,
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
D("attr type: %u, len %u", type, attr->nla_len);
IF_D(debug, "attr type: %u, len %u", type, attr->nla_len);
if (mnl_attr_type_valid(attr, attr_max) < 0) {
D("attr type: %u INVALID", type);
IF_D(debug, "attr type: %u INVALID", type);
return MNL_CB_ERROR;
}
if (mnl_attr_validate(attr, policy[type].type) < 0) {
D("attr type: %u POLICY, attrlen %u", type,
IF_D(debug, "attr type: %u POLICY, attrlen %u", type,
mnl_attr_get_payload_len(attr));
return MNL_CB_ERROR;
}
if (policy[type].type == MNL_TYPE_NUL_STRING
&& mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN)
if (policy[type].type == MNL_TYPE_NUL_STRING &&
mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN)
return MNL_CB_ERROR;
tb[type] = attr;
return MNL_CB_OK;
@@ -635,7 +665,7 @@ call_outfn(struct ipset_session *session)
/* Handle printing failures */
static jmp_buf printf_failure;
static int __attribute__((format(printf,2,3)))
static int __attribute__((format(printf, 2, 3)))
safe_snprintf(struct ipset_session *session, const char *fmt, ...)
{
va_list args;
@@ -816,8 +846,9 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
type->name);
break;
case IPSET_LIST_PLAIN:
safe_snprintf(session, "Name: %s\n"
safe_snprintf(session, "%sName: %s\n"
"Type: %s\nHeader: ",
session->printed_set ? "\n" : "",
ipset_data_setname(data),
type->name);
break;
@@ -834,10 +865,10 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
}
for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->opt; arg++) {
if (!arg->print
|| !ipset_data_test(data, arg->opt)
|| (arg->opt == IPSET_OPT_FAMILY
&& family == type->family))
if (!arg->print ||
!ipset_data_test(data, arg->opt) ||
(arg->opt == IPSET_OPT_FAMILY &&
family == type->family))
continue;
switch (session->mode) {
case IPSET_LIST_SAVE:
@@ -874,18 +905,24 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
safe_snprintf(session, "\nReferences: ");
safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
safe_snprintf(session, "\nMembers:\n");
safe_snprintf(session,
session->envopts & IPSET_ENV_LIST_HEADER ?
"\n" : "\nMembers:\n");
break;
case IPSET_LIST_XML:
safe_snprintf(session, "</elements>\n <memsize>");
safe_snprintf(session, " <memsize>");
safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
safe_snprintf(session, "</memsize>\n <references>");
safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
safe_snprintf(session, "</references>\n </header>\n <members>\n");
safe_snprintf(session,
session->envopts & IPSET_ENV_LIST_HEADER ?
"</references>\n </header>\n" :
"</references>\n </header>\n <members>\n");
break;
default:
break;
}
session->printed_set++;
return MNL_CB_OK;
}
@@ -897,16 +934,17 @@ print_set_done(struct ipset_session *session)
? "NONE" : session->saved_setname);
switch (session->mode) {
case IPSET_LIST_XML:
if (session->saved_setname[0] == '\0')
safe_snprintf(session, "\n");
else
if (session->envopts & IPSET_ENV_LIST_SETNAME)
break;
if (session->envopts & IPSET_ENV_LIST_HEADER) {
if (session->saved_setname[0] != '\0')
safe_snprintf(session, "</ipset>\n");
break;
}
if (session->saved_setname[0] != '\0')
safe_snprintf(session, " </members>\n</ipset>\n");
break;
case IPSET_LIST_SAVE:
/* No empty lines between the sets */
break;
default:
safe_snprintf(session, "\n");
break;
}
return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
@@ -918,8 +956,11 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
{
struct ipset_data *data = session->data;
if (setjmp(printf_failure))
if (setjmp(printf_failure)) {
session->saved_setname[0] = '\0';
session->printed_set = 0;
return MNL_CB_ERROR;
}
if (!nla[IPSET_ATTR_SETNAME])
FAILURE("Broken %s kernel message: missing setname!",
@@ -927,10 +968,21 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs);
D("setname %s", ipset_data_setname(data));
if (session->envopts & IPSET_ENV_LIST_SETNAME &&
session->mode != IPSET_LIST_SAVE) {
if (session->mode == IPSET_LIST_XML)
safe_snprintf(session, "<ipset name=\"%s\"/>\n",
ipset_data_setname(data));
else
safe_snprintf(session, "%s\n",
ipset_data_setname(data));
return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK;
}
if (STREQ(ipset_data_setname(data), session->saved_setname)) {
/* Header part already seen */
if (ipset_data_test(data, IPSET_OPT_TYPE)
&& nla[IPSET_ATTR_DATA] != NULL)
if (ipset_data_test(data, IPSET_OPT_TYPE) &&
nla[IPSET_ATTR_DATA] != NULL)
FAILURE("Broken %s kernel message: "
"extra DATA received!", cmd2name[cmd]);
} else {
@@ -946,13 +998,14 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
if (nla[IPSET_ATTR_DATA] != NULL) {
struct nlattr *cattr[IPSET_ATTR_CREATE_MAX+1] = {};
if (!(nla[IPSET_ATTR_TYPENAME]
&& nla[IPSET_ATTR_FAMILY]
&& nla[IPSET_ATTR_REVISION]))
if (!(nla[IPSET_ATTR_TYPENAME] &&
nla[IPSET_ATTR_FAMILY] &&
nla[IPSET_ATTR_REVISION]))
FAILURE("Broken %s kernel message: missing %s!",
cmd2name[cmd],
!nla[IPSET_ATTR_TYPENAME] ? "typename" :
!nla[IPSET_ATTR_FAMILY] ? "family" : "revision");
!nla[IPSET_ATTR_FAMILY] ? "family" :
"revision");
/* Reset CREATE specific flags */
ipset_data_flags_unset(data, IPSET_CREATE_FLAGS);
@@ -1021,8 +1074,8 @@ callback_version(struct ipset_session *session, struct nlattr *nla[])
"while userspace supports protocol versions %u-%u",
min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX);
if (!(session->envopts & IPSET_ENV_QUIET)
&& max != IPSET_PROTOCOL_MAX)
if (!(session->envopts & IPSET_ENV_QUIET) &&
max != IPSET_PROTOCOL_MAX)
ipset_warn(session,
"Kernel support protocol versions %u-%u "
"while userspace supports protocol versions %u-%u",
@@ -1048,9 +1101,9 @@ callback_header(struct ipset_session *session, struct nlattr *nla[])
"does not match with received one `%s'!",
ipset_data_setname(data), setname);
if (!(nla[IPSET_ATTR_TYPENAME]
&& nla[IPSET_ATTR_REVISION]
&& nla[IPSET_ATTR_FAMILY]))
if (!(nla[IPSET_ATTR_TYPENAME] &&
nla[IPSET_ATTR_REVISION] &&
nla[IPSET_ATTR_FAMILY]))
FAILURE("Broken HEADER kernel message: "
"missing attribute '%s'!",
!nla[IPSET_ATTR_TYPENAME] ? "typename" :
@@ -1071,9 +1124,9 @@ callback_type(struct ipset_session *session, struct nlattr *nla[])
const struct ipset_data *data = session->data;
const char *typename, *orig;
if (!(nla[IPSET_ATTR_TYPENAME]
&& nla[IPSET_ATTR_REVISION]
&& nla[IPSET_ATTR_FAMILY]))
if (!(nla[IPSET_ATTR_TYPENAME] &&
nla[IPSET_ATTR_REVISION] &&
nla[IPSET_ATTR_FAMILY]))
FAILURE("Broken TYPE kernel message: "
"missing attribute '%s'!",
!nla[IPSET_ATTR_TYPENAME] ? "typename" :
@@ -1272,16 +1325,20 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata)
break;
case IPSET_CMD_RENAME:
ipset_cache_rename(ipset_data_setname(data),
ipset_data_get(data, IPSET_OPT_SETNAME2));
ipset_data_get(data,
IPSET_OPT_SETNAME2));
break;
case IPSET_CMD_SWAP:
ipset_cache_swap(ipset_data_setname(data),
ipset_data_get(data, IPSET_OPT_SETNAME2));
ipset_data_get(data,
IPSET_OPT_SETNAME2));
break;
case IPSET_CMD_TEST:
if (!(session->envopts & IPSET_ENV_QUIET)) {
ipset_print_elem(session->report, IPSET_ERRORBUFLEN,
session->data, IPSET_OPT_NONE, 0);
ipset_print_elem(session->report,
IPSET_ERRORBUFLEN,
session->data,
IPSET_OPT_NONE, 0);
ipset_warn(session, " is in set %s.",
ipset_data_setname(data));
}
@@ -1295,7 +1352,8 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata)
print_set_done(session);
break;
default:
FAILURE("ACK message received to command %s[%u], which is not expected",
FAILURE("ACK message received to command %s[%u], "
"which is not expected",
session->cmd < IPSET_MSG_MAX
? cmd2name[session->cmd] : "unknown",
session->cmd);
@@ -1307,8 +1365,8 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata)
/* Error messages */
/* Special case for IPSET_CMD_TEST */
if (session->cmd == IPSET_CMD_TEST
&& err->error == -IPSET_ERR_EXIST) {
if (session->cmd == IPSET_CMD_TEST &&
err->error == -IPSET_ERR_EXIST) {
if (!(session->envopts & IPSET_ENV_QUIET)) {
ipset_print_elem(session->report, IPSET_ERRORBUFLEN,
session->data, IPSET_OPT_NONE, 0);
@@ -1373,7 +1431,8 @@ attr_len(const struct ipset_attr_policy *attr, uint8_t family, uint16_t *flags)
}
#define BUFFER_FULL(bufsize, nlmsg_len, nestlen, attrlen) \
(nlmsg_len + nestlen + MNL_ATTR_HDRLEN + MNL_ALIGN(alen) + MNL_ALIGN(sizeof(struct nlmsgerr)) > bufsize)
(nlmsg_len + nestlen + MNL_ATTR_HDRLEN + MNL_ALIGN(alen) + \
MNL_ALIGN(sizeof(struct nlmsgerr)) > bufsize)
static int
rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
@@ -1393,7 +1452,8 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
: IPSET_ATTR_IPADDR_IPV6;
alen = attr_len(attr, family, &flags);
if (BUFFER_FULL(session->bufsize, nlh->nlmsg_len, MNL_ATTR_HDRLEN, alen))
if (BUFFER_FULL(session->bufsize, nlh->nlmsg_len,
MNL_ATTR_HDRLEN, alen))
return 1;
nested = mnl_attr_nest_start(nlh, type);
D("family: %s", family == AF_INET ? "INET" :
@@ -1409,6 +1469,9 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
return 1;
switch (attr->type) {
case MNL_TYPE_NUL_STRING:
alen = strlen((const char *)d) + 1;
break;
case MNL_TYPE_U32: {
uint32_t value = htonl(*(const uint32_t *)d);
@@ -1484,7 +1547,7 @@ addattr_adt(struct ipset_session *session,
static int
build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
{
char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned));
char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned)) = {};
struct nlmsghdr *nlh = (void *)buffer;
struct ipset_data *data = session->data;
int len = PRIVATE_MSG_BUFLEN, ret;
@@ -1510,9 +1573,11 @@ build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
return ipset_err(session,
"Invalid internal TYPE command: "
"missing settype");
ADDATTR(session, nlh, data, IPSET_ATTR_TYPENAME, AF_INET, cmd_attrs);
ADDATTR(session, nlh, data, IPSET_ATTR_TYPENAME,
AF_INET, cmd_attrs);
if (ipset_data_test(data, IPSET_OPT_FAMILY))
ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY, AF_INET, cmd_attrs);
ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY,
AF_INET, cmd_attrs);
else
/* bitmap:port and list:set types */
mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, AF_UNSPEC);
@@ -1533,10 +1598,10 @@ build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
static inline bool
may_aggregate_ad(struct ipset_session *session, enum ipset_cmd cmd)
{
return session->lineno != 0
&& (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)
&& cmd == session->cmd
&& STREQ(ipset_data_setname(session->data), session->saved_setname);
return session->lineno != 0 &&
(cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL) &&
cmd == session->cmd &&
STREQ(ipset_data_setname(session->data), session->saved_setname);
}
static int
@@ -1574,12 +1639,18 @@ build_msg(struct ipset_session *session, bool aggregate)
/* Core attributes:
* setname, typename, revision, family, flags (optional) */
ADDATTR_SETNAME(session, nlh, data);
ADDATTR(session, nlh, data, IPSET_ATTR_TYPENAME, AF_INET, cmd_attrs);
ADDATTR(session, nlh, data, IPSET_ATTR_TYPENAME,
AF_INET, cmd_attrs);
ADDATTR_RAW(session, nlh, &type->revision,
IPSET_ATTR_REVISION, cmd_attrs);
D("family: %u, type family %u",
ipset_data_family(data), type->family);
ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY, AF_INET, cmd_attrs);
if (ipset_data_test(data, IPSET_OPT_FAMILY))
ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY,
AF_INET, cmd_attrs);
else
/* bitmap:port and list:set types */
mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, AF_UNSPEC);
/* Type-specific create attributes */
D("call open_nested");
@@ -1591,23 +1662,41 @@ build_msg(struct ipset_session *session, bool aggregate)
}
case IPSET_CMD_DESTROY:
case IPSET_CMD_FLUSH:
case IPSET_CMD_LIST:
case IPSET_CMD_SAVE:
if (ipset_data_test(data, IPSET_SETNAME))
ADDATTR_SETNAME(session, nlh, data);
break;
case IPSET_CMD_LIST: {
uint32_t flags = 0;
if (session->envopts & IPSET_ENV_LIST_SETNAME)
flags |= IPSET_FLAG_LIST_SETNAME;
if (session->envopts & IPSET_ENV_LIST_HEADER)
flags |= IPSET_FLAG_LIST_HEADER;
if (ipset_data_test(data, IPSET_SETNAME))
ADDATTR_SETNAME(session, nlh, data);
if (flags && session->mode != IPSET_LIST_SAVE) {
ipset_data_set(data, IPSET_OPT_FLAGS, &flags);
ADDATTR(session, nlh, data, IPSET_ATTR_FLAGS, AF_INET,
cmd_attrs);
}
break;
}
case IPSET_CMD_RENAME:
case IPSET_CMD_SWAP:
if (!ipset_data_test(data, IPSET_SETNAME))
return ipset_err(session,
"Invalid %s command: missing from-setname",
session->cmd == IPSET_CMD_SWAP ? "swap" : "rename");
session->cmd == IPSET_CMD_SWAP ? "swap" :
"rename");
if (!ipset_data_test(data, IPSET_OPT_SETNAME2))
return ipset_err(session,
"Invalid %s command: missing to-setname",
session->cmd == IPSET_CMD_SWAP ? "swap" : "rename");
session->cmd == IPSET_CMD_SWAP ? "swap" :
"rename");
ADDATTR_SETNAME(session, nlh, data);
ADDATTR_RAW(session, nlh, ipset_data_get(data, IPSET_OPT_SETNAME2),
ADDATTR_RAW(session, nlh,
ipset_data_get(data, IPSET_OPT_SETNAME2),
IPSET_ATTR_SETNAME2, cmd_attrs);
break;
case IPSET_CMD_ADD:
@@ -1619,12 +1708,14 @@ build_msg(struct ipset_session *session, bool aggregate)
if (!ipset_data_test(data, IPSET_SETNAME))
return ipset_err(session,
"Invalid %s command: missing setname",
session->cmd == IPSET_CMD_ADD ? "add" : "del");
session->cmd == IPSET_CMD_ADD ? "add" :
"del");
if (!ipset_data_test(data, IPSET_OPT_TYPE))
return ipset_err(session,
"Invalid %s command: missing settype",
session->cmd == IPSET_CMD_ADD ? "add" : "del");
session->cmd == IPSET_CMD_ADD ? "add" :
"del");
/* Core options: setname */
ADDATTR_SETNAME(session, nlh, data);
@@ -1642,11 +1733,12 @@ build_msg(struct ipset_session *session, bool aggregate)
D("open_nested failed");
return 1;
}
if (addattr_adt(session, nlh, data, ipset_data_family(data))
|| ADDATTR_RAW(session, nlh, &session->lineno,
if (addattr_adt(session, nlh, data, ipset_data_family(data)) ||
ADDATTR_RAW(session, nlh, &session->lineno,
IPSET_ATTR_LINENO, cmd_attrs)) {
/* Cancel last, unfinished nested attribute */
mnl_attr_nest_cancel(nlh, session->nested[session->nestid-1]);
mnl_attr_nest_cancel(nlh,
session->nested[session->nestid-1]);
session->nested[--session->nestid] = NULL;
return 1;
}
@@ -1700,7 +1792,8 @@ ipset_commit(struct ipset_session *session)
assert(session);
nlh = session->buffer;
D("send buffer: len %u, cmd %s", nlh->nlmsg_len, cmd2name[session->cmd]);
D("send buffer: len %u, cmd %s",
nlh->nlmsg_len, cmd2name[session->cmd]);
if (nlh->nlmsg_len == 0)
/* Nothing to do */
return 0;
@@ -1716,6 +1809,7 @@ ipset_commit(struct ipset_session *session)
/* Reset saved data and nested state */
session->saved_setname[0] = '\0';
session->printed_set = 0;
for (i = session->nestid - 1; i >= 0; i--)
session->nested[i] = NULL;
session->nestid = 0;
@@ -1832,8 +1926,8 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
/* We have to save the type for error handling */
session->saved_type = ipset_data_get(data, IPSET_OPT_TYPE);
if (session->lineno != 0
&& (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) {
if (session->lineno != 0 &&
(cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) {
/* Save setname for the next possible aggregated restore line */
strcpy(session->saved_setname, ipset_data_setname(data));
ipset_data_reset(data);
@@ -1913,3 +2007,7 @@ ipset_session_fini(struct ipset_session *session)
free(session);
return 0;
}
#ifdef IPSET_DEBUG
#include "debug.c"
#endif

View File

@@ -28,8 +28,8 @@ struct ipset {
struct ipset *next;
};
static struct ipset_type *typelist = NULL; /* registered set types */
static struct ipset *setlist = NULL; /* cached sets */
static struct ipset_type *typelist; /* registered set types */
static struct ipset *setlist; /* cached sets */
/**
* ipset_cache_add - add a set to the cache
@@ -178,7 +178,7 @@ ipset_cache_swap(const char *from, const char *to)
bool
ipset_match_typename(const char *name, const struct ipset_type *type)
{
const char * const * alias = type->alias;
const char * const *alias = type->alias;
if (STREQ(name, type->name))
return true;
@@ -198,7 +198,7 @@ create_type_get(struct ipset_session *session)
struct ipset_data *data;
const char *typename;
uint8_t family, tmin = 0, tmax = 0;
const uint8_t *kmin, *kmax;
uint8_t kmin, kmax;
int ret;
data = ipset_session_data(session);
@@ -216,7 +216,7 @@ create_type_get(struct ipset_session *session)
&& MATCH_FAMILY(t, family)) {
if (match == NULL) {
match = t;
tmax = t->revision;
tmin = tmax = t->revision;
} else if (t->family == match->family)
tmin = t->revision;
}
@@ -240,34 +240,48 @@ create_type_get(struct ipset_session *session)
if (ret != 0)
return NULL;
kmax = ipset_data_get(data, IPSET_OPT_REVISION);
kmin = kmax = *(const uint8_t *)ipset_data_get(data,
IPSET_OPT_REVISION);
if (ipset_data_test(data, IPSET_OPT_REVISION_MIN))
kmin = ipset_data_get(data, IPSET_OPT_REVISION_MIN);
else
kmin = kmax;
if (MAX(tmin, *kmin) > MIN(tmax, *kmax)) {
if (*kmin > tmax)
kmin = *(const uint8_t *)ipset_data_get(data,
IPSET_OPT_REVISION_MIN);
if (MAX(tmin, kmin) > MIN(tmax, kmax)) {
if (kmin > tmax)
return ipset_errptr(session,
"Kernel supports %s type with family %s "
"in minimal revision %u while ipset library "
"in maximal revision %u. "
"You need to upgrade your ipset library.",
"Kernel supports %s type, family %s "
"with minimal revision %u while ipset program "
"with maximal revision %u.\n"
"You need to upgrade your ipset program.",
typename,
family == AF_INET ? "INET" :
family == AF_INET6 ? "INET6" : "UNSPEC",
*kmin, tmax);
kmin, tmax);
else
return ipset_errptr(session,
"Kernel supports %s type with family %s "
"in maximal revision %u while ipset library "
"in minimal revision %u. "
"Kernel supports %s type, family %s "
"with maximal revision %u while ipset program "
"with minimal revision %u.\n"
"You need to upgrade your kernel.",
typename,
family == AF_INET ? "INET" :
family == AF_INET6 ? "INET6" : "UNSPEC",
*kmax, tmin);
kmax, tmin);
}
/* Disable unsupported revisions */
for (match = NULL, t = typelist; t != NULL; t = t->next) {
/* Skip revisions which are unsupported by the kernel */
if (t->kernel_check == IPSET_KERNEL_MISMATCH)
continue;
if (ipset_match_typename(typename, t)
&& MATCH_FAMILY(t, family)) {
if (t->revision < kmin || t->revision > kmax)
t->kernel_check = IPSET_KERNEL_MISMATCH;
else if (match == NULL)
match = t;
}
}
match->kernel_check = IPSET_KERNEL_OK;
found:
ipset_data_set(data, IPSET_OPT_TYPE, match);
@@ -441,13 +455,15 @@ ipset_type_add(struct ipset_type *type)
assert(type);
if (strlen(type->name) > IPSET_MAXNAMELEN - 1)
return -EINVAL;
/* Add to the list: higher revision numbers first */
for (t = typelist, prev = NULL; t != NULL; t = t->next) {
if (STREQ(t->name, type->name)) {
if (t->revision == type->revision) {
errno = EEXIST;
return -1;
} else if (t->revision < type->revision) {
if (t->revision == type->revision)
return -EEXIST;
else if (t->revision < type->revision) {
type->next = t;
if (prev)
prev->next = type;
@@ -457,10 +473,9 @@ ipset_type_add(struct ipset_type *type)
}
}
if (t->next != NULL && STREQ(t->next->name, type->name)) {
if (t->next->revision == type->revision) {
errno = EEXIST;
return -1;
} else if (t->next->revision < type->revision) {
if (t->next->revision == type->revision)
return -EEXIST;
else if (t->next->revision < type->revision) {
type->next = t->next;
t->next = type;
return 0;

312
extensions/ipset-6/pfxlen.c Normal file
View File

@@ -0,0 +1,312 @@
#include "pfxlen.h"
/*
* Prefixlen maps for fast conversions, by Jan Engelhardt.
*/
#define E(a, b, c, d) \
{.ip6 = { \
__constant_htonl(a), __constant_htonl(b), \
__constant_htonl(c), __constant_htonl(d), \
} }
/*
* This table works for both IPv4 and IPv6;
* just use prefixlen_netmask_map[prefixlength].ip.
*/
const union nf_inet_addr ip_set_netmask_map[] = {
E(0x00000000, 0x00000000, 0x00000000, 0x00000000),
E(0x80000000, 0x00000000, 0x00000000, 0x00000000),
E(0xC0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xE0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xF0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xF8000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFC000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFE000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFF000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFF800000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF),
};
EXPORT_SYMBOL_GPL(ip_set_netmask_map);
#undef E
#define E(a, b, c, d) \
{.ip6 = { (__force __be32) a, (__force __be32) b, \
(__force __be32) c, (__force __be32) d, \
} }
/*
* This table works for both IPv4 and IPv6;
* just use prefixlen_hostmask_map[prefixlength].ip.
*/
const union nf_inet_addr ip_set_hostmask_map[] = {
E(0x00000000, 0x00000000, 0x00000000, 0x00000000),
E(0x80000000, 0x00000000, 0x00000000, 0x00000000),
E(0xC0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xE0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xF0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xF8000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFC000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFE000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFF000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFF800000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF),
};
EXPORT_SYMBOL_GPL(ip_set_hostmask_map);
/* Find the largest network which matches the range from left, in host order. */
u32
ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr)
{
u32 last;
u8 i;
for (i = 1; i < 32; i++) {
if ((from & ip_set_hostmask(i)) != from)
continue;
last = from | ~ip_set_hostmask(i);
if (!after(last, to)) {
*cidr = i;
return last;
}
}
*cidr = 32;
return from;
}
EXPORT_SYMBOL_GPL(ip_set_range_to_cidr);

View File

@@ -0,0 +1,44 @@
#ifndef _PFXLEN_H
#define _PFXLEN_H
#include <asm/byteorder.h>
#include <linux/netfilter.h>
#include <net/tcp.h>
/* Prefixlen maps, by Jan Engelhardt */
extern const union nf_inet_addr ip_set_netmask_map[];
extern const union nf_inet_addr ip_set_hostmask_map[];
static inline __be32
ip_set_netmask(u8 pfxlen)
{
return ip_set_netmask_map[pfxlen].ip;
}
static inline const __be32 *
ip_set_netmask6(u8 pfxlen)
{
return &ip_set_netmask_map[pfxlen].ip6[0];
}
static inline u32
ip_set_hostmask(u8 pfxlen)
{
return (__force u32) ip_set_hostmask_map[pfxlen].ip;
}
static inline const __be32 *
ip_set_hostmask6(u8 pfxlen)
{
return &ip_set_hostmask_map[pfxlen].ip6[0];
}
extern u32 ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr);
#define ip_set_mask_from_to(from, to, cidr) \
do { \
from &= ip_set_hostmask(cidr); \
to = from | ~ip_set_hostmask(cidr); \
} while (0)
#endif /*_PFXLEN_H */

View File

@@ -21,8 +21,10 @@
/* Core kernel error codes */
static const struct ipset_errcode_table core_errcode_table[] = {
/* Generic error codes */
{ EEXIST, 0,
{ ENOENT, 0,
"The set with the given name does not exist" },
{ EMSGSIZE, 0,
"Kernel error received: message could not be created" },
{ IPSET_ERR_PROTOCOL, 0,
"Kernel error received: ipset protocol error" },
@@ -30,14 +32,14 @@ static const struct ipset_errcode_table core_errcode_table[] = {
{ EEXIST, IPSET_CMD_CREATE,
"Set cannot be created: set with the same name already exists" },
{ IPSET_ERR_FIND_TYPE, 0,
"Kernel error received: set type does not supported" },
"Kernel error received: set type not supported" },
{ IPSET_ERR_MAX_SETS, 0,
"Kernel error received: maximal number of sets reached, "
"cannot create more." },
{ IPSET_ERR_INVALID_NETMASK, 0,
"The value of the netmask parameter is invalid" },
{ IPSET_ERR_INVALID_FAMILY, 0,
"The protocol family not supported by the set type" },
"Protocol family not supported by the set type" },
/* DESTROY specific error codes */
{ IPSET_ERR_BUSY, IPSET_CMD_DESTROY,
@@ -111,6 +113,10 @@ static const struct ipset_errcode_table hash_errcode_table[] = {
"Invalid protocol specified" },
{ IPSET_ERR_MISSING_PROTO, 0,
"Protocol missing, but must be specified" },
{ IPSET_ERR_HASH_RANGE_UNSUPPORTED, 0,
"Range is not supported in the \"net\" component of the element" },
{ IPSET_ERR_HASH_RANGE, 0,
"Invalid range, covers the whole address space" },
{ },
};
@@ -168,8 +174,8 @@ ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode)
retry:
for (i = 0, generic = -1; table[i].errcode; i++) {
if (table[i].errcode == errcode
&& (table[i].cmd == cmd || table[i].cmd == 0)) {
if (table[i].errcode == errcode &&
(table[i].cmd == cmd || table[i].cmd == 0)) {
if (table[i].cmd == 0) {
generic = i;
continue;

View File

@@ -21,7 +21,7 @@ ipset \(em administration tool for IP sets
.PP
COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR | \fBlist\fR | \fBsave\fR | \fBrestore\fR | \fBflush\fR | \fBrename\fR | \fBswap\fR | \fBhelp\fR | \fBversion\fR | \fB\-\fR }
.PP
\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR }
\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR | \fB\-name\fR | \fB\-terse\fR }
.PP
\fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ]
.PP
@@ -109,7 +109,7 @@ Destroy the specified set or all the sets if none is given.
If the set has got reference(s), nothing is done and no set destroyed.
.TP
\fBlist\fP [ \fISETNAME\fP ]
\fBlist\fP [ \fISETNAME\fP ] [ \fIOPTIONS\fP ]
List the header data and the entries for the specified set, or for
all sets if none is given. The
\fB\-resolve\fP
@@ -120,8 +120,13 @@ type supports the operation). The option
\fB\-output\fR
can be used to control the format of the listing:
\fBplain\fR, \fBsave\fR or \fBxml\fR.
The default is
\fBplain\fR.
(The default is
\fBplain\fR.)
If the option
\fB\-name\fR
is specified, just the names of the existing sets are listed. If the option
\fB\-terse\fR
is specified, just the set names and headers are listed.
.TP
\fBsave\fP [ \fISETNAME\fP ]
Save the given set, or all sets if none is given
@@ -190,6 +195,13 @@ DNS lookups.
.TP
\fB\-s\fP, \fB\-sorted\fP
Sorted output. When listing sets entries are listed sorted. Not supported yet.
.TP
\fB\-n\fP, \fB\-name\fP
List just the names of the existing sets, i.e. suppress listing of set headers and members.
.TP
\fB\-t\fP, \fB\-terse\fP
List the set names and headers, i.e. suppress listing of set members.
.SH "SET TYPES"
A set type comprises of the storage method by which the data is stored and
the data type(s) which are stored in the set. Therefore the
@@ -202,8 +214,8 @@ command follows the syntax
where the current list of the methods are
\fBbitmap\fR, \fBhash\fR, and \fBlist\fR and the possible data types
are \fBip\fR, \fBmac\fR and \fBport\fR. The dimension of a set
is equal to the number of data types in its type name.
are \fBip\fR, \fBnet\fR, \fBmac\fR, \fBport\fR and \fBiface\fR.
The dimension of a set is equal to the number of data types in its type name.
When adding, deleting or testing entries in a set, the same comma separated
data syntax must be used for the entry parameter of the commands, i.e
@@ -231,6 +243,8 @@ parameter for the \fBcreate\fR command means the default timeout value (in secon
for new entries. If a set is created with timeout support, then the same
\fBtimeout\fR option can be used to specify non\-default timeout values
when adding entries. Zero timeout value means the entry is added permanent to the set.
The timeout value of already added elements can be changed by readding the element
using the \fB\-exist\fR option.
.SS bitmap:ip
The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host
(default) or IPv4 network addresses. A \fBbitmap:ip\fR type of set can store up
@@ -300,9 +314,10 @@ matched by the kernel, it will automatically fill out the missing MAC address wi
source MAC address from the packet. If the entry was specified with a timeout value,
the timer starts off when the IP and MAC address pair is complete.
.PP
Please note, the \fBset\fR match and \fBSET\fR target netfilter kernel modules
\fBalways\fR use the source MAC address from the packet to match, add or delete
entries from a \fBbitmap:ip,mac\fR type of set.
The \fBbitmap:ip,mac\fR type of sets require two \fBsrc/dst\fR parameters of
the \fBset\fR match and \fBSET\fR target netfilter kernel modules and the second
one must be \fBsrc\fR to match, add or delete entries because the \fBset\fR match
and \fBSET\fR target have access to the source MAC address only.
.PP
Examples:
.IP
@@ -330,6 +345,9 @@ Mandatory options to use when creating a \fBbitmap:port\fR type of set:
\fBrange\fP \fIfromport\fP\-\fItoport\fR
Create the set from the specified inclusive port range.
.PP
The \fBset\fR match and \fBSET\fR target netfilter kernel modules interpret
the stored numbers as TCP or UDP port numbers.
.PP
Examples:
.IP
ipset create foo bitmap:port range 0\-1024
@@ -380,9 +398,9 @@ a range or a network:
.PP
Examples:
.IP
ipset create foo hash:ip netmask 24
ipset create foo hash:ip netmask 30
.IP
ipset add foo 192.168.1.1\-192.168.1.2
ipset add foo 192.168.1.0/24
.IP
ipset test foo 192.168.1.2
.SS hash:net
@@ -391,13 +409,16 @@ Network address with zero prefix size cannot be stored in this type of sets.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
\fIADD\-ENTRY\fR := \fInetaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
\fIDEL\-ENTRY\fR := \fInetaddr\fR
.PP
\fITEST\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
\fITEST\-ENTRY\fR := \fInetaddr\fR
.PP
where
\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
@@ -413,9 +434,16 @@ correct value.
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
For the \fBinet\fR family one can add or delete multiple entries by specifying
a range, which is converted internally to network(s) equal to the range:
.PP
\fInetaddr\fR := { \fIip\fR[/\fIcidr\fR] | \fIfromaddr\fR\-\fItoaddr\fR }
.PP
When adding/deleting/testing entries, if the cidr prefix parameter is not specified,
then the host prefix value is assumed. When adding/deleting entries, overlapping
elements are not checked.
then the host prefix value is assumed. When adding/deleting entries, the exact
element is added/deleted and overlapping elements are not checked by the kernel.
When testing entries, if a host address is tested, then the kernel tries to match
the host address in the networks added to the set and reports the result accordingly.
.PP
From the \fBset\fR netfilter match point of view the searching for a match
always starts from the smallest size of netblock (most specific
@@ -431,7 +459,7 @@ Examples:
.IP
ipset create foo hash:net
.IP
ipset add foo 192.168.0/24
ipset add foo 192.168.0.0/24
.IP
ipset add foo 10.1.0.0/16
.IP
@@ -481,8 +509,8 @@ TCP port or range of ports expressed in TCP portname identifiers from /etc/servi
\fIportnumber[\-portnumber]\fR
TCP port or range of ports expressed in TCP port numbers
.TP
\fBtcp\fR|\fBudp\fR:\fIportname\fR|\fIportnumber\fR[\-\fIportname\fR|\fIportnumber\fR]
TCP or UDP port or port range expressed in port name(s) or port number(s)
\fBtcp\fR|\fBsctp\fR|\fBudp\fR|\fBudplite\fR:\fIportname\fR|\fIportnumber\fR[\-\fIportname\fR|\fIportnumber\fR]
TCP, SCTP, UDP or UDPLITE port or port range expressed in port name(s) or port number(s)
.TP
\fBicmp\fR:\fIcodename\fR|\fItype\fR/\fIcode\fR
ICMP codename or type/code. The supported ICMP codename identifiers can always
@@ -508,7 +536,7 @@ ipset add foo 192.168.1.0/24,80\-82
.IP
ipset add foo 192.168.1.1,udp:53
.IP
ipset add foo 192.168.1.1,ospf:0
ipset add foo 192.168.1.1,vrrp:0
.IP
ipset test foo 192.168.1.1,80
.SS hash:net,port
@@ -519,13 +547,16 @@ address with zero prefix size is not accepted either.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
\fIADD\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
\fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
\fITEST\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
\fITEST\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
where
\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
@@ -541,14 +572,17 @@ correct value.
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
For the
For the \fInetaddr\fR part of the elements
see the description at the \fBhash:net\fR set type. For the
[\fIproto\fR:]\fIport\fR
part of the elements see the description at the
\fBhash:ip,port\fR set type.
.PP
When adding/deleting/testing entries, if the cidr prefix parameter is not specified,
then the host prefix value is assumed. When adding/deleting entries, overlapping
elements are not checked.
then the host prefix value is assumed. When adding/deleting entries, the exact
element is added/deleted and overlapping elements are not checked by the kernel.
When testing entries, if a host address is tested, then the kernel tries to match
the host address in the networks added to the set and reports the result accordingly.
.PP
From the \fBset\fR netfilter match point of view the searching for a match
always starts from the smallest size of netblock (most specific
@@ -623,18 +657,22 @@ address with zero prefix size cannot be stored either.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
For the first \fIipaddr\fR and
where
\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
For the \fIipaddr\fR and
[\fIproto\fR:]\fIport\fR
parts of the elements see the descriptions at the
\fBhash:ip,port\fR set type.
\fBhash:ip,port\fR set type. For the \fInetaddr\fR part of the elements
see the description at the \fBhash:net\fR set type.
.PP
Optional \fBcreate\fR options:
.TP
@@ -673,6 +711,73 @@ ipset add foo 192.168.1,80,10.0.0/24
ipset add foo 192.168.2,25,10.1.0.0/16
.IP
ipset test foo 192.168.1,80.10.0.0/24
.SS hash:net,iface
The \fBhash:net,iface\fR set type uses a hash to store different sized IP network
address and interface name pairs. Network address with zero prefix size is not
accepted.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fInetaddr\fR,[\fBphysdev\fR:]\fIiface\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fBphysdev\fR:]\fIiface\fR
.PP
\fITEST\-ENTRY\fR := \fInetaddr\fR,[\fBphysdev\fR:]\fIiface\fR
.PP
where
\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
\fBfamily\fR { \fBinet\fR | \fBinet6\fR }
The protocol family of the IP addresses to be stored in the set. The default is
\fBinet\fR, i.e IPv4.
.TP
\fBhashsize\fR \fIvalue\fR
The initial hash size for the set, default is 1024. The hash size must be a power
of two, the kernel automatically rounds up non power of two hash sizes to the first
correct value.
.TP
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
For the \fInetaddr\fR part of the elements
see the description at the \fBhash:net\fR set type.
.PP
When adding/deleting/testing entries, if the cidr prefix parameter is not specified,
then the host prefix value is assumed. When adding/deleting entries, the exact
element is added/deleted and overlapping elements are not checked by the kernel.
When testing entries, if a host address is tested, then the kernel tries to match
the host address in the networks added to the set and reports the result accordingly.
.PP
From the \fBset\fR netfilter match point of view the searching for a match
always starts from the smallest size of netblock (most specific
prefix) to the largest one (least specific prefix) added to the set.
When adding/deleting IP
addresses to the set by the \fBSET\fR netfilter target, it will be
added/deleted by the most specific prefix which can be found in the
set, or by the host prefix value if the set is empty.
.PP
The second direction parameter of the \fBset\fR match and
\fBSET\fR target modules corresponds to the incoming/outgoing interface
: \fBsrc\fR to the incoming, while \fBdst\fR to the outgoing. When
the interface is flagged with \fBphysdev:\fR, the interface is interpreted
as the incoming/outgoing bridge port.
.PP
The lookup time grows linearly with the number of the different prefix
values added to the set.
.PP
Examples:
.IP
ipset create foo hash:net,iface
.IP
ipset add foo 192.168.0/24,eth0
.IP
ipset add foo 10.1.0.0/16,eth1
.IP
ipset test foo 192.168.0/24,eth0
.SS list:set
The \fBlist:set\fR type uses a simple list in which you can store
set names.

View File

@@ -26,12 +26,12 @@
static char program_name[] = PACKAGE;
static char program_version[] = PACKAGE_VERSION;
static struct ipset_session *session = NULL;
static uint32_t restore_line = 0;
static bool interactive = false;
static struct ipset_session *session;
static uint32_t restore_line;
static bool interactive;
static char cmdline[1024];
static char *newargv[255];
static int newargc = 0;
static int newargc;
/* The known set types: (typename, revision, family) is unique */
extern struct ipset_type ipset_bitmap_ip0;
@@ -39,10 +39,14 @@ extern struct ipset_type ipset_bitmap_ipmac0;
extern struct ipset_type ipset_bitmap_port0;
extern struct ipset_type ipset_hash_ip0;
extern struct ipset_type ipset_hash_net0;
extern struct ipset_type ipset_hash_netport0;
extern struct ipset_type ipset_hash_ipport0;
extern struct ipset_type ipset_hash_ipportip0;
extern struct ipset_type ipset_hash_ipportnet0;
extern struct ipset_type ipset_hash_net1;
extern struct ipset_type ipset_hash_netport1;
extern struct ipset_type ipset_hash_netport2;
extern struct ipset_type ipset_hash_netiface0;
extern struct ipset_type ipset_hash_ipport1;
extern struct ipset_type ipset_hash_ipportip1;
extern struct ipset_type ipset_hash_ipportnet1;
extern struct ipset_type ipset_hash_ipportnet2;
extern struct ipset_type ipset_list_set0;
enum exittype {
@@ -53,12 +57,12 @@ enum exittype {
SESSION_PROBLEM,
};
static int __attribute__((format(printf,2,3)))
static int __attribute__((format(printf, 2, 3)))
exit_error(int status, const char *msg, ...)
{
bool quiet = !interactive
&& session
&& ipset_envopt_test(session, IPSET_ENV_QUIET);
bool quiet = !interactive &&
session &&
ipset_envopt_test(session, IPSET_ENV_QUIET);
if (status && msg && !quiet) {
va_list args;
@@ -94,8 +98,8 @@ exit_error(int status, const char *msg, ...)
static int
handle_error(void)
{
if (ipset_session_warning(session)
&& !ipset_envopt_test(session, IPSET_ENV_QUIET))
if (ipset_session_warning(session) &&
!ipset_envopt_test(session, IPSET_ENV_QUIET))
fprintf(stderr, "Warning: %s\n",
ipset_session_warning(session));
if (ipset_session_error(session))
@@ -121,9 +125,8 @@ help(void)
"Usage: %s [options] COMMAND\n\nCommands:\n",
program_name, program_version, program_name);
for (c = ipset_commands; c->cmd; c++) {
for (c = ipset_commands; c->cmd; c++)
printf("%s %s\n", c->name[0], c->help);
}
printf("\nOptions:\n");
while (opt->flag) {
@@ -145,9 +148,9 @@ build_argv(char *buffer)
newargv[i] = NULL;
newargc = 1;
ptr = strtok(buffer, " \t\n");
ptr = strtok(buffer, " \t\r\n");
newargv[newargc++] = ptr;
while ((ptr = strtok(NULL, " \t\n")) != NULL) {
while ((ptr = strtok(NULL, " \t\r\n")) != NULL) {
if ((newargc + 1) < (int)(sizeof(newargv)/sizeof(char *)))
newargv[newargc++] = ptr;
else {
@@ -181,7 +184,7 @@ restore(char *argv0)
c++;
if (c[0] == '\0' || c[0] == '#')
continue;
else if (strcmp(c, "COMMIT\n") == 0) {
else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) {
ret = ipset_commit(session);
if (ret < 0)
handle_error();
@@ -206,68 +209,61 @@ restore(char *argv0)
static int
call_parser(int *argc, char *argv[], const struct ipset_arg *args)
{
int i = 1, ret = 0;
int ret = 0;
const struct ipset_arg *arg;
const char *optstr;
/* Currently CREATE and ADT may have got additional arguments */
if (!args)
goto done;
if (!args && *argc > 1)
goto err_unknown;
while (*argc > 1) {
for (arg = args; arg->opt; arg++) {
for (i = 1; i < *argc; ) {
D("argc: %u, i: %u: %s vs %s",
*argc, i, argv[i], arg->name[0]);
if (!(ipset_match_option(argv[i], arg->name))) {
i++;
D("argc: %u, %s vs %s", *argc, argv[1], arg->name[0]);
if (!(ipset_match_option(argv[1], arg->name)))
continue;
}
optstr = argv[i];
optstr = argv[1];
/* Shift off matched option */
D("match %s", arg->name[0]);
ipset_shift_argv(argc, argv, i);
D("argc: %u, i: %u", *argc, i);
ipset_shift_argv(argc, argv, 1);
switch (arg->has_arg) {
case IPSET_MANDATORY_ARG:
if (i + 1 > *argc)
if (*argc < 2)
return exit_error(PARAMETER_PROBLEM,
"Missing mandatory argument "
"of option `%s'",
arg->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
if (i + 1 <= *argc) {
if (*argc >= 2) {
ret = ipset_call_parser(session,
arg->parse,
optstr, arg->opt,
argv[i]);
arg, argv[1]);
if (ret < 0)
return ret;
ipset_shift_argv(argc, argv, i);
ipset_shift_argv(argc, argv, 1);
break;
}
/* Fall through */
default:
ret = ipset_call_parser(session,
arg->parse,
optstr, arg->opt,
optstr);
ret = ipset_call_parser(session, arg, optstr);
if (ret < 0)
return ret;
}
break;
}
if (!arg->opt)
goto err_unknown;
}
done:
if (i < *argc)
return exit_error(PARAMETER_PROBLEM,
"Unknown argument: `%s'",
argv[i]);
return ret;
err_unknown:
return exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'", argv[1]);
}
static enum ipset_adt
cmd2cmd(int cmd)
{
switch(cmd) {
switch (cmd) {
case IPSET_CMD_ADD:
return IPSET_ADD;
case IPSET_CMD_DEL:
@@ -353,9 +349,9 @@ check_allowed(const struct ipset_type *type, enum ipset_cmd command)
allowed |= IPSET_FLAG(IPSET_OPT_CIDR);
for (i = IPSET_OPT_IP; i < IPSET_OPT_FLAGS; i++) {
if (!(cmdflags & IPSET_FLAG(i))
|| (allowed & IPSET_FLAG(i))
|| !(flags & IPSET_FLAG(i)))
if (!(cmdflags & IPSET_FLAG(i)) ||
(allowed & IPSET_FLAG(i)) ||
!(flags & IPSET_FLAG(i)))
continue;
/* Not allowed element-expressions */
switch (i) {
@@ -363,19 +359,22 @@ check_allowed(const struct ipset_type *type, enum ipset_cmd command)
exit_error(OTHER_PROBLEM,
"IP/CIDR range is not allowed in command %s "
"with set type %s and family %s",
cmd2name(command), type->name, session_family());
cmd2name(command), type->name,
session_family());
return;
case IPSET_OPT_IP_TO:
exit_error(OTHER_PROBLEM,
"FROM-TO IP range is not allowed in command %s "
"with set type %s and family %s",
cmd2name(command), type->name, session_family());
cmd2name(command), type->name,
session_family());
return;
case IPSET_OPT_PORT_TO:
exit_error(OTHER_PROBLEM,
"FROM-TO port range is not allowed in command %s "
"with set type %s and family %s",
cmd2name(command), type->name, session_family());
cmd2name(command), type->name,
session_family());
return;
default:
break;
@@ -395,7 +394,8 @@ check_allowed(const struct ipset_type *type, enum ipset_cmd command)
"%s parameter is not allowed in command %s "
"with set type %s and family %s",
arg->name[0],
cmd2name(command), type->name, session_family());
cmd2name(command), type->name,
session_family());
return;
}
exit_error(OTHER_PROBLEM,
@@ -431,13 +431,8 @@ parse_commandline(int argc, char *argv[])
const struct ipset_commands *command;
const struct ipset_type *type;
/* Initialize session */
if (session == NULL) {
session = ipset_session_init(printf);
if (session == NULL)
return exit_error(OTHER_PROBLEM,
"Cannot initialize ipset session, aborting.");
}
/* Set session lineno to report parser errors correctly */
ipset_session_lineno(session, restore_line);
/* Commandline parsing, somewhat similar to that of 'ip' */
@@ -481,62 +476,58 @@ parse_commandline(int argc, char *argv[])
/* Second: parse command */
for (command = ipset_commands;
command->cmd && cmd == IPSET_CMD_NONE;
argc > 1 && command->cmd && cmd == IPSET_CMD_NONE;
command++) {
for (i = 1; i < argc; ) {
if (!ipset_match_cmd(argv[1], command->name)) {
i++;
if (!ipset_match_cmd(argv[1], command->name))
continue;
}
if (restore_line != 0
&& (command->cmd == IPSET_CMD_RESTORE
|| command->cmd == IPSET_CMD_VERSION
|| command->cmd == IPSET_CMD_HELP))
if (restore_line != 0 &&
(command->cmd == IPSET_CMD_RESTORE ||
command->cmd == IPSET_CMD_VERSION ||
command->cmd == IPSET_CMD_HELP))
return exit_error(PARAMETER_PROBLEM,
"Command `%s' is invalid "
"in restore mode.",
command->name[0]);
if (interactive
&& command->cmd == IPSET_CMD_RESTORE) {
if (interactive && command->cmd == IPSET_CMD_RESTORE) {
printf("Restore command ignored "
"in interactive mode\n");
return 0;
}
/* Shift off matched command arg */
ipset_shift_argv(&argc, argv, i);
ipset_shift_argv(&argc, argv, 1);
cmd = command->cmd;
switch (command->has_arg) {
case IPSET_MANDATORY_ARG:
case IPSET_MANDATORY_ARG2:
if (i + 1 > argc)
if (argc < 2)
return exit_error(PARAMETER_PROBLEM,
"Missing mandatory argument "
"to command %s",
command->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
arg0 = argv[i];
if (i + 1 <= argc)
arg0 = argv[1];
if (argc >= 2)
/* Shift off first arg */
ipset_shift_argv(&argc, argv, i);
ipset_shift_argv(&argc, argv, 1);
break;
default:
break;
}
if (command->has_arg == IPSET_MANDATORY_ARG2) {
if (i + 1 > argc)
if (argc < 2)
return exit_error(PARAMETER_PROBLEM,
"Missing second mandatory "
"argument to command %s",
command->name[0]);
arg1 = argv[i];
arg1 = argv[1];
/* Shift off second arg */
ipset_shift_argv(&argc, argv, i);
ipset_shift_argv(&argc, argv, 1);
}
break;
}
}
/* Third: catch interactive mode, handle help, version */
switch (cmd) {
@@ -570,15 +561,16 @@ parse_commandline(int argc, char *argv[])
argv[1]);
return exit_error(PARAMETER_PROBLEM, "No command specified.");
case IPSET_CMD_VERSION:
printf("%s v%s.\n", program_name, program_version);
printf("%s v%s, protocol version: %u\n",
program_name, program_version, IPSET_PROTOCOL);
if (interactive)
return 0;
return exit_error(NO_PROBLEM, NULL);
case IPSET_CMD_HELP:
help();
if (interactive
|| !ipset_envopt_test(session, IPSET_ENV_QUIET)) {
if (interactive ||
!ipset_envopt_test(session, IPSET_ENV_QUIET)) {
if (arg0) {
/* Type-specific help, without kernel checking */
type = type_find(arg0);
@@ -731,17 +723,33 @@ parse_commandline(int argc, char *argv[])
int
main(int argc, char *argv[])
{
int ret;
/* Register types */
ipset_type_add(&ipset_bitmap_ip0);
ipset_type_add(&ipset_bitmap_ipmac0);
ipset_type_add(&ipset_bitmap_port0);
ipset_type_add(&ipset_hash_ip0);
ipset_type_add(&ipset_hash_net0);
ipset_type_add(&ipset_hash_netport0);
ipset_type_add(&ipset_hash_ipport0);
ipset_type_add(&ipset_hash_ipportip0);
ipset_type_add(&ipset_hash_ipportnet0);
ipset_type_add(&ipset_hash_net1);
ipset_type_add(&ipset_hash_netport1);
ipset_type_add(&ipset_hash_netport2);
ipset_type_add(&ipset_hash_netiface0);
ipset_type_add(&ipset_hash_ipport1);
ipset_type_add(&ipset_hash_ipportip1);
ipset_type_add(&ipset_hash_ipportnet1);
ipset_type_add(&ipset_hash_ipportnet2);
ipset_type_add(&ipset_list_set0);
return parse_commandline(argc, argv);
/* Initialize session */
session = ipset_session_init(printf);
if (session == NULL)
return exit_error(OTHER_PROBLEM,
"Cannot initialize ipset session, aborting.");
ret = parse_commandline(argc, argv);
ipset_session_fini(session);
return ret;
}

View File

@@ -81,7 +81,7 @@ static const char hash_ip_usage[] =
struct ipset_type ipset_hash_ip0 = {
.name = "hash:ip",
.alias = { "iphash", "iptree", "iptreemap", NULL },
.alias = { "iphash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_ONE,
@@ -92,7 +92,6 @@ struct ipset_type ipset_hash_ip0 = {
.opt = IPSET_OPT_IP
},
},
.compat_parse_elem = ipset_parse_iptimeout,
.args = {
[IPSET_CREATE] = hash_ip_create_args,
[IPSET_ADD] = hash_ip_add_args,

View File

@@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipport_add_args[] = {
{ },
};
static const char hash_ipport_usage[] =
static const char hash_ipport1_usage[] =
"create SETNAME hash:ip,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -82,13 +82,13 @@ static const char hash_ipport_usage[] =
" IP is a valid IPv4 or IPv6 address (or hostname).\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n"
" is supported both for IPv4 and IPv6.\n";
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_ipport0 = {
struct ipset_type ipset_hash_ipport1 = {
.name = "hash:ip,port",
.alias = { "ipporthash", NULL },
.revision = 0,
.revision = 1,
.family = AF_INET46,
.dimension = IPSET_DIM_TWO,
.elem = {
@@ -139,6 +139,6 @@ struct ipset_type ipset_hash_ipport0 = {
| IPSET_FLAG(IPSET_OPT_PROTO),
},
.usage = hash_ipport_usage,
.usage = hash_ipport1_usage,
.usagefn = ipset_port_usage,
};

View File

@@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipportip_add_args[] = {
{ },
};
static const char hash_ipportip_usage[] =
static const char hash_ipportip1_usage[] =
"create SETNAME hash:ip,port,ip\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -82,13 +82,13 @@ static const char hash_ipportip_usage[] =
" IP is a valid IPv4 or IPv6 address (or hostname).\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" in the first IP component is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n"
" is supported both for IPv4 and IPv6.\n";
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_ipportip0 = {
struct ipset_type ipset_hash_ipportip1 = {
.name = "hash:ip,port,ip",
.alias = { "ipportiphash", NULL },
.revision = 0,
.revision = 1,
.family = AF_INET46,
.dimension = IPSET_DIM_THREE,
.elem = {
@@ -150,6 +150,6 @@ struct ipset_type ipset_hash_ipportip0 = {
| IPSET_FLAG(IPSET_OPT_IP2),
},
.usage = hash_ipportip_usage,
.usage = hash_ipportip1_usage,
.usagefn = ipset_port_usage,
};

View File

@@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipportnet_add_args[] = {
{ },
};
static const char hash_ipportnet_usage[] =
static const char hash_ipportnet1_usage[] =
"create SETNAME hash:ip,port,net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -83,13 +83,13 @@ static const char hash_ipportnet_usage[] =
" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" in the first IP component is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n"
" is supported both for IPv4 and IPv6.\n";
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_ipportnet0 = {
struct ipset_type ipset_hash_ipportnet1 = {
.name = "hash:ip,port,net",
.alias = { "ipportnethash", NULL },
.revision = 0,
.revision = 1,
.family = AF_INET46,
.dimension = IPSET_DIM_THREE,
.elem = {
@@ -133,6 +133,7 @@ struct ipset_type ipset_hash_ipportnet0 = {
| IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
@@ -141,6 +142,7 @@ struct ipset_type ipset_hash_ipportnet0 = {
| IPSET_FLAG(IPSET_OPT_CIDR2)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
@@ -154,6 +156,99 @@ struct ipset_type ipset_hash_ipportnet0 = {
| IPSET_FLAG(IPSET_OPT_CIDR2),
},
.usage = hash_ipportnet_usage,
.usage = hash_ipportnet1_usage,
.usagefn = ipset_port_usage,
};
static const char hash_ipportnet2_usage[] =
"create SETNAME hash:ip,port,net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP,PROTO:PORT,IP[/CIDR] [timeout VALUE]\n"
"del SETNAME IP,PROTO:PORT,IP[/CIDR]\n"
"test SETNAME IP,PROTO:PORT,IP[/CIDR]\n\n"
"where depending on the INET family\n"
" IP are valid IPv4 or IPv6 addresses (or hostnames),\n"
" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" in both IP components are supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_ipportnet2 = {
.name = "hash:ip,port,net",
.alias = { "ipportnethash", NULL },
.revision = 2,
.family = AF_INET46,
.dimension = IPSET_DIM_THREE,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_ip4_single6,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
[IPSET_DIM_TWO] = {
.parse = ipset_parse_proto_port,
.print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT
},
[IPSET_DIM_THREE] = {
.parse = ipset_parse_ip4_net6,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP2
},
},
.args = {
[IPSET_CREATE] = hash_ipportnet_create_args,
[IPSET_ADD] = hash_ipportnet_add_args,
},
.mandatory = {
[IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_IP2),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_IP2),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_IP2),
},
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_IP2)
| IPSET_FLAG(IPSET_OPT_CIDR2)
| IPSET_FLAG(IPSET_OPT_IP2_TO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_IP2)
| IPSET_FLAG(IPSET_OPT_CIDR2)
| IPSET_FLAG(IPSET_OPT_IP2_TO),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_IP2)
| IPSET_FLAG(IPSET_OPT_CIDR2),
},
.usage = hash_ipportnet2_usage,
.usagefn = ipset_port_usage,
};

View File

@@ -57,7 +57,7 @@ static const struct ipset_arg hash_net_add_args[] = {
{ },
};
static const char hash_net_usage[] =
static const char hash_net0_usage[] =
"create SETNAME hash:net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -105,5 +105,60 @@ struct ipset_type ipset_hash_net0 = {
| IPSET_FLAG(IPSET_OPT_CIDR),
},
.usage = hash_net_usage,
.usage = hash_net0_usage,
};
static const char hash_net1_usage[] =
"create SETNAME hash:net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP[/CIDR]|FROM-TO [timeout VALUE]\n"
"del SETNAME IP[/CIDR]|FROM-TO\n"
"test SETNAME IP[/CIDR]\n\n"
"where depending on the INET family\n"
" IP is an IPv4 or IPv6 address (or hostname),\n"
" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
" IP range is not supported with IPv6.\n";
struct ipset_type ipset_hash_net1 = {
.name = "hash:net",
.alias = { "nethash", NULL },
.revision = 1,
.family = AF_INET46,
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_ip4_net6,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
},
.args = {
[IPSET_CREATE] = hash_net_create_args,
[IPSET_ADD] = hash_net_add_args,
},
.mandatory = {
[IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
},
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR),
},
.usage = hash_net1_usage,
};

View File

@@ -1,4 +1,4 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
/* Copyright 2011 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* 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
@@ -11,7 +11,7 @@
#include <libipset/types.h> /* prototypes */
/* Parse commandline arguments */
static const struct ipset_arg hash_netport_create_args[] = {
static const struct ipset_arg hash_netiface_create_args[] = {
{ .name = { "family", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY,
.parse = ipset_parse_family, .print = ipset_print_family,
@@ -41,7 +41,7 @@ static const struct ipset_arg hash_netport_create_args[] = {
{ },
};
static const struct ipset_arg hash_netport_add_args[] = {
static const struct ipset_arg hash_netiface_add_args[] = {
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
@@ -49,74 +49,72 @@ static const struct ipset_arg hash_netport_add_args[] = {
{ },
};
static const char hash_netport_usage[] =
"create SETNAME hash:net,port\n"
static const char hash_netiface_usage[] =
"create SETNAME hash:net,iface\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP[/CIDR],PROTO:PORT [timeout VALUE]\n"
"del SETNAME IP[/CIDR],PROTO:PORT\n"
"test SETNAME IP[/CIDR],PROTO:PORT\n\n"
"add SETNAME IP[/CIDR]|FROM-TO,[physdev:]IFACE [timeout VALUE]\n"
"del SETNAME IP[/CIDR]|FROM-TO,[physdev:]IFACE\n"
"test SETNAME IP[/CIDR],[physdev:]IFACE\n\n"
"where depending on the INET family\n"
" IP is a valid IPv4 or IPv6 address (or hostname),\n"
" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
" Adding/deleting multiple elements with TCP/UDP port range supported.\n";
" Adding/deleting multiple elements with IPv4 is supported.\n";
struct ipset_type ipset_hash_netport0 = {
.name = "hash:net,port",
.alias = { "netporthash", NULL },
struct ipset_type ipset_hash_netiface0 = {
.name = "hash:net,iface",
.alias = { "netifacehash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_TWO,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_ipnet,
.parse = ipset_parse_ip4_net6,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
[IPSET_DIM_TWO] = {
.parse = ipset_parse_proto_port,
.print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT
.parse = ipset_parse_iface,
.print = ipset_print_iface,
.opt = IPSET_OPT_IFACE
},
},
.args = {
[IPSET_CREATE] = hash_netport_create_args,
[IPSET_ADD] = hash_netport_add_args,
[IPSET_CREATE] = hash_netiface_create_args,
[IPSET_ADD] = hash_netiface_add_args,
},
.mandatory = {
[IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
| IPSET_FLAG(IPSET_OPT_IFACE),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
| IPSET_FLAG(IPSET_OPT_IFACE),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
| IPSET_FLAG(IPSET_OPT_IFACE),
},
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT)
| IPSET_FLAG(IPSET_OPT_CIDR),
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_IFACE)
| IPSET_FLAG(IPSET_OPT_PHYSDEV)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_CIDR),
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_IFACE)
| IPSET_FLAG(IPSET_OPT_PHYSDEV),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_CIDR),
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_IFACE)
| IPSET_FLAG(IPSET_OPT_PHYSDEV),
},
.usage = hash_netport_usage,
.usagefn = ipset_port_usage,
.usage = hash_netiface_usage,
};

View File

@@ -0,0 +1,199 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* 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.
*/
#include <libipset/data.h> /* IPSET_OPT_* */
#include <libipset/parse.h> /* parser functions */
#include <libipset/print.h> /* printing functions */
#include <libipset/ui.h> /* ipset_port_usage */
#include <libipset/types.h> /* prototypes */
/* Parse commandline arguments */
static const struct ipset_arg hash_netport_create_args[] = {
{ .name = { "family", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY,
.parse = ipset_parse_family, .print = ipset_print_family,
},
/* Alias: family inet */
{ .name = { "-4", NULL },
.has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
.parse = ipset_parse_family,
},
/* Alias: family inet6 */
{ .name = { "-6", NULL },
.has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
.parse = ipset_parse_family,
},
{ .name = { "hashsize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
{ .name = { "maxelem", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
{ },
};
static const struct ipset_arg hash_netport_add_args[] = {
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
{ },
};
static const char hash_netport1_usage[] =
"create SETNAME hash:net,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP[/CIDR],PROTO:PORT [timeout VALUE]\n"
"del SETNAME IP[/CIDR],PROTO:PORT\n"
"test SETNAME IP[/CIDR],PROTO:PORT\n\n"
"where depending on the INET family\n"
" IP is a valid IPv4 or IPv6 address (or hostname),\n"
" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_netport1 = {
.name = "hash:net,port",
.alias = { "netporthash", NULL },
.revision = 1,
.family = AF_INET46,
.dimension = IPSET_DIM_TWO,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_ipnet,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
[IPSET_DIM_TWO] = {
.parse = ipset_parse_proto_port,
.print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT
},
},
.args = {
[IPSET_CREATE] = hash_netport_create_args,
[IPSET_ADD] = hash_netport_add_args,
},
.mandatory = {
[IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
},
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT)
| IPSET_FLAG(IPSET_OPT_CIDR),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_CIDR),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_CIDR),
},
.usage = hash_netport1_usage,
.usagefn = ipset_port_usage,
};
static const char hash_netport2_usage[] =
"create SETNAME hash:net,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP[/CIDR]|FROM-TO,PROTO:PORT [timeout VALUE]\n"
"del SETNAME IP[/CIDR]|FROM-TO,PROTO:PORT\n"
"test SETNAME IP[/CIDR],PROTO:PORT\n\n"
"where depending on the INET family\n"
" IP is a valid IPv4 or IPv6 address (or hostname),\n"
" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
" Adding/deleting multiple elements with IPv4 is supported.\n"
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_netport2 = {
.name = "hash:net,port",
.alias = { "netporthash", NULL },
.revision = 2,
.family = AF_INET46,
.dimension = IPSET_DIM_TWO,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_ip4_net6,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
[IPSET_DIM_TWO] = {
.parse = ipset_parse_proto_port,
.print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT
},
},
.args = {
[IPSET_CREATE] = hash_netport_create_args,
[IPSET_ADD] = hash_netport_add_args,
},
.mandatory = {
[IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_PORT),
},
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_PROTO),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO),
},
.usage = hash_netport2_usage,
.usagefn = ipset_port_usage,
};

View File

@@ -23,9 +23,9 @@
const struct ipset_commands ipset_commands[] = {
/* Order is important */
{ /* c[reate], --create, n, -N */
{ /* c[reate], --create, n[ew], -N */
.cmd = IPSET_CMD_CREATE,
.name = { "create", "n" },
.name = { "create", "new" },
.has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME TYPENAME [type-specific-options]\n"
" Create a new set",
@@ -143,14 +143,15 @@ ipset_match_cmd(const char *arg, const char * const name[])
if (len > strlen(name[0]) || !len)
return false;
else if (strncmp(arg, name[0], len) == 0)
else if (len > 1 &&
((strncmp(arg, name[0], len) == 0) ||
(name[1] != NULL && (strncmp(arg, name[1], len) == 0))))
return true;
else if (len != 1)
return false;
else if (name[1] == NULL)
return tolower(arg[0]) == name[0][0];
else
return tolower(arg[0]) == name[1][0];
return tolower(arg[0]) == name[0][0] ||
(name[1] != NULL && tolower(arg[0]) == name[1][0]);
}
const struct ipset_envopts ipset_envopts[] = {
@@ -188,6 +189,19 @@ const struct ipset_envopts ipset_envopts[] = {
" when adding already existing elements\n"
" or when deleting non-existing elements.",
},
{ .name = { "-n", "-name" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_LIST_SETNAME,
.help = "\n"
" When listing, list just setnames from kernel.\n",
},
{ .name = { "-t", "-terse" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_LIST_HEADER,
.help = "\n"
" When listing, list setnames and set headers\n"
" from kernel only.",
},
{ },
};
@@ -202,8 +216,8 @@ ipset_match_option(const char *arg, const char * const name[])
if (arg[0] == '-' && arg[1] == '-')
arg++, arg++;
return STREQ(arg, name[0])
|| (name[1] != NULL && STREQ(arg, name[1]));
return STREQ(arg, name[0]) ||
(name[1] != NULL && STREQ(arg, name[1]));
}
/* Strict envopt matching */
@@ -217,8 +231,8 @@ ipset_match_envopt(const char *arg, const char * const name[])
if (arg[0] == '-' && arg[1] == '-')
arg++;
return STREQ(arg, name[0])
|| (name[1] != NULL && STREQ(arg, name[1]));
return STREQ(arg, name[0]) ||
(name[1] != NULL && STREQ(arg, name[1]));
}
/**
@@ -237,9 +251,8 @@ ipset_shift_argv(int *argc, char *argv[], int from)
assert(*argc >= from + 1);
for (i = from + 1; i <= *argc; i++) {
for (i = from + 1; i <= *argc; i++)
argv[i-1] = argv[i];
}
(*argc)--;
return;
}
@@ -256,9 +269,9 @@ ipset_port_usage(void)
const char *name;
printf(" [PROTO:]PORT is a valid pattern of the following:\n"
" PORTNAME port name from /etc/services\n"
" PORTNUMBER port number identifier\n"
" tcp|udp:PORTNAME|PORTNUMBER\n"
" PORTNAME TCP port name from /etc/services\n"
" PORTNUMBER TCP port number identifier\n"
" tcp|sctp|udp|udplite:PORTNAME|PORTNUMBER\n"
" icmp:CODENAME supported ICMP codename\n"
" icmp:TYPE/CODE ICMP type/code value\n"
" icmpv6:CODENAME supported ICMPv6 codename\n"

View File

@@ -1,7 +1,7 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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
@@ -29,52 +29,33 @@ MODULE_ALIAS("ip6t_SET");
static inline int
match_set(ip_set_id_t index, const struct sk_buff *skb,
u8 pf, u8 dim, u8 flags, int inv)
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt, int inv)
{
if (ip_set_test(index, skb, pf, dim, flags))
if (ip_set_test(index, skb, par, opt))
inv = !inv;
return inv;
}
#define ADT_OPT(n, f, d, fs, cfs, t) \
const struct ip_set_adt_opt n = { \
.family = f, \
.dim = d, \
.flags = fs, \
.cmdflags = cfs, \
.timeout = t, \
}
/* Revision 0 interface: backward compatible with netfilter/iptables */
/* Backward compatibility constrains (incomplete):
* 2.6.24: [NETLINK]: Introduce nested and byteorder flag to netlink attribute
* 2.6.25: is_vmalloc_addr(): Check if an address is within the vmalloc
* boundaries
* 2.6.27: rcu: split list.h and move rcu-protected lists into rculist.h
* 2.6.28: netfilter: ctnetlink: remove bogus module dependency between
* ctnetlink and nf_nat (nfnl_lock/nfnl_unlock)
* 2.6.29: generic swap(): introduce global macro swap(a, b)
* 2.6.31: netfilter: passive OS fingerprint xtables match
* 2.6.34: rcu: Add lockdep-enabled variants of rcu_dereference()
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
#error "Linux kernel version too old: must be >= 2.6.34"
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
#define CHECK_OK 1
#define CHECK_FAIL 0
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
#define CHECK_OK 0
#define CHECK_FAIL (-EINVAL)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_match_v0(const struct sk_buff *skb, const struct xt_match_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static bool
set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
#endif
{
const struct xt_set_info_match_v0 *info = par->matchinfo;
ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
info->match_set.u.compat.flags, 0, UINT_MAX);
return match_set(info->match_set.index, skb, par->family,
info->match_set.u.compat.dim,
info->match_set.u.compat.flags,
return match_set(info->match_set.index, skb, par, &opt,
info->match_set.u.compat.flags & IPSET_INV_MATCH);
}
@@ -94,13 +75,8 @@ compat_flags(struct xt_set_info_v0 *info)
}
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_match_v0_checkentry(const struct xt_mtchk_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_match_v0_checkentry(const struct xt_mtchk_param *par)
#endif
{
struct xt_set_info_match_v0 *info = par->matchinfo;
ip_set_id_t index;
@@ -108,19 +84,21 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
index = ip_set_nfnl_get_byindex(info->match_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match",
pr_warning("Cannot find set indentified by id %u to match\n",
info->match_set.index);
return CHECK_FAIL; /* error */
return -ENOENT;
}
if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("That's nasty!");
return CHECK_FAIL; /* error */
pr_warning("Protocol error: set match dimension "
"is over the limit!\n");
ip_set_nfnl_put(info->match_set.index);
return -ERANGE;
}
/* Fill out compatibility data */
compat_flags(&info->match_set);
return CHECK_OK;
return 0;
}
static void
@@ -131,35 +109,25 @@ set_match_v0_destroy(const struct xt_mtdtor_param *par)
ip_set_nfnl_put(info->match_set.index);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static unsigned int
set_target_v0(struct sk_buff *skb, const struct xt_target_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static unsigned int
set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
#endif
{
const struct xt_set_info_target_v0 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
info->add_set.u.compat.flags, 0, UINT_MAX);
ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
info->del_set.u.compat.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par->family,
info->add_set.u.compat.dim,
info->add_set.u.compat.flags);
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, skb, par->family,
info->del_set.u.compat.dim,
info->del_set.u.compat.flags);
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_target_v0_checkentry(const struct xt_tgchk_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_target_v0_checkentry(const struct xt_tgchk_param *par)
#endif
{
struct xt_set_info_target_v0 *info = par->targinfo;
ip_set_id_t index;
@@ -167,31 +135,38 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("cannot find add_set index %u as target",
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
return CHECK_FAIL; /* error */
return -ENOENT;
}
}
if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("cannot find del_set index %u as target",
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
return CHECK_FAIL; /* error */
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
return -ENOENT;
}
}
if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0
|| info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("That's nasty!");
return CHECK_FAIL; /* error */
if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
return -ERANGE;
}
/* Fill out compatibility data */
compat_flags(&info->add_set);
compat_flags(&info->del_set);
return CHECK_OK;
return 0;
}
static void
@@ -205,123 +180,110 @@ set_target_v0_destroy(const struct xt_tgdtor_param *par)
ip_set_nfnl_put(info->del_set.index);
}
/* Revision 1: current interface to netfilter/iptables */
/* Revision 1 match and target */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_match(const struct sk_buff *skb, const struct xt_match_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static bool
set_match(const struct sk_buff *skb, struct xt_action_param *par)
#endif
set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_set_info_match *info = par->matchinfo;
const struct xt_set_info_match_v1 *info = par->matchinfo;
ADT_OPT(opt, par->family, info->match_set.dim,
info->match_set.flags, 0, UINT_MAX);
return match_set(info->match_set.index, skb, par->family,
info->match_set.dim,
info->match_set.flags,
return match_set(info->match_set.index, skb, par, &opt,
info->match_set.flags & IPSET_INV_MATCH);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_match_checkentry(const struct xt_mtchk_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_match_checkentry(const struct xt_mtchk_param *par)
#endif
set_match_v1_checkentry(const struct xt_mtchk_param *par)
{
struct xt_set_info_match *info = par->matchinfo;
struct xt_set_info_match_v1 *info = par->matchinfo;
ip_set_id_t index;
index = ip_set_nfnl_get_byindex(info->match_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match",
pr_warning("Cannot find set indentified by id %u to match\n",
info->match_set.index);
return CHECK_FAIL; /* error */
return -ENOENT;
}
if (info->match_set.dim > IPSET_DIM_MAX) {
pr_warning("That's nasty!");
return CHECK_FAIL; /* error */
pr_warning("Protocol error: set match dimension "
"is over the limit!\n");
ip_set_nfnl_put(info->match_set.index);
return -ERANGE;
}
return CHECK_OK;
return 0;
}
static void
set_match_destroy(const struct xt_mtdtor_param *par)
set_match_v1_destroy(const struct xt_mtdtor_param *par)
{
struct xt_set_info_match *info = par->matchinfo;
struct xt_set_info_match_v1 *info = par->matchinfo;
ip_set_nfnl_put(info->match_set.index);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static unsigned int
set_target(struct sk_buff *skb, const struct xt_target_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static unsigned int
set_target(struct sk_buff *skb, const struct xt_action_param *par)
#endif
set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_set_info_target *info = par->targinfo;
const struct xt_set_info_target_v1 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.dim,
info->add_set.flags, 0, UINT_MAX);
ADT_OPT(del_opt, par->family, info->del_set.dim,
info->del_set.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index,
skb, par->family,
info->add_set.dim,
info->add_set.flags);
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index,
skb, par->family,
info->add_set.dim,
info->del_set.flags);
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_target_checkentry(const struct xt_tgchk_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_target_checkentry(const struct xt_tgchk_param *par)
#endif
set_target_v1_checkentry(const struct xt_tgchk_param *par)
{
const struct xt_set_info_target *info = par->targinfo;
const struct xt_set_info_target_v1 *info = par->targinfo;
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("cannot find add_set index %u as target",
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
return CHECK_FAIL; /* error */
return -ENOENT;
}
}
if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("cannot find del_set index %u as target",
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
return CHECK_FAIL; /* error */
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
return -ENOENT;
}
}
if (info->add_set.dim > IPSET_DIM_MAX
|| info->del_set.flags > IPSET_DIM_MAX) {
pr_warning("That's nasty!");
return CHECK_FAIL; /* error */
if (info->add_set.dim > IPSET_DIM_MAX ||
info->del_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
return -ERANGE;
}
return CHECK_OK;
return 0;
}
static void
set_target_destroy(const struct xt_tgdtor_param *par)
set_target_v1_destroy(const struct xt_tgdtor_param *par)
{
const struct xt_set_info_target *info = par->targinfo;
const struct xt_set_info_target_v1 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
@@ -329,6 +291,28 @@ set_target_destroy(const struct xt_tgdtor_param *par)
ip_set_nfnl_put(info->del_set.index);
}
/* Revision 2 target */
static unsigned int
set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_set_info_target_v2 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.dim,
info->add_set.flags, info->flags, info->timeout);
ADT_OPT(del_opt, par->family, info->del_set.dim,
info->del_set.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
#define set_target_v2_checkentry set_target_v1_checkentry
#define set_target_v2_destroy set_target_v1_destroy
static struct xt_match set_matches[] __read_mostly = {
{
.name = "set",
@@ -344,20 +328,20 @@ static struct xt_match set_matches[] __read_mostly = {
.name = "set",
.family = NFPROTO_IPV4,
.revision = 1,
.match = set_match,
.matchsize = sizeof(struct xt_set_info_match),
.checkentry = set_match_checkentry,
.destroy = set_match_destroy,
.match = set_match_v1,
.matchsize = sizeof(struct xt_set_info_match_v1),
.checkentry = set_match_v1_checkentry,
.destroy = set_match_v1_destroy,
.me = THIS_MODULE
},
{
.name = "set",
.family = NFPROTO_IPV6,
.revision = 1,
.match = set_match,
.matchsize = sizeof(struct xt_set_info_match),
.checkentry = set_match_checkentry,
.destroy = set_match_destroy,
.match = set_match_v1,
.matchsize = sizeof(struct xt_set_info_match_v1),
.checkentry = set_match_v1_checkentry,
.destroy = set_match_v1_destroy,
.me = THIS_MODULE
},
};
@@ -377,20 +361,40 @@ static struct xt_target set_targets[] __read_mostly = {
.name = "SET",
.revision = 1,
.family = NFPROTO_IPV4,
.target = set_target,
.targetsize = sizeof(struct xt_set_info_target),
.checkentry = set_target_checkentry,
.destroy = set_target_destroy,
.target = set_target_v1,
.targetsize = sizeof(struct xt_set_info_target_v1),
.checkentry = set_target_v1_checkentry,
.destroy = set_target_v1_destroy,
.me = THIS_MODULE
},
{
.name = "SET",
.revision = 1,
.family = NFPROTO_IPV6,
.target = set_target,
.targetsize = sizeof(struct xt_set_info_target),
.checkentry = set_target_checkentry,
.destroy = set_target_destroy,
.target = set_target_v1,
.targetsize = sizeof(struct xt_set_info_target_v1),
.checkentry = set_target_v1_checkentry,
.destroy = set_target_v1_destroy,
.me = THIS_MODULE
},
{
.name = "SET",
.revision = 2,
.family = NFPROTO_IPV4,
.target = set_target_v2,
.targetsize = sizeof(struct xt_set_info_target_v2),
.checkentry = set_target_v2_checkentry,
.destroy = set_target_v2_destroy,
.me = THIS_MODULE
},
{
.name = "SET",
.revision = 2,
.family = NFPROTO_IPV6,
.target = set_target_v2,
.targetsize = sizeof(struct xt_set_info_target_v2),
.checkentry = set_target_v2_checkentry,
.destroy = set_target_v2_destroy,
.me = THIS_MODULE
},
};

View File

@@ -1,6 +1,7 @@
#ifndef _XT_SET_H
#define _XT_SET_H
#include <linux/types.h>
#include "ip_set.h"
/* Revision 0 interface: backward compatible with netfilter/iptables */
@@ -34,7 +35,7 @@ struct xt_set_info_target_v0 {
struct xt_set_info_v0 del_set;
};
/* Revision 1: current interface to netfilter/iptables */
/* Revision 1 match and target */
struct xt_set_info {
ip_set_id_t index;
@@ -43,13 +44,22 @@ struct xt_set_info {
};
/* match and target infos */
struct xt_set_info_match {
struct xt_set_info_match_v1 {
struct xt_set_info match_set;
};
struct xt_set_info_target {
struct xt_set_info_target_v1 {
struct xt_set_info add_set;
struct xt_set_info del_set;
};
/* Revision 2 target */
struct xt_set_info_target_v2 {
struct xt_set_info add_set;
struct xt_set_info del_set;
u32 flags;
u32 timeout;
};
#endif /*_XT_SET_H*/

View File

@@ -71,10 +71,10 @@ static void chaos_tg_print(const void *ip,
switch (info->variant) {
case XTCHAOS_DELUDE:
printf("DELUDE ");
printf(" DELUDE ");
break;
case XTCHAOS_TARPIT:
printf("TARPIT ");
printf(" TARPIT ");
break;
}
}
@@ -85,10 +85,10 @@ static void chaos_tg_save(const void *ip, const struct xt_entry_target *target)
switch (info->variant) {
case XTCHAOS_DELUDE:
printf("--delude ");
printf(" --delude ");
break;
case XTCHAOS_TARPIT:
printf("--tarpit ");
printf(" --tarpit ");
break;
}
}

View File

@@ -62,7 +62,7 @@ static void CHECKSUM_print(const void *ip, const struct xt_entry_target *target,
printf("CHECKSUM ");
if (einfo->operation & XT_CHECKSUM_OP_FILL)
printf("fill ");
printf(" fill ");
}
static void CHECKSUM_save(const void *ip, const struct xt_entry_target *target)
@@ -71,7 +71,7 @@ static void CHECKSUM_save(const void *ip, const struct xt_entry_target *target)
(const struct xt_CHECKSUM_info *)target->data;
if (einfo->operation & XT_CHECKSUM_OP_FILL)
printf("--checksum-fill ");
printf(" --checksum-fill ");
}
static struct xtables_target checksum_tg_reg = {

View File

@@ -66,7 +66,7 @@ static void dhcpmac_tg_print(const void *ip,
{
const struct dhcpmac_info *info = (void *)target->data;
printf("DHCPMAC %s" DH_MAC_FMT "/%u ",
printf(" DHCPMAC %s" DH_MAC_FMT "/%u ",
info->invert ? "!" : "", DH_MAC_HEX(info->addr), info->mask);
}
@@ -76,8 +76,8 @@ static void dhcpmac_tg_save(const void *ip,
const struct dhcpmac_info *info = (const void *)target->data;
if (info->invert)
printf("! ");
printf("--set-mac " DH_MAC_FMT "/%u ",
printf(" !");
printf(" --set-mac " DH_MAC_FMT "/%u ",
DH_MAC_HEX(info->addr), info->mask);
}

241
extensions/libxt_DNETMAP.c Normal file
View File

@@ -0,0 +1,241 @@
/* Shared library add-on to iptables to add DNETMAP support.
* (C) 2010 Marek Kierdelewicz <marek@koba.pl>
*
* uses some code from libipt_NETMAP by:
* Svenning Soerensen <svenning@post5.tele.dk>
*/
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <xtables.h>
#include <net/netfilter/nf_nat.h>
#include "xt_DNETMAP.h"
#define MODULENAME "DNETMAP"
static const struct option DNETMAP_opts[] = {
{"prefix", 1, NULL, 'p'},
{"reuse", 0, NULL, 'r'},
{"ttl", 1, NULL, 't'},
{.name = NULL}
};
static void DNETMAP_help(void)
{
printf(MODULENAME " target options:\n"
" --%s address[/mask]\n"
" Network subnet to map to. If not specified, all existing prefixes are used.\n"
" --%s\n"
" Reuse entry for given prenat-ip from any prefix despite bindings ttl < 0.\n"
" --%s seconds\n"
" Regenerate bindings ttl value to seconds. If negative value is specified,\n"
" bindings ttl is kept unchanged. If not specified then default ttl value (600s)\n"
" is used.\n\n",
DNETMAP_opts[0].name, DNETMAP_opts[1].name,
DNETMAP_opts[2].name);
}
static u_int32_t bits2netmask(int bits)
{
u_int32_t netmask, bm;
if (bits >= 32 || bits < 0)
return ~0;
for (netmask = 0, bm = 0x80000000; bits; bits--, bm >>= 1)
netmask |= bm;
return htonl(netmask);
}
static int netmask2bits(u_int32_t netmask)
{
u_int32_t bm;
int bits;
netmask = ntohl(netmask);
for (bits = 0, bm = 0x80000000; netmask & bm; netmask <<= 1)
bits++;
if (netmask)
return -1; /* holes in netmask */
return bits;
}
static void DNETMAP_init(struct xt_entry_target *t)
{
struct xt_DNETMAP_tginfo *tginfo = (void *)&t->data;
struct nf_nat_multi_range *mr = &tginfo->prefix;
/* Actually, it's 0, but it's ignored at the moment. */
mr->rangesize = 1;
tginfo->ttl = 0;
tginfo->flags = 0;
}
/* Parses network address */
static void parse_prefix(char *arg, struct nf_nat_range *range)
{
char *slash;
const struct in_addr *ip;
u_int32_t netmask;
unsigned int bits;
range->flags |= IP_NAT_RANGE_MAP_IPS;
slash = strchr(arg, '/');
if (slash)
*slash = '\0';
ip = xtables_numeric_to_ipaddr(arg);
if (ip == NULL)
xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
arg);
range->min_ip = ip->s_addr;
if (slash) {
if (strchr(slash + 1, '.')) {
ip = xtables_numeric_to_ipmask(slash + 1);
if (ip == NULL)
xtables_error(PARAMETER_PROBLEM,
"Bad netmask \"%s\"\n",
slash + 1);
netmask = ip->s_addr;
} else {
if (!xtables_strtoui(slash + 1, NULL, &bits, 0, 32))
xtables_error(PARAMETER_PROBLEM,
"Bad netmask \"%s\"\n",
slash + 1);
netmask = bits2netmask(bits);
}
/* Don't allow /0 (/1 is probably insane, too) */
if (netmask == 0)
xtables_error(PARAMETER_PROBLEM, "Netmask needed\n");
/* Mask should be <= then /16 */
if (bits < 16)
xtables_error(PARAMETER_PROBLEM,
"Max netmask size is /16\n");
} else
netmask = ~0;
if (range->min_ip & ~netmask) {
if (slash)
*slash = '/';
xtables_error(PARAMETER_PROBLEM, "Bad network address \"%s\"\n",
arg);
}
range->max_ip = range->min_ip | ~netmask;
}
static int DNETMAP_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_DNETMAP_tginfo *tginfo = (void *)(*target)->data;
struct nf_nat_multi_range *mr = &tginfo->prefix;
char *end;
switch (c) {
case 'p':
xtables_param_act(XTF_ONLY_ONCE, MODULENAME, "--prefix",
*flags & XT_DNETMAP_PREFIX);
xtables_param_act(XTF_NO_INVERT, MODULENAME, "--prefix",
invert);
/* TO-DO use xtables_ipparse_any instead? */
parse_prefix(optarg, &mr->range[0]);
*flags |= XT_DNETMAP_PREFIX;
tginfo->flags |= XT_DNETMAP_PREFIX;
return 1;
case 'r':
xtables_param_act(XTF_ONLY_ONCE, MODULENAME, "--reuse",
*flags & XT_DNETMAP_REUSE);
xtables_param_act(XTF_NO_INVERT, MODULENAME, "--reuse", invert);
*flags |= XT_DNETMAP_REUSE;
tginfo->flags |= XT_DNETMAP_REUSE;
return 1;
case 't':
xtables_param_act(XTF_ONLY_ONCE, MODULENAME, "--ttl",
*flags & XT_DNETMAP_TTL);
xtables_param_act(XTF_NO_INVERT, MODULENAME, "--ttl", invert);
*flags |= XT_DNETMAP_TTL;
tginfo->flags |= XT_DNETMAP_TTL;
tginfo->ttl = strtol(optarg, &end, 10);
if (*end != '\0')
return 0;
return 1;
default:
return 0;
}
}
static void DNETMAP_print_addr(const void *ip,
const struct xt_entry_target *target,
int numeric)
{
struct xt_DNETMAP_tginfo *tginfo = (void *)&target->data;
const struct nf_nat_multi_range *mr = &tginfo->prefix;
const struct nf_nat_range *r = &mr->range[0];
struct in_addr a;
int bits;
a.s_addr = r->min_ip;
printf("%s", xtables_ipaddr_to_numeric(&a));
a.s_addr = ~(r->min_ip ^ r->max_ip);
bits = netmask2bits(a.s_addr);
if (bits < 0)
printf("/%s", xtables_ipaddr_to_numeric(&a));
else
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");
printf(" reuse %i", (*flags & XT_DNETMAP_REUSE) > 0);
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)
{
struct xt_DNETMAP_tginfo *tginfo = (void *)&target->data;
const __u8 *flags = &tginfo->flags;
if (*flags & XT_DNETMAP_PREFIX) {
printf(" --%s ", DNETMAP_opts[0].name);
DNETMAP_print_addr(ip, target, 0);
}
printf(" --reuse %i ", *flags & XT_DNETMAP_REUSE);
/* ommited because default value can change as kernel mod param */
if (*flags & XT_DNETMAP_TTL)
printf(" --ttl %i ", tginfo->ttl);
}
static struct xtables_target dnetmap_tg_reg = {
.name = MODULENAME,
.version = XTABLES_VERSION,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_DNETMAP_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_DNETMAP_tginfo)),
.help = DNETMAP_help,
.init = DNETMAP_init,
.parse = DNETMAP_parse,
.print = DNETMAP_print,
.save = DNETMAP_save,
.extra_opts = DNETMAP_opts,
};
static void _init(void)
{
xtables_register_target(&dnetmap_tg_reg);
}

View File

@@ -0,0 +1,91 @@
The \fBDNETMAP\fR target allows dynamic two-way 1:1 mapping of IPv4 subnets.
Single rule can map private subnet to shorter public subnet creating and
maintaining unambigeous private-public ip bindings. Second rule can be used to
map new flows to private subnet according to maintained bindings. Target allows
efficient public IPv4 space usage and unambigeous NAT at the same time.
Target can be used only in \fBnat\fR table in \fBPOSTROUTING\fR or \fBOUTPUT\fR
chains for SNAT and in \fBPREROUTING\fR for DNAT. Only flows directed to bound
IPs will be DNATed. Packet continues chain traversal if there is no free
postnat-ip to be assigned to prenat-ip. Default binding \fBttl\fR is \fI10
minutes\fR and can be changed using \fBdefault_ttl\fR module option. Default ip
hash size is 256 and can be changed using \fBhash_size\fR module option.
.TP
\fB\-\-prefix\fR \fIaddr\fR\fB/\fR\fImask\fR
Network subnet to map to. If not specified, all existing prefixes are used.
.TP
\fB\-\-reuse\fR
Reuse entry for given prenat-ip from any prefix despite bindings ttl < 0.
.TP
\fB\-\-ttl\fR \fIseconds\fR
Regenerate bindings ttl value to \fIseconds\fR. If negative value is specified,
bindings ttl is kept unchanged. If not specified then default ttl value (600s)
is used.
.PP
\fB* /proc interface\fR
Module creates following entries for each new specified subnet:
.TP
\fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR
Contains binding table for subnet/mask. Each line contains \fBprenat-ip\fR,
\fBpostnat-ip\fR,\fBttl\fR (seconds till entry times out), \fBlasthit\fR (last
entry hit in seconds relative to system boot time).
.TP
\fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR\fB_stat\fR
Contains statistics for given subnet/mask. Line contains contains three
numerical values separated by spaces. First one is number of currently used
addresses (bindings with negative ttl excluded), second one is number of all
usable addresses in subnet and third one is mean \fBttl\fR value for all active
entries.
.PP
Entries are removed if the last iptables rule for a specific subnet is deleted.
\fB* Logging\fR
Module logs binding add/timeout events to klog. This behaviour can be disabled
using \fBdisable_log\fR module parameter.
\fB* Examples\fR
\fB1.\fR Map subnet 192.168.0.0/24 to subnets 20.0.0.0/26. SNAT only:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
Active hosts from 192.168.0.0/24 subnet are mapped to 20.0.0.0/26. If packet
from not yet bound prenat-ip hits the rule and there are no free or timed-out
(ttl<0) entries in prefix 20.0.0.0/28, then notice is logged to klog and chain
traversal continues. If packet from already bound prenat-ip hits the rule,
bindings ttl value is regenerated to default_ttl and SNAT is performed.
\fB2.\fR Use of \fB\-\-reuse\fR and \fB\-\-ttl\fR switches, multiple rule
interaction:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix
20.0.0.0/26 --reuse --ttl 200
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 30.0.0.0/26
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
(30.0.0.0/26) is used with default ttl. It's important to note that the first
rule SNATs all flows whose source IP is already actively (ttl>0) bound to ANY
prefix. Parameter \fB\-\-reuse\fR makes this functionality work even for
inactive (ttl<0) entries.
If both subnets are exhaused, then chain traversal continues.
\fB3.\fR Map 192.168.0.0/24 to subnets 20.0.0.0/26 bidirectional way:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
iptables -t nat -A PREROUTING -j DNETMAP
If host 192.168.0.10 generates some traffic, it gets bound to first free IP in
subnet - 20.0.0.0. Now any traffic directed to 20.0.0.0 gets DNATed to
192.168.0.10 as long as there's an active (ttl>0) binding. There's no need to
specify \fB\-\-prefix\fR parameter in PREROUTING rule, because this way it DNATs
traffic to all active prefixes. You could specify prefix it you'd like to make
DNAT work for specific prefix only.
.

View File

@@ -119,16 +119,16 @@ ipmark_tg_print(const void *entry, const struct xt_entry_target *target,
const struct xt_ipmark_tginfo *info = (const void *)target->data;
if (info->selector == XT_IPMARK_SRC)
printf("IPMARK src ip ");
printf(" IPMARK src ip ");
else
printf("IPMARK dst ip ");
printf(" IPMARK dst ip ");
if (info->shift != 0)
printf("shift %u ", (unsigned int)info->shift);
printf(" shift %u ", (unsigned int)info->shift);
if (info->andmask != ~0U)
printf("and 0x%x ", (unsigned int)info->andmask);
printf(" and 0x%x ", (unsigned int)info->andmask);
if (info->ormask != 0)
printf("or 0x%x ", (unsigned int)info->ormask);
printf(" or 0x%x ", (unsigned int)info->ormask);
}
static void
@@ -137,16 +137,16 @@ ipmark_tg_save(const void *entry, const struct xt_entry_target *target)
const struct xt_ipmark_tginfo *info = (const void *)target->data;
if (info->selector == XT_IPMARK_SRC)
printf("--addr src ");
printf(" --addr src ");
else
printf("--addr dst ");
printf(" --addr dst ");
if (info->shift != 0)
printf("--shift %u ", (unsigned int)info->shift);
printf(" --shift %u ", (unsigned int)info->shift);
if (info->andmask != ~0U)
printf("--and-mask 0x%x ", (unsigned int)info->andmask);
printf(" --and-mask 0x%x ", (unsigned int)info->andmask);
if (info->ormask != 0)
printf("--or-mask 0x%x ", (unsigned int)info->ormask);
printf(" --or-mask 0x%x ", (unsigned int)info->ormask);
}
static struct xtables_target ipmark_tg_reg = {

View File

@@ -83,7 +83,7 @@ logmark_tg_print(const void *ip, const struct xt_entry_target *target,
{
const struct xt_logmark_tginfo *info = (void *)target->data;
printf("LOGMARK level %u prefix \"%s\" ", info->level, info->prefix);
printf(" LOGMARK level %u prefix \"%s\" ", info->level, info->prefix);
}
static void
@@ -92,9 +92,9 @@ logmark_tg_save(const void *ip, const struct xt_entry_target *target)
const struct xt_logmark_tginfo *info = (void *)target->data;
if (info->level != 4)
printf("--log-level %u ", info->level);
printf(" --log-level %u ", info->level);
if (*info->prefix != '\0')
printf("--log-prefix \"%s\" ", info->prefix);
printf(" --log-prefix \"%s\" ", info->prefix);
}
static struct xtables_target logmark_tg_reg = {

View File

@@ -110,10 +110,10 @@ rawdnat_tg4_print(const void *entry, const struct xt_entry_target *target,
const struct xt_rawnat_tginfo *info = (const void *)target->data;
if (!numeric && info->mask == 32)
printf("to-destination %s ",
printf(" to-destination %s ",
xtables_ipaddr_to_anyname(&info->addr.in));
else
printf("to-destination %s/%u ",
printf(" to-destination %s/%u ",
xtables_ipaddr_to_numeric(&info->addr.in), info->mask);
}
@@ -124,10 +124,10 @@ rawdnat_tg6_print(const void *entry, const struct xt_entry_target *target,
const struct xt_rawnat_tginfo *info = (const void *)target->data;
if (!numeric && info->mask == 128)
printf("to-destination %s ",
printf(" to-destination %s ",
xtables_ip6addr_to_anyname(&info->addr.in6));
else
printf("to-destination %s/%u ",
printf(" to-destination %s/%u ",
xtables_ip6addr_to_numeric(&info->addr.in6), info->mask);
}
@@ -136,7 +136,7 @@ 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 ",
printf(" --to-destination %s/%u ",
xtables_ipaddr_to_numeric(&info->addr.in),
info->mask);
}
@@ -146,7 +146,7 @@ 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 ",
printf(" --to-destination %s/%u ",
xtables_ip6addr_to_numeric(&info->addr.in6),
info->mask);
}

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