Compare commits

...

58 Commits
v1.33 ... v1.38

Author SHA1 Message Date
Jan Engelhardt
65b75fc19c Xtables-addons 1.38 2011-08-20 17:02:31 +02:00
Jan Engelhardt
bac406bff5 ipset-6: unambiguouize reported name 2011-08-20 16:50:41 +02:00
Jan Engelhardt
aa53733851 build: disable ipset-4 by default
This is no longer supported by upstream.
2011-08-20 16:47:40 +02:00
Jan Engelhardt
9ccd32d840 ipset: fix compile error due to changed function signature with Linux 3.1 2011-08-20 16:45:58 +02:00
Jan Engelhardt
939d3c8b27 xt_ipp2p: support UDPLITE 2011-08-16 14:50:53 +02:00
Jan Engelhardt
c2d93e16fd xt_SYSRQ: fix UDPLITE header lookup in IPv6 2011-08-12 15:44:27 +02:00
Jan Engelhardt
04aed87cb6 xt_pknock: support UDPLITE 2011-08-12 15:42:44 +02:00
Jan Engelhardt
5ef3a7c436 xt_CHECKSUM: abort build when the feature is already provided by mainline 2011-08-12 15:42:39 +02:00
Jan Engelhardt
27a77b62f5 Merge branch 'ipset'
Conflicts:
	doc/changelog.txt
2011-08-12 15:37:50 +02:00
Jan Engelhardt
c10e974bd6 Merge branch 'psd' 2011-08-12 15:37:24 +02:00
Jan Engelhardt
01d864f4fc xt_psd: resolve compiler warning
xt_psd.c: In function "xt_psd_match":
xt_psd.c:253:27: warning: "tcph" may be used uninitialized in this
function [-Wuninitialized]
2011-08-11 15:50:08 +02:00
Jan Engelhardt
071c95b750 xt_psd: compact temporary skb buffers 2011-08-11 15:49:40 +02:00
Jan Engelhardt
a141cc311c xt_psd: support UDPLITE 2011-08-11 15:47:20 +02:00
Jan Engelhardt
7e92ce7ce6 xt_psd: move early bail-out code above skb_header_pointer 2011-08-11 15:46:53 +02:00
Jan Engelhardt
21da1dfea5 xt_psd: cleanup and reduce number of condition checks 2011-08-11 15:46:53 +02:00
Jan Engelhardt
6c17eb46b5 xt_psd: restore skb_header_pointer functionality for UDP 2011-08-11 15:46:53 +02:00
Jan Engelhardt
74ea647303 ipset: update to 6.8-genl 2011-07-28 13:56:45 +02:00
Jan Engelhardt
e0154bfa4c xt_TEE: abort build when the feature is already provided by mainline 2011-07-28 13:50:38 +02:00
Jan Engelhardt
cd18e2479c xt_TARPIT: fix kernel warning about RTAX_HOPLIMIT being used 2011-07-26 01:57:45 +02:00
Jan Engelhardt
d2f3541cda xt_LOGMARK: put ct dumping into its own function 2011-07-21 00:18:28 +02:00
Jan Engelhardt
1fed8bbf09 extensions: more precise description 2011-07-17 14:27:07 +02:00
Jan Engelhardt
6e8fb7f231 Xtables-addons 1.37 2011-06-25 00:57:02 +02:00
Jan Engelhardt
eceaee3431 doc: do not advertise old tools
Remove mention of netcat from the libxt_SYSRQ manpage.
2011-06-25 00:05:26 +02:00
John Haxby
77b29a62ee xt_SYSRQ: include host address in digest
The xt_SYSRQ hash now includes the destination IPv4 or IPv6 address
which makes it harder to replay a request to many different machines
in the hope that some of them are using the same password.
2011-06-25 00:03:28 +02:00
John Haxby
33db992c39 xt_SYSRQ: make IPv6 trigger work again
IPv6 sysrq never worked because of bad pointer arithmetic.
2011-06-24 23:42:38 +02:00
Martin Barrowcliff
85d8f98dd7 xt_TARPIT: fix a kernel oops in --reset mode
1. Moved misplaced code that was causing kernel oops in reset mode.

2. Added payload size calc to honeypot mode, so ack sequence may ACK
the length of client's sent payload packets correctly.

3. Modified TTL for honeypot mode so we look more like a Windows
machine.
2011-06-24 22:09:34 +02:00
Peter Volkov
e84391ce66 build: use absolute path for M=
Use absolute path for M during checking kernelrelease. This will force
temporary objects be built in the current directory and not $kbuilddir
as it happened, e.g. in the current kernel scripts/Kbuild.include
try-run target (it is called with TMPOUT=M=. and during call
pwd=$kbuilddir). This should fix sandbox violation in Gentoo:
https://bugs.gentoo.org/show_bug.cgi?id=371997
2011-06-20 10:48:10 +02:00
Peter Volkov
ef7fb0db7f build: fix support for 2.6.x kernels
After commit 75b3762ef4 "WARNING: That
kernel version is not supported." is issued to supported kernels too.
Fix this.
2011-06-20 09:14:51 +02:00
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
108 changed files with 5356 additions and 4210 deletions

14
INSTALL
View File

@@ -14,7 +14,7 @@ Supported configurations for this release
* iptables >= 1.4.3 * iptables >= 1.4.3
* kernel-source >= 2.6.17, no upper bound known * kernel-source >= 2.6.29
with prepared build/output directory with prepared build/output directory
- CONFIG_NF_CONNTRACK or CONFIG_IP_NF_CONNTRACK - CONFIG_NF_CONNTRACK or CONFIG_IP_NF_CONNTRACK
- CONFIG_NF_CONNTRACK_MARK or CONFIG_IP_NF_CONNTRACK_MARK - CONFIG_NF_CONNTRACK_MARK or CONFIG_IP_NF_CONNTRACK_MARK
@@ -22,18 +22,14 @@ Supported configurations for this release
- CONFIG_CONNECTOR y/m if you wish to receive userspace - CONFIG_CONNECTOR y/m if you wish to receive userspace
notifications from pknock through netlink/connector notifications from pknock through netlink/connector
Extra notes: Compilation of ipset-genl-6.x is enabled by default. This additionally
requires
* 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:
* libmnl * libmnl
* Linux kernel >= 2.6.35 * Linux kernel >= 2.6.35
so if you do not have these, turn it off in mconfig before compilation.
Selecting extensions Selecting extensions
==================== ====================

2
README
View File

@@ -19,7 +19,7 @@ simplified, and sped up.
Included in this package Included in this package
======================== ========================
- ipset 4.5 - ipset 4.5
- ipset 5.4.1 - ipset 6.7-genl
- xt_ACCOUNT 1.16, libxt_ACCOUNT 1.3 - xt_ACCOUNT 1.16, libxt_ACCOUNT 1.3

View File

@@ -1,5 +1,4 @@
AC_INIT([xtables-addons], [1.38])
AC_INIT([xtables-addons], [1.33])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_PROG_INSTALL AC_PROG_INSTALL
@@ -40,18 +39,9 @@ regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
-Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \ -Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \
-Winline -pipe"; -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;
if test -n "$kbuilddir"; then if test -n "$kbuilddir"; then
AC_MSG_CHECKING([kernel version that we will build against]) AC_MSG_CHECKING([kernel version that we will build against])
krel="$(make -sC "$kbuilddir" M=. kernelrelease)"; krel="$(make -sC "$kbuilddir" M=$PWD kernelrelease)";
kmajor="${krel%%[[^0-9]]*}"; kmajor="${krel%%[[^0-9]]*}";
kmajor="$(($kmajor+0))"; kmajor="$(($kmajor+0))";
krel="${krel:${#kmajor}}"; krel="${krel:${#kmajor}}";
@@ -70,15 +60,14 @@ if test -n "$kbuilddir"; then
echo "WARNING: Version detection did not succeed. Continue at own luck."; echo "WARNING: Version detection did not succeed. Continue at own luck.";
else else
echo "$kmajor.$kminor.$kmicro.$kstable in $kbuilddir"; echo "$kmajor.$kminor.$kmicro.$kstable in $kbuilddir";
if test "$kmajor" -gt 2 -o "$kminor" -gt 6 -o "$kmicro" -gt 38; 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. :-)"; echo "WARNING: You are trying a newer kernel. Results may vary. :-)";
elif test \( "$kmajor" -lt 2 -o \ elif test "$kmajor" -eq 3; then
\( "$kmajor" -eq 2 -a "$kminor" -lt 6 \) -o \ :;
\( "$kmajor" -eq 2 -a "$kminor" -eq 0 -a "$kmicro" -lt 17 \) -o \ elif test "$kmajor" -eq 2 -a "$kminor" -eq 6 -a "$kmicro" -ge 29; then
\( "$kmajor" -eq 2 -a "$kminor" -eq 6 -a "$kmicro" -eq 18 -a \ :;
"$kstable" -lt 5 \) \); then else
echo "ERROR: That kernel version is not supported. Please see INSTALL for minimum configuration."; echo "WARNING: That kernel version is not supported.";
exit 1;
fi; fi;
fi; fi;
fi; fi;
@@ -89,6 +78,6 @@ AC_SUBST([kbuilddir])
AC_SUBST([xtlibdir]) AC_SUBST([xtlibdir])
AC_CONFIG_FILES([Makefile Makefile.iptrules Makefile.mans geoip/Makefile AC_CONFIG_FILES([Makefile Makefile.iptrules Makefile.mans geoip/Makefile
extensions/Makefile extensions/ACCOUNT/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]) extensions/pknock/Makefile])
AC_OUTPUT AC_OUTPUT

View File

@@ -3,6 +3,65 @@ HEAD
==== ====
v1.38 (2011-08-20)
==================
- xt_CHECKSUM: abort build when the feature is already provided by mainline
- xt_SYSRQ: fix UDPLITE header lookup in IPv6
- xt_TARPIT: fix kernel warning about RTAX_HOPLIMIT being used
- xt_TEE: abort build when the feature is already provided by mainline
- xt_ipp2p: support UDPLITE
- xt_pknock: support UDPLITE
- xt_psd: restore functionality with UDP
- xt_psd: support UDPLITE
- update to ipset 6.8
- support for Linux 3.1
v1.37 (2011-06-25)
==================
Fixes:
- xt_SYSRQ: make IPv6 trigger work again
- xt_SYSRQ: improve security: include host address in digest
- xt_TARPIT: fix a kernel oops in --reset mode
v1.36 (2011-06-03)
==================
Changes:
- xt_geoip: avoid recursive function calls
- xt_TARPIT: unlock for use in all tables
- xt_TARPIT: honeypot and reset modes
- update to ipset 6.7
- support for Linux 3.0
v1.35 (2011-04-11)
==================
Enhancements:
- update to ipset 6.3
* allow "new" as a commad alias to "create"
* resolving IP addresses did not work at listing/saving sets, fixed
* check ICMP and ICMPv6 with the set match and target in the testsuite
* avoid possible syntax clashing at saving hostnames
* fix linking with CONFIG_IPV6=n
* sctp, udplite support for the hash:*port* types
- ipset-genl: handle EAGAIN return value emitted from autoloader
- ipset-genl: resolve nfgenmsg remains and fix spurious protocol abort
v1.34 (2011-04-07)
==================
Fixes:
- xt_pknock: avoid crash when hash TFM could not be allocated
- xt_pknock: avoid inversion of rule lookup that led to warnings
- xt_DNETMAP: add missing module alias
- xt_DNETMAP: support for kernels below 2.6.34
Changes:
- Linux kernel versions below 2.6.29 are no longer officially
supported, and will not be part of compilation testing.
Expect that compat code will be removed shortly.
v1.33 (2011-02-02) v1.33 (2011-02-02)
================== ==================
Fixes: Fixes:

View File

@@ -9,3 +9,5 @@ sbin_PROGRAMS = iptaccount
iptaccount_LDADD = libxt_ACCOUNT_cl.la iptaccount_LDADD = libxt_ACCOUNT_cl.la
lib_LTLIBRARIES = libxt_ACCOUNT_cl.la lib_LTLIBRARIES = libxt_ACCOUNT_cl.la
man_MANS = iptaccount.8

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

@@ -40,19 +40,7 @@ to account the overall traffic to/from your internet provider.
.PP .PP
The data can be queried using the userspace libxt_ACCOUNT_cl library, The data can be queried using the userspace libxt_ACCOUNT_cl library,
and by the reference implementation to show usage of this library, and by the reference implementation to show usage of this library,
the \fBiptaccount\fP(8) tool, which features following options: the \fBiptaccount\fP(8) tool.
.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)
.PP .PP
Here is an example of use: Here is an example of use:
.PP .PP

View File

@@ -28,7 +28,7 @@ obj-${build_geoip} += xt_geoip.o
obj-${build_iface} += xt_iface.o obj-${build_iface} += xt_iface.o
obj-${build_ipp2p} += xt_ipp2p.o obj-${build_ipp2p} += xt_ipp2p.o
obj-${build_ipset4} += ipset-4/ obj-${build_ipset4} += ipset-4/
obj-${build_ipset5} += ipset-5/ obj-${build_ipset6} += ipset-6/
obj-${build_ipv4options} += xt_ipv4options.o obj-${build_ipv4options} += xt_ipv4options.o
obj-${build_length2} += xt_length2.o obj-${build_length2} += xt_length2.o
obj-${build_lscan} += xt_lscan.o obj-${build_lscan} += xt_lscan.o

View File

@@ -20,7 +20,7 @@ obj-${build_geoip} += libxt_geoip.so
obj-${build_iface} += libxt_iface.so obj-${build_iface} += libxt_iface.so
obj-${build_ipp2p} += libxt_ipp2p.so obj-${build_ipp2p} += libxt_ipp2p.so
obj-${build_ipset4} += ipset-4/ obj-${build_ipset4} += ipset-4/
obj-${build_ipset5} += ipset-5/ obj-${build_ipset6} += ipset-6/
obj-${build_ipv4options} += libxt_ipv4options.so obj-${build_ipv4options} += libxt_ipv4options.so
obj-${build_length2} += libxt_length2.so obj-${build_length2} += libxt_length2.so
obj-${build_lscan} += libxt_lscan.so obj-${build_lscan} += libxt_lscan.so

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +0,0 @@
#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);
extern bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
__be16 *port, u8 *proto);
extern bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src,
__be16 *port);
#endif /*_IP_SET_GETPORT_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,7 +4,7 @@ obj-m += xt_set.o
obj-m += ip_set.o ip_set_bitmap_ip.o ip_set_bitmap_ipmac.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_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_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 ip_set-y := ip_set_core.o ip_set_getport.o pfxlen.o

View File

@@ -17,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_bitmap_ipmac.c src/ipset_bitmap_port.c \
src/ipset_hash_ip.c src/ipset_hash_ipport.c \ src/ipset_hash_ip.c src/ipset_hash_ipport.c \
src/ipset_hash_ipportip.c src/ipset_hash_ipportnet.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 src/ipset_list_set.c
ipset_LDADD = libipset.la 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

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_DATA_H #ifndef LIBIPSET_DATA_H
@@ -44,12 +44,15 @@ enum ipset_opt {
IPSET_OPT_NAMEREF, IPSET_OPT_NAMEREF,
IPSET_OPT_IP2, IPSET_OPT_IP2,
IPSET_OPT_CIDR2, IPSET_OPT_CIDR2,
IPSET_OPT_IP2_TO,
IPSET_OPT_PROTO, IPSET_OPT_PROTO,
IPSET_OPT_IFACE,
/* Swap/rename to */ /* Swap/rename to */
IPSET_OPT_SETNAME2, IPSET_OPT_SETNAME2,
/* Flags */ /* Flags */
IPSET_OPT_EXIST, IPSET_OPT_EXIST,
IPSET_OPT_BEFORE, IPSET_OPT_BEFORE,
IPSET_OPT_PHYSDEV,
/* Internal options */ /* Internal options */
IPSET_OPT_FLAGS = 48, /* IPSET_FLAG_EXIST| */ IPSET_OPT_FLAGS = 48, /* IPSET_FLAG_EXIST| */
IPSET_OPT_CADT_FLAGS, /* IPSET_FLAG_BEFORE| */ IPSET_OPT_CADT_FLAGS, /* IPSET_FLAG_BEFORE| */
@@ -65,7 +68,7 @@ enum ipset_opt {
#define IPSET_FLAGS_ALL (~0LL) #define IPSET_FLAGS_ALL (~0LL)
#define IPSET_CREATE_FLAGS \ #define IPSET_CREATE_FLAGS \
( IPSET_FLAG(IPSET_OPT_FAMILY) \ (IPSET_FLAG(IPSET_OPT_FAMILY) \
| IPSET_FLAG(IPSET_OPT_TYPENAME)\ | IPSET_FLAG(IPSET_OPT_TYPENAME)\
| IPSET_FLAG(IPSET_OPT_TYPE) \ | IPSET_FLAG(IPSET_OPT_TYPE) \
| IPSET_FLAG(IPSET_OPT_IP) \ | IPSET_FLAG(IPSET_OPT_IP) \
@@ -83,7 +86,7 @@ enum ipset_opt {
| IPSET_FLAG(IPSET_OPT_SIZE)) | IPSET_FLAG(IPSET_OPT_SIZE))
#define IPSET_ADT_FLAGS \ #define IPSET_ADT_FLAGS \
( IPSET_FLAG(IPSET_OPT_IP) \ (IPSET_FLAG(IPSET_OPT_IP) \
| IPSET_FLAG(IPSET_OPT_IP_TO) \ | IPSET_FLAG(IPSET_OPT_IP_TO) \
| IPSET_FLAG(IPSET_OPT_CIDR) \ | IPSET_FLAG(IPSET_OPT_CIDR) \
| IPSET_FLAG(IPSET_OPT_PORT) \ | IPSET_FLAG(IPSET_OPT_PORT) \
@@ -95,8 +98,10 @@ enum ipset_opt {
| IPSET_FLAG(IPSET_OPT_IP2) \ | IPSET_FLAG(IPSET_OPT_IP2) \
| IPSET_FLAG(IPSET_OPT_CIDR2) \ | IPSET_FLAG(IPSET_OPT_CIDR2) \
| IPSET_FLAG(IPSET_OPT_PROTO) \ | IPSET_FLAG(IPSET_OPT_PROTO) \
| IPSET_FLAG(IPSET_OPT_IFACE) \
| IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\ | IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\
| IPSET_FLAG(IPSET_OPT_BEFORE)) | IPSET_FLAG(IPSET_OPT_BEFORE) \
| IPSET_FLAG(IPSET_OPT_PHYSDEV))
struct ipset_data; struct ipset_data;
@@ -109,8 +114,8 @@ 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, extern int ipset_data_set(struct ipset_data *data, enum ipset_opt opt,
const void *value); 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); enum ipset_opt opt);
static inline bool static inline bool
ipset_data_test(const struct ipset_data *data, enum ipset_opt opt) ipset_data_test(const struct ipset_data *data, enum ipset_opt opt)
@@ -119,13 +124,13 @@ ipset_data_test(const struct ipset_data *data, enum ipset_opt opt)
} }
/* Shortcuts */ /* 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_family(const struct ipset_data *data);
extern uint8_t ipset_data_cidr(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 uint64_t ipset_data_flags(const struct ipset_data *data);
extern void ipset_data_reset(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 void ipset_data_fini(struct ipset_data *data);
extern size_t ipset_data_sizeof(enum ipset_opt opt, uint8_t family); extern size_t ipset_data_sizeof(enum ipset_opt opt, uint8_t family);

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_DEBUG_H #ifndef LIBIPSET_DEBUG_H
@@ -12,7 +12,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#define D(fmt, args...) \ #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...) \ #define IF_D(test, fmt, args...) \
if (test) \ if (test) \
D(fmt , ## args) D(fmt , ## args)

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2008 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2008 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_ERRCODE_H #ifndef LIBIPSET_ERRCODE_H
@@ -13,9 +13,9 @@ struct ipset_session;
/* Kernel error code to message table */ /* Kernel error code to message table */
struct ipset_errcode_table { struct ipset_errcode_table {
int errcode; /* error code returned by the kernel */ int errcode; /* error code returned by the kernel */
enum ipset_cmd cmd; /* issued command */ enum ipset_cmd cmd; /* issued command */
const char *message; /* error message the code translated to */ const char *message; /* error message the code translated to */
}; };
extern int ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, extern int ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd,

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_ICMP_H #ifndef LIBIPSET_ICMP_H
@@ -9,8 +9,8 @@
#include <stdint.h> /* uintxx_t */ #include <stdint.h> /* uintxx_t */
extern const char * id_to_icmp(uint8_t id); extern const char *id_to_icmp(uint8_t id);
extern const char * icmp_to_name(uint8_t type, uint8_t code); extern const char *icmp_to_name(uint8_t type, uint8_t code);
extern int name_to_icmp(const char *str, uint16_t *typecode); extern int name_to_icmp(const char *str, uint16_t *typecode);
#endif /* LIBIPSET_ICMP_H */ #endif /* LIBIPSET_ICMP_H */

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_ICMPV6_H #ifndef LIBIPSET_ICMPV6_H
@@ -9,8 +9,8 @@
#include <stdint.h> /* uintxx_t */ #include <stdint.h> /* uintxx_t */
extern const char * id_to_icmpv6(uint8_t id); extern const char *id_to_icmpv6(uint8_t id);
extern const char * icmpv6_to_name(uint8_t type, uint8_t code); extern const char *icmpv6_to_name(uint8_t type, uint8_t code);
extern int name_to_icmpv6(const char *str, uint16_t *typecode); extern int name_to_icmpv6(const char *str, uint16_t *typecode);
#endif /* LIBIPSET_ICMPV6_H */ #endif /* LIBIPSET_ICMPV6_H */

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_MNL_H #ifndef LIBIPSET_MNL_H

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_NF_INET_ADDR_H #ifndef LIBIPSET_NF_INET_ADDR_H

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_PARSE_H #ifndef LIBIPSET_PARSE_H
@@ -17,12 +17,13 @@
#define IPSET_PROTO_SEPARATOR ":" #define IPSET_PROTO_SEPARATOR ":"
struct ipset_session; struct ipset_session;
struct ipset_arg;
typedef int (*ipset_parsefn)(struct ipset_session *s, typedef int (*ipset_parsefn)(struct ipset_session *s,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_ether(struct ipset_session *session, extern int ipset_parse_ether(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_port(struct ipset_session *session, extern int ipset_parse_port(struct ipset_session *session,
enum ipset_opt opt, const char *str, enum ipset_opt opt, const char *str,
const char *proto); const char *proto);
@@ -34,23 +35,23 @@ extern int ipset_parse_tcp_port(struct ipset_session *session,
extern int ipset_parse_single_tcp_port(struct ipset_session *session, extern int ipset_parse_single_tcp_port(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_proto(struct ipset_session *session, extern int ipset_parse_proto(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_icmp(struct ipset_session *session, extern int ipset_parse_icmp(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_icmpv6(struct ipset_session *session, extern int ipset_parse_icmpv6(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_proto_port(struct ipset_session *session, extern int ipset_parse_proto_port(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_family(struct ipset_session *session, extern int ipset_parse_family(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_ip(struct ipset_session *session, extern int ipset_parse_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_single_ip(struct ipset_session *session, extern int ipset_parse_single_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_net(struct ipset_session *session, extern int ipset_parse_net(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_range(struct ipset_session *session, extern int ipset_parse_range(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_netrange(struct ipset_session *session, extern int ipset_parse_netrange(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_iprange(struct ipset_session *session, extern int ipset_parse_iprange(struct ipset_session *session,
@@ -59,33 +60,37 @@ extern int ipset_parse_ipnet(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_ip4_single6(struct ipset_session *session, extern int ipset_parse_ip4_single6(struct ipset_session *session,
enum ipset_opt opt, const char *str); 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, extern int ipset_parse_name(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_before(struct ipset_session *session, extern int ipset_parse_before(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_after(struct ipset_session *session, extern int ipset_parse_after(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_setname(struct ipset_session *session, extern int ipset_parse_setname(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_uint32(struct ipset_session *session, extern int ipset_parse_uint32(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_uint8(struct ipset_session *session, extern int ipset_parse_uint8(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_netmask(struct ipset_session *session, extern int ipset_parse_netmask(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_flag(struct ipset_session *session, extern int ipset_parse_flag(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_typename(struct ipset_session *session, extern int ipset_parse_typename(struct ipset_session *session,
enum ipset_opt opt, const char *str); 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, extern int ipset_parse_output(struct ipset_session *session,
int opt, const char *str); int opt, const char *str);
extern int ipset_parse_ignored(struct ipset_session *session, extern int ipset_parse_ignored(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_parse_elem(struct ipset_session *session, extern int ipset_parse_elem(struct ipset_session *session,
enum ipset_opt opt, const char *str); enum ipset_opt opt, const char *str);
extern int ipset_call_parser(struct ipset_session *session, extern int ipset_call_parser(struct ipset_session *session,
ipset_parsefn parse, const char *optstr, const struct ipset_arg *arg,
enum ipset_opt optional, const char *str); const char *str);
/* Compatibility parser functions */ /* Compatibility parser functions */
extern int ipset_parse_iptimeout(struct ipset_session *session, extern int ipset_parse_iptimeout(struct ipset_session *session,

View File

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

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_PRINT_H #ifndef LIBIPSET_PRINT_H
@@ -37,6 +37,9 @@ extern int ipset_print_name(char *buf, unsigned int len,
extern int ipset_print_port(char *buf, unsigned int len, extern int ipset_print_port(char *buf, unsigned int len,
const struct ipset_data *data, const struct ipset_data *data,
enum ipset_opt opt, uint8_t env); 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, extern int ipset_print_proto(char *buf, unsigned int len,
const struct ipset_data *data, const struct ipset_data *data,
enum ipset_opt opt, uint8_t env); enum ipset_opt opt, uint8_t env);

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_SESSION_H #ifndef LIBIPSET_SESSION_H
@@ -21,10 +21,14 @@ struct ipset_session;
struct ipset_data; struct ipset_data;
struct ipset_handle; struct ipset_handle;
extern struct ipset_data * ipset_session_data(const struct ipset_session *session); extern struct ipset_data *
extern struct ipset_handle * ipset_session_handle(const struct ipset_session *session); ipset_session_data(const struct ipset_session *session);
extern const struct ipset_type * ipset_saved_type(const struct ipset_session *session); extern struct ipset_handle *
extern void ipset_session_lineno(struct ipset_session *session, uint32_t lineno); 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 { enum ipset_err_type {
IPSET_ERROR, IPSET_ERROR,
@@ -47,8 +51,8 @@ extern int ipset_session_report(struct ipset_session *session,
}) })
extern void ipset_session_report_reset(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_error(const struct ipset_session *session);
extern const char * ipset_session_warning(const struct ipset_session *session); extern const char *ipset_session_warning(const struct ipset_session *session);
#define ipset_session_data_set(session, opt, value) \ #define ipset_session_data_set(session, opt, value) \
ipset_data_set(ipset_session_data(session), opt, value) ipset_data_set(ipset_session_data(session), opt, value)
@@ -65,6 +69,10 @@ enum ipset_envopt {
IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE), IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE),
IPSET_ENV_BIT_EXIST = 3, IPSET_ENV_BIT_EXIST = 3,
IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST), 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, extern int ipset_envopt_parse(struct ipset_session *session,
@@ -89,7 +97,9 @@ extern int ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd,
typedef int (*ipset_outfn)(const char *fmt, ...) typedef int (*ipset_outfn)(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2))); __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 int ipset_session_fini(struct ipset_session *session);
extern void ipset_debug_msg(const char *dir, void *buffer, int len);
#endif /* LIBIPSET_SESSION_H */ #endif /* LIBIPSET_SESSION_H */

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_TRANSPORT_H #ifndef LIBIPSET_TRANSPORT_H
@@ -12,7 +12,7 @@
#include <libmnl/libmnl.h> /* mnl_cb_t */ #include <libmnl/libmnl.h> /* mnl_cb_t */
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */ #include <libipset/linux_ip_set.h> /* enum ipset_cmd */
struct ipset_handle; struct ipset_handle;

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_TYPES_H #ifndef LIBIPSET_TYPES_H
@@ -70,7 +70,7 @@ struct ipset_elem {
* but for the readability the full list is supported. * but for the readability the full list is supported.
*/ */
struct ipset_type { struct ipset_type {
char name[IPSET_MAXNAMELEN]; /* type name */ const char *name;
uint8_t revision; /* revision number */ uint8_t revision; /* revision number */
uint8_t family; /* supported family */ uint8_t family; /* supported family */
uint8_t dimension; /* elem dimension */ 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 int ipset_cache_init(void);
extern void ipset_cache_fini(void); extern void ipset_cache_fini(void);
extern const struct ipset_type * ipset_type_get(struct ipset_session *session, extern const struct ipset_type *
enum ipset_cmd cmd); 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_check(struct ipset_session *session);
extern int ipset_type_add(struct ipset_type *type); extern int ipset_type_add(struct ipset_type *type);
extern const struct ipset_type * ipset_types(void); extern const struct ipset_type *ipset_types(void);
extern const char * ipset_typename_resolve(const char *str); extern const char *ipset_typename_resolve(const char *str);
extern bool ipset_match_typename(const char *str, extern bool ipset_match_typename(const char *str,
const struct ipset_type *t); const struct ipset_type *t);

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_UI_H #ifndef LIBIPSET_UI_H

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef LIBIPSET_UTILS_H #ifndef LIBIPSET_UTILS_H
@@ -11,10 +11,10 @@
#include <netinet/in.h> /* struct in[6]_addr */ #include <netinet/in.h> /* struct in[6]_addr */
/* String equality tests */ /* String equality tests */
#define STREQ(a,b) (strcmp(a,b) == 0) #define STREQ(a, b) (strcmp(a, b) == 0)
#define STRNEQ(a,b,n) (strncmp(a,b,n) == 0) #define STRNEQ(a, b, n) (strncmp(a, b, n) == 0)
#define STRCASEQ(a,b) (strcasecmp(a,b) == 0) #define STRCASEQ(a, b) (strcasecmp(a, b) == 0)
#define STRNCASEQ(a,b,n) (strncasecmp(a,b,n) == 0) #define STRNCASEQ(a, b, n) (strncasecmp(a, b, n) == 0)
/* Stringify tokens */ /* Stringify tokens */
#define _STR(c) #c #define _STR(c) #c

View File

@@ -14,7 +14,7 @@
#include <linux/netlink.h> #include <linux/netlink.h>
/* The protocol version */ /* The protocol version */
#define IPSET_PROTOCOL 5 #define IPSET_PROTOCOL 0x60
/* The max length of strings including NUL: set and type identifiers */ /* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32 #define IPSET_MAXNAMELEN 32
@@ -106,6 +106,8 @@ enum {
IPSET_ATTR_NAMEREF, IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2, IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2, IPSET_ATTR_CIDR2,
IPSET_ATTR_IP2_TO,
IPSET_ATTR_IFACE,
__IPSET_ATTR_ADT_MAX, __IPSET_ATTR_ADT_MAX,
}; };
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) #define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
@@ -120,7 +122,7 @@ enum {
/* Error codes */ /* Error codes */
enum ipset_errno { enum ipset_errno {
IPSET_ERR_PRIVATE = 128, IPSET_ERR_PRIVATE = 4096,
IPSET_ERR_PROTOCOL, IPSET_ERR_PROTOCOL,
IPSET_ERR_FIND_TYPE, IPSET_ERR_FIND_TYPE,
IPSET_ERR_MAX_SETS, IPSET_ERR_MAX_SETS,
@@ -137,19 +139,25 @@ enum ipset_errno {
IPSET_ERR_IPADDR_IPV6, IPSET_ERR_IPADDR_IPV6,
/* Type specific error codes */ /* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 160, IPSET_ERR_TYPE_SPECIFIC = 4352,
}; };
/* Flags at command level */ /* Flags at command level */
enum ipset_cmd_flags { enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0, IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), 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 */ /* Flags at CADT attribute level */
enum ipset_cadt_flags { enum ipset_cadt_flags {
IPSET_FLAG_BIT_BEFORE = 0, IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), 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 */ /* Commands with settype-specific attributes */
@@ -167,6 +175,7 @@ enum ipset_adt {
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter/x_tables.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <net/netlink.h> #include <net/netlink.h>
@@ -208,6 +217,8 @@ enum ip_set_feature {
IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG), IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG),
IPSET_TYPE_NAME_FLAG = 4, IPSET_TYPE_NAME_FLAG = 4,
IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG), 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: /* Strictly speaking not a feature, but a flag for dumping:
* this settype must be dumped last */ * this settype must be dumped last */
IPSET_DUMP_LAST_FLAG = 7, IPSET_DUMP_LAST_FLAG = 7,
@@ -216,7 +227,17 @@ enum ip_set_feature {
struct ip_set; 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 */ /* Set type, variant-specific part */
struct ip_set_type_variant { struct ip_set_type_variant {
@@ -225,14 +246,15 @@ struct ip_set_type_variant {
* zero for no match/success to add/delete * zero for no match/success to add/delete
* positive for matching element */ * positive for matching element */
int (*kadt)(struct ip_set *set, const struct sk_buff * skb, 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 /* Userspace: test/add/del entries
* returns negative error code, * returns negative error code,
* zero for no match/success to add/delete * zero for no match/success to add/delete
* positive for matching element */ * positive for matching element */
int (*uadt)(struct ip_set *set, struct nlattr *head, int len, int (*uadt)(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags); enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
/* Low level add/del/test functions */ /* Low level add/del/test functions */
ipset_adtfn adt[IPSET_ADT_MAX]; ipset_adtfn adt[IPSET_ADT_MAX];
@@ -270,12 +292,15 @@ struct ip_set_type {
u8 dimension; u8 dimension;
/* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */ /* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */
u8 family; u8 family;
/* Type revision */ /* Type revisions */
u8 revision; u8 revision_min, revision_max;
/* Create set */ /* Create set */
int (*create)(struct ip_set *set, int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags);
struct nlattr *head, int len, 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 */ /* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me; struct module *me;
@@ -292,13 +317,15 @@ struct ip_set {
/* Lock protecting the set data */ /* Lock protecting the set data */
rwlock_t lock; rwlock_t lock;
/* References to the set */ /* References to the set */
atomic_t ref; u32 ref;
/* The core set type */ /* The core set type */
struct ip_set_type *type; struct ip_set_type *type;
/* The type variant doing the real job */ /* The type variant doing the real job */
const struct ip_set_type_variant *variant; const struct ip_set_type_variant *variant;
/* The actual INET family of the set */ /* The actual INET family of the set */
u8 family; u8 family;
/* The type revision */
u8 revision;
/* The type specific data */ /* The type specific data */
void *data; void *data;
}; };
@@ -306,21 +333,25 @@ struct ip_set {
/* register and unregister set references */ /* register and unregister set references */
extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set); 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 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(const char *name);
extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); 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); extern void ip_set_nfnl_put(ip_set_id_t index);
/* API for iptables set match, and SET target */ /* API for iptables set match, and SET target */
extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb, 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, 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, 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);
/* Utility functions */ /* Utility functions */
extern void * ip_set_alloc(size_t size, gfp_t gfp_mask); extern void *ip_set_alloc(size_t size);
extern void ip_set_free(void *members); extern void ip_set_free(void *members);
extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr); 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); extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr);
@@ -330,7 +361,7 @@ ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr)
{ {
__be32 ip; __be32 ip;
int ret = ip_set_get_ipaddr4(nla, &ip); int ret = ip_set_get_ipaddr4(nla, &ip);
if (ret) if (ret)
return ret; return ret;
*ipaddr = ntohl(ip); *ipaddr = ntohl(ip);

View File

@@ -5,6 +5,11 @@
#include "jhash.h" #include "jhash.h"
#include "ip_set_timeout.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 /* Hashing which uses arrays to resolve clashing. The hash table is resized
* (doubled) when searching becomes too long. * (doubled) when searching becomes too long.
* Internally jhash is used with the assumption that the size of the * Internally jhash is used with the assumption that the size of the
@@ -23,7 +28,32 @@
/* Number of elements to store in an initial array block */ /* Number of elements to store in an initial array block */
#define AHASH_INIT_SIZE 4 #define AHASH_INIT_SIZE 4
/* Max number of elements to store in an array block */ /* Max number of elements to store in an array block */
#define AHASH_MAX_SIZE (3*4) #define AHASH_MAX_SIZE (3*AHASH_INIT_SIZE)
/* Max number of elements can be tuned */
#ifdef IP_SET_HASH_WITH_MULTI
#define AHASH_MAX(h) ((h)->ahash_max)
static inline u8
tune_ahash_max(u8 curr, u32 multi)
{
u32 n;
if (multi < curr)
return curr;
n = curr + AHASH_INIT_SIZE;
/* Currently, at listing one hash bucket must fit into a message.
* Therefore we have a hard limit here.
*/
return n > curr && n <= 64 ? n : curr;
}
#define TUNE_AHASH_MAX(h, multi) \
((h)->ahash_max = tune_ahash_max((h)->ahash_max, multi))
#else
#define AHASH_MAX(h) AHASH_MAX_SIZE
#define TUNE_AHASH_MAX(h, multi)
#endif
/* A hash bucket */ /* A hash bucket */
struct hbucket { struct hbucket {
@@ -38,7 +68,7 @@ struct htable {
struct hbucket bucket[0]; /* hashtable buckets */ 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 */ /* Book-keeping of the prefixes added to the set */
struct ip_set_hash_nets { struct ip_set_hash_nets {
@@ -54,9 +84,16 @@ struct ip_set_hash {
u32 initval; /* random jhash init value */ u32 initval; /* random jhash init value */
u32 timeout; /* timeout value, if enabled */ u32 timeout; /* timeout value, if enabled */
struct timer_list gc; /* garbage collection when timeout enabled */ struct timer_list gc; /* garbage collection when timeout enabled */
struct type_pf_next next; /* temporary storage for uadd */
#ifdef IP_SET_HASH_WITH_MULTI
u8 ahash_max; /* max elements in an array block */
#endif
#ifdef IP_SET_HASH_WITH_NETMASK #ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask; /* netmask value for subnets to store */ u8 netmask; /* netmask value for subnets to store */
#endif #endif
#ifdef IP_SET_HASH_WITH_RBTREE
struct rb_root rbtree;
#endif
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
struct ip_set_hash_nets nets[0]; /* book-keeping of prefixes */ struct ip_set_hash_nets nets[0]; /* book-keeping of prefixes */
#endif #endif
@@ -194,17 +231,24 @@ ip_set_hash_destroy(struct ip_set *set)
del_timer_sync(&h->gc); del_timer_sync(&h->gc);
ahash_destroy(h->table); ahash_destroy(h->table);
#ifdef IP_SET_HASH_WITH_RBTREE
rbtree_destroy(&h->rbtree);
#endif
kfree(h); kfree(h);
set->data = NULL; set->data = NULL;
} }
#define HKEY(data, initval, htable_bits) \
(jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \
& jhash_mask(htable_bits))
#endif /* _IP_SET_AHASH_H */ #endif /* _IP_SET_AHASH_H */
#ifndef HKEY_DATALEN
#define HKEY_DATALEN sizeof(struct type_pf_elem)
#endif
#define HKEY(data, initval, htable_bits) \
(jhash2((u32 *)(data), HKEY_DATALEN/sizeof(u32), initval) \
& jhash_mask(htable_bits))
#define CONCAT(a, b, c) a##b##c #define CONCAT(a, b, c) a##b##c
#define TOKEN(a, b, c) CONCAT(a, b, c) #define TOKEN(a, b, c) CONCAT(a, b, c)
@@ -217,6 +261,7 @@ ip_set_hash_destroy(struct ip_set *set)
#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask) #define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask)
#define type_pf_data_list TOKEN(TYPE, PF, _data_list) #define type_pf_data_list TOKEN(TYPE, PF, _data_list)
#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist) #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_elem TOKEN(TYPE, PF, _elem)
#define type_pf_telem TOKEN(TYPE, PF, _telem) #define type_pf_telem TOKEN(TYPE, PF, _telem)
@@ -262,12 +307,13 @@ ip_set_hash_destroy(struct ip_set *set)
/* Add an element to the hash table when resizing the set: /* Add an element to the hash table when resizing the set:
* we spare the maintenance of the internal counters. */ * we spare the maintenance of the internal counters. */
static int static int
type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value) type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value,
u8 ahash_max)
{ {
if (n->pos >= n->size) { if (n->pos >= n->size) {
void *tmp; void *tmp;
if (n->size >= AHASH_MAX_SIZE) if (n->size >= ahash_max)
/* Trigger rehashing */ /* Trigger rehashing */
return -EAGAIN; return -EAGAIN;
@@ -311,8 +357,7 @@ retry:
/* In case we have plenty of memory :-) */ /* In case we have plenty of memory :-) */
return -IPSET_ERR_HASH_FULL; return -IPSET_ERR_HASH_FULL;
t = ip_set_alloc(sizeof(*t) t = ip_set_alloc(sizeof(*t)
+ jhash_size(htable_bits) * sizeof(struct hbucket), + jhash_size(htable_bits) * sizeof(struct hbucket));
GFP_KERNEL);
if (!t) if (!t)
return -ENOMEM; return -ENOMEM;
t->htable_bits = htable_bits; t->htable_bits = htable_bits;
@@ -323,7 +368,7 @@ retry:
for (j = 0; j < n->pos; j++) { for (j = 0; j < n->pos; j++) {
data = ahash_data(n, j); data = ahash_data(n, j);
m = hbucket(t, HKEY(data, h->initval, htable_bits)); m = hbucket(t, HKEY(data, h->initval, htable_bits));
ret = type_pf_elem_add(m, data); ret = type_pf_elem_add(m, data, AHASH_MAX(h));
if (ret < 0) { if (ret < 0) {
read_unlock_bh(&set->lock); read_unlock_bh(&set->lock);
ahash_destroy(t); ahash_destroy(t);
@@ -347,17 +392,20 @@ retry:
return 0; return 0;
} }
static inline 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, /* Add an element to a hash and update the internal counters when succeeded,
* otherwise report the proper error code. */ * otherwise report the proper error code. */
static int 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 ip_set_hash *h = set->data;
struct htable *t; struct htable *t;
const struct type_pf_elem *d = value; const struct type_pf_elem *d = value;
struct hbucket *n; struct hbucket *n;
int i, ret = 0; int i, ret = 0;
u32 key; u32 key, multi = 0;
if (h->elements >= h->maxelem) if (h->elements >= h->maxelem)
return -IPSET_ERR_HASH_FULL; return -IPSET_ERR_HASH_FULL;
@@ -367,14 +415,17 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout)
key = HKEY(value, h->initval, t->htable_bits); key = HKEY(value, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) for (i = 0; i < n->pos; i++)
if (type_pf_data_equal(ahash_data(n, i), d)) { if (type_pf_data_equal(ahash_data(n, i), d, &multi)) {
ret = -IPSET_ERR_EXIST; ret = -IPSET_ERR_EXIST;
goto out; goto out;
} }
TUNE_AHASH_MAX(h, multi);
ret = type_pf_elem_add(n, value); ret = type_pf_elem_add(n, value, AHASH_MAX(h));
if (ret != 0) if (ret != 0) {
if (ret == -EAGAIN)
type_pf_data_next(h, d);
goto out; goto out;
}
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
add_cidr(h, d->cidr, HOST_MASK); add_cidr(h, d->cidr, HOST_MASK);
@@ -389,7 +440,7 @@ out:
* and free up space if possible. * and free up space if possible.
*/ */
static int 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 ip_set_hash *h = set->data;
struct htable *t = h->table; struct htable *t = h->table;
@@ -397,13 +448,13 @@ type_pf_del(struct ip_set *set, void *value, u32 timeout)
struct hbucket *n; struct hbucket *n;
int i; int i;
struct type_pf_elem *data; struct type_pf_elem *data;
u32 key; u32 key, multi = 0;
key = HKEY(value, h->initval, t->htable_bits); key = HKEY(value, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i); data = ahash_data(n, i);
if (!type_pf_data_equal(data, d)) if (!type_pf_data_equal(data, d, &multi))
continue; continue;
if (i != n->pos - 1) if (i != n->pos - 1)
/* Not last one */ /* Not last one */
@@ -444,17 +495,17 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
struct hbucket *n; struct hbucket *n;
const struct type_pf_elem *data; const struct type_pf_elem *data;
int i, j = 0; int i, j = 0;
u32 key; u32 key, multi = 0;
u8 host_mask = SET_HOST_MASK(set->family); u8 host_mask = SET_HOST_MASK(set->family);
pr_debug("test by nets\n"); pr_debug("test by nets\n");
for (; j < host_mask && h->nets[j].cidr; j++) { for (; j < host_mask && h->nets[j].cidr && !multi; j++) {
type_pf_data_netmask(d, h->nets[j].cidr); type_pf_data_netmask(d, h->nets[j].cidr);
key = HKEY(d, h->initval, t->htable_bits); key = HKEY(d, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i); data = ahash_data(n, i);
if (type_pf_data_equal(data, d)) if (type_pf_data_equal(data, d, &multi))
return 1; return 1;
} }
} }
@@ -464,7 +515,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 */ /* Test whether the element is added to the set */
static int 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 ip_set_hash *h = set->data;
struct htable *t = h->table; struct htable *t = h->table;
@@ -472,7 +523,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout)
struct hbucket *n; struct hbucket *n;
const struct type_pf_elem *data; const struct type_pf_elem *data;
int i; int i;
u32 key; u32 key, multi = 0;
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
/* If we test an IP address and not a network address, /* If we test an IP address and not a network address,
@@ -485,7 +536,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout)
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i); data = ahash_data(n, i);
if (type_pf_data_equal(data, d)) if (type_pf_data_equal(data, d, &multi))
return 1; return 1;
} }
return 0; return 0;
@@ -516,8 +567,7 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb)
if (h->netmask != HOST_MASK) if (h->netmask != HOST_MASK)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask); NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask);
#endif #endif
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)); NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize));
if (with_timeout(h->timeout)) if (with_timeout(h->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout)); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout));
@@ -525,7 +575,7 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb)
return 0; return 0;
nla_put_failure: nla_put_failure:
return -EFAULT; return -EMSGSIZE;
} }
/* Reply a LIST/SAVE request: dump the elements of the specified set */ /* Reply a LIST/SAVE request: dump the elements of the specified set */
@@ -545,7 +595,7 @@ type_pf_list(const struct ip_set *set,
atd = ipset_nest_start(skb, IPSET_ATTR_ADT); atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd) if (!atd)
return -EFAULT; return -EMSGSIZE;
pr_debug("list hash set %s\n", set->name); pr_debug("list hash set %s\n", set->name);
for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) {
incomplete = skb_tail_pointer(skb); incomplete = skb_tail_pointer(skb);
@@ -559,7 +609,7 @@ type_pf_list(const struct ip_set *set,
if (!nested) { if (!nested) {
if (cb->args[2] == first) { if (cb->args[2] == first) {
nla_nest_cancel(skb, atd); nla_nest_cancel(skb, atd);
return -EFAULT; return -EMSGSIZE;
} else } else
goto nla_put_failure; goto nla_put_failure;
} }
@@ -581,16 +631,18 @@ nla_put_failure:
pr_warning("Can't list set %s: one bucket does not fit into " pr_warning("Can't list set %s: one bucket does not fit into "
"a message. Please report it!\n", set->name); "a message. Please report it!\n", set->name);
cb->args[2] = 0; cb->args[2] = 0;
return -EMSGSIZE;
} }
return 0; return 0;
} }
static int static int
type_pf_kadt(struct ip_set *set, const struct sk_buff * skb, 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 static int
type_pf_uadt(struct ip_set *set, struct nlattr *head, int len, type_pf_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags); enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
static const struct ip_set_type_variant type_pf_variant = { static const struct ip_set_type_variant type_pf_variant = {
.kadt = type_pf_kadt, .kadt = type_pf_kadt,
@@ -641,14 +693,14 @@ type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout)
static int static int
type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value, type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value,
u32 timeout) u8 ahash_max, u32 timeout)
{ {
struct type_pf_elem *data; struct type_pf_elem *data;
if (n->pos >= n->size) { if (n->pos >= n->size) {
void *tmp; void *tmp;
if (n->size >= AHASH_MAX_SIZE) if (n->size >= ahash_max)
/* Trigger rehashing */ /* Trigger rehashing */
return -EAGAIN; return -EAGAIN;
@@ -742,8 +794,7 @@ retry:
/* In case we have plenty of memory :-) */ /* In case we have plenty of memory :-) */
return -IPSET_ERR_HASH_FULL; return -IPSET_ERR_HASH_FULL;
t = ip_set_alloc(sizeof(*t) t = ip_set_alloc(sizeof(*t)
+ jhash_size(htable_bits) * sizeof(struct hbucket), + jhash_size(htable_bits) * sizeof(struct hbucket));
GFP_KERNEL);
if (!t) if (!t)
return -ENOMEM; return -ENOMEM;
t->htable_bits = htable_bits; t->htable_bits = htable_bits;
@@ -754,7 +805,7 @@ retry:
for (j = 0; j < n->pos; j++) { for (j = 0; j < n->pos; j++) {
data = ahash_tdata(n, j); data = ahash_tdata(n, j);
m = hbucket(t, HKEY(data, h->initval, htable_bits)); m = hbucket(t, HKEY(data, h->initval, htable_bits));
ret = type_pf_elem_tadd(m, data, ret = type_pf_elem_tadd(m, data, AHASH_MAX(h),
type_pf_data_timeout(data)); type_pf_data_timeout(data));
if (ret < 0) { if (ret < 0) {
read_unlock_bh(&set->lock); read_unlock_bh(&set->lock);
@@ -778,15 +829,16 @@ retry:
} }
static int 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 ip_set_hash *h = set->data;
struct htable *t = h->table; struct htable *t = h->table;
const struct type_pf_elem *d = value; const struct type_pf_elem *d = value;
struct hbucket *n; struct hbucket *n;
struct type_pf_elem *data; struct type_pf_elem *data;
int ret = 0, i, j = AHASH_MAX_SIZE + 1; int ret = 0, i, j = AHASH_MAX(h) + 1;
u32 key; bool flag_exist = flags & IPSET_FLAG_EXIST;
u32 key, multi = 0;
if (h->elements >= h->maxelem) if (h->elements >= h->maxelem)
/* FIXME: when set is full, we slow down here */ /* FIXME: when set is full, we slow down here */
@@ -800,18 +852,18 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout)
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i); data = ahash_tdata(n, i);
if (type_pf_data_equal(data, d)) { if (type_pf_data_equal(data, d, &multi)) {
if (type_pf_data_expired(data)) if (type_pf_data_expired(data) || flag_exist)
j = i; j = i;
else { else {
ret = -IPSET_ERR_EXIST; ret = -IPSET_ERR_EXIST;
goto out; goto out;
} }
} else if (j == AHASH_MAX_SIZE + 1 && } else if (j == AHASH_MAX(h) + 1 &&
type_pf_data_expired(data)) type_pf_data_expired(data))
j = i; j = i;
} }
if (j != AHASH_MAX_SIZE + 1) { if (j != AHASH_MAX(h) + 1) {
data = ahash_tdata(n, j); data = ahash_tdata(n, j);
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
del_cidr(h, data->cidr, HOST_MASK); del_cidr(h, data->cidr, HOST_MASK);
@@ -821,9 +873,13 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout)
type_pf_data_timeout_set(data, timeout); type_pf_data_timeout_set(data, timeout);
goto out; goto out;
} }
ret = type_pf_elem_tadd(n, d, timeout); TUNE_AHASH_MAX(h, multi);
if (ret != 0) ret = type_pf_elem_tadd(n, d, AHASH_MAX(h), timeout);
if (ret != 0) {
if (ret == -EAGAIN)
type_pf_data_next(h, d);
goto out; goto out;
}
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
add_cidr(h, d->cidr, HOST_MASK); add_cidr(h, d->cidr, HOST_MASK);
@@ -835,24 +891,24 @@ out:
} }
static int 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 ip_set_hash *h = set->data;
struct htable *t = h->table; struct htable *t = h->table;
const struct type_pf_elem *d = value; const struct type_pf_elem *d = value;
struct hbucket *n; struct hbucket *n;
int i, ret = 0; int i;
struct type_pf_elem *data; struct type_pf_elem *data;
u32 key; u32 key, multi = 0;
key = HKEY(value, h->initval, t->htable_bits); key = HKEY(value, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i); data = ahash_tdata(n, i);
if (!type_pf_data_equal(data, d)) if (!type_pf_data_equal(data, d, &multi))
continue; continue;
if (type_pf_data_expired(data)) if (type_pf_data_expired(data))
ret = -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
if (i != n->pos - 1) if (i != n->pos - 1)
/* Not last one */ /* Not last one */
type_pf_data_copy(data, ahash_tdata(n, n->pos - 1)); type_pf_data_copy(data, ahash_tdata(n, n->pos - 1));
@@ -889,16 +945,16 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
struct type_pf_elem *data; struct type_pf_elem *data;
struct hbucket *n; struct hbucket *n;
int i, j = 0; int i, j = 0;
u32 key; u32 key, multi = 0;
u8 host_mask = SET_HOST_MASK(set->family); u8 host_mask = SET_HOST_MASK(set->family);
for (; j < host_mask && h->nets[j].cidr; j++) { for (; j < host_mask && h->nets[j].cidr && !multi; j++) {
type_pf_data_netmask(d, h->nets[j].cidr); type_pf_data_netmask(d, h->nets[j].cidr);
key = HKEY(d, h->initval, t->htable_bits); key = HKEY(d, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i); data = ahash_tdata(n, i);
if (type_pf_data_equal(data, d)) if (type_pf_data_equal(data, d, &multi))
return !type_pf_data_expired(data); return !type_pf_data_expired(data);
} }
} }
@@ -907,14 +963,14 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
#endif #endif
static int 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 ip_set_hash *h = set->data;
struct htable *t = h->table; struct htable *t = h->table;
struct type_pf_elem *data, *d = value; struct type_pf_elem *data, *d = value;
struct hbucket *n; struct hbucket *n;
int i; int i;
u32 key; u32 key, multi = 0;
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
if (d->cidr == SET_HOST_MASK(set->family)) if (d->cidr == SET_HOST_MASK(set->family))
@@ -924,7 +980,7 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout)
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i); data = ahash_tdata(n, i);
if (type_pf_data_equal(data, d)) if (type_pf_data_equal(data, d, &multi))
return !type_pf_data_expired(data); return !type_pf_data_expired(data);
} }
return 0; return 0;
@@ -946,7 +1002,7 @@ type_pf_tlist(const struct ip_set *set,
atd = ipset_nest_start(skb, IPSET_ATTR_ADT); atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd) if (!atd)
return -EFAULT; return -EMSGSIZE;
for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) {
incomplete = skb_tail_pointer(skb); incomplete = skb_tail_pointer(skb);
n = hbucket(t, cb->args[2]); n = hbucket(t, cb->args[2]);
@@ -960,7 +1016,7 @@ type_pf_tlist(const struct ip_set *set,
if (!nested) { if (!nested) {
if (cb->args[2] == first) { if (cb->args[2] == first) {
nla_nest_cancel(skb, atd); nla_nest_cancel(skb, atd);
return -EFAULT; return -EMSGSIZE;
} else } else
goto nla_put_failure; goto nla_put_failure;
} }
@@ -982,6 +1038,7 @@ nla_put_failure:
pr_warning("Can't list set %s: one bucket does not fit into " pr_warning("Can't list set %s: one bucket does not fit into "
"a message. Please report it!\n", set->name); "a message. Please report it!\n", set->name);
cb->args[2] = 0; cb->args[2] = 0;
return -EMSGSIZE;
} }
return 0; return 0;
} }
@@ -1031,6 +1088,8 @@ type_pf_gc_init(struct ip_set *set)
IPSET_GC_PERIOD(h->timeout)); IPSET_GC_PERIOD(h->timeout));
} }
#undef HKEY_DATALEN
#undef HKEY
#undef type_pf_data_equal #undef type_pf_data_equal
#undef type_pf_data_isnull #undef type_pf_data_isnull
#undef type_pf_data_copy #undef type_pf_data_copy

View File

@@ -13,7 +13,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/netlink.h> #include <linux/netlink.h>
@@ -33,8 +32,7 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("bitmap:ip type of IP sets"); MODULE_DESCRIPTION("bitmap:ip type of IP sets");
MODULE_ALIAS("ip_set_bitmap:ip"); MODULE_ALIAS("ip_set_bitmap:ip");
/* Base variant */ /* Type structure */
struct bitmap_ip { struct bitmap_ip {
void *members; /* the set members */ void *members; /* the set members */
u32 first_ip; /* host byte order, included in range */ u32 first_ip; /* host byte order, included in range */
@@ -43,32 +41,45 @@ struct bitmap_ip {
u32 hosts; /* number of hosts in a subnet */ u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */ size_t memsize; /* members size */
u8 netmask; /* subnet netmask */ u8 netmask; /* subnet netmask */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
}; };
/* Base variant */
static inline u32 static inline u32
ip_to_id(const struct bitmap_ip *m, u32 ip) ip_to_id(const struct bitmap_ip *m, u32 ip)
{ {
return ((ip & ip_set_hostmask(m->netmask)) - m->first_ip)/m->hosts; return ((ip & ip_set_hostmask(m->netmask)) - m->first_ip)/m->hosts;
} }
static inline int static int
bitmap_ip_test(const struct bitmap_ip *map, u32 id) 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); return !!test_bit(id, map->members);
} }
static inline int static int
bitmap_ip_add(struct bitmap_ip *map, u32 id) 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)) if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
return 0; return 0;
} }
static inline int static int
bitmap_ip_del(struct bitmap_ip *map, u32 id) 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)) if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
@@ -76,52 +87,166 @@ bitmap_ip_del(struct bitmap_ip *map, u32 id)
} }
static int static int
bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, bitmap_ip_list(const struct ip_set *set,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags) 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; 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; 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) if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE; return -IPSET_ERR_BITMAP_RANGE;
ip = ip_to_id(map, ip); ip = ip_to_id(map, ip);
switch (adt) { return adtfn(set, &ip, opt_timeout(opt, map), opt->cmdflags);
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;
}
} }
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 static int
bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
struct bitmap_ip *map = set->data; 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; u32 ip, ip_to, id;
int ret = 0; int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, if (unlikely(!tb[IPSET_ATTR_IP] ||
bitmap_ip_adt_policy)) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP]))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
@@ -134,13 +259,16 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
if (ip < map->first_ip || ip > map->last_ip) if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE; return -IPSET_ERR_BITMAP_RANGE;
/* Set was defined without timeout support: if (tb[IPSET_ATTR_TIMEOUT]) {
* don't ignore the attribute silently */ if (!with_timeout(map->timeout))
if (tb[IPSET_ATTR_TIMEOUT]) return -IPSET_ERR_TIMEOUT;
return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST) if (adt == IPSET_TEST) {
return bitmap_ip_test(map, ip_to_id(map, ip)); id = ip_to_id(map, ip);
return adtfn(set, &id, timeout, flags);
}
if (tb[IPSET_ATTR_IP_TO]) { if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
@@ -156,8 +284,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32) if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
ip &= ip_set_hostmask(cidr); ip_set_mask_from_to(ip, ip_to, cidr);
ip_to = ip | ~ip_set_hostmask(cidr);
} else } else
ip_to = ip; ip_to = ip;
@@ -166,8 +293,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
for (; !before(ip_to, ip); ip += map->hosts) { for (; !before(ip_to, ip); ip += map->hosts) {
id = ip_to_id(map, ip); id = ip_to_id(map, ip);
ret = adt == IPSET_ADD ? bitmap_ip_add(map, id) ret = adtfn(set, &id, timeout, flags);
: bitmap_ip_del(map, id);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
@@ -182,6 +308,9 @@ bitmap_ip_destroy(struct ip_set *set)
{ {
struct bitmap_ip *map = set->data; struct bitmap_ip *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
ip_set_free(map->members); ip_set_free(map->members);
kfree(map); kfree(map);
@@ -209,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)); NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
if (map->netmask != 32) if (map->netmask != 32)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask);
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->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); ipset_nest_end(skb, nested);
return 0; return 0;
nla_put_failure: nla_put_failure:
return -EFAULT; return -EMSGSIZE;
}
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;
} }
static bool static bool
@@ -266,12 +358,18 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
return x->first_ip == y->first_ip && return x->first_ip == y->first_ip &&
x->last_ip == y->last_ip && x->last_ip == y->last_ip &&
x->netmask == y->netmask; x->netmask == y->netmask &&
x->timeout == y->timeout;
} }
static const struct ip_set_type_variant bitmap_ip = { static const struct ip_set_type_variant bitmap_ip = {
.kadt = bitmap_ip_kadt, .kadt = bitmap_ip_kadt,
.uadt = bitmap_ip_uadt, .uadt = bitmap_ip_uadt,
.adt = {
[IPSET_ADD] = bitmap_ip_add,
[IPSET_DEL] = bitmap_ip_del,
[IPSET_TEST] = bitmap_ip_test,
},
.destroy = bitmap_ip_destroy, .destroy = bitmap_ip_destroy,
.flush = bitmap_ip_flush, .flush = bitmap_ip_flush,
.head = bitmap_ip_head, .head = bitmap_ip_head,
@@ -279,261 +377,26 @@ static const struct ip_set_type_variant bitmap_ip = {
.same_set = bitmap_ip_same_set, .same_set = bitmap_ip_same_set,
}; };
/* Timeout variant */ static const struct ip_set_type_variant bitmap_tip = {
.kadt = bitmap_ip_kadt,
struct bitmap_ip_timeout { .uadt = bitmap_ip_uadt,
unsigned long *members; /* the set members */ .adt = {
u32 first_ip; /* host byte order, included in range */ [IPSET_ADD] = bitmap_ip_tadd,
u32 last_ip; /* host byte order, included in range */ [IPSET_DEL] = bitmap_ip_tdel,
u32 elements; /* number of max elements in the set */ [IPSET_TEST] = bitmap_ip_ttest,
u32 hosts; /* number of hosts in a subnet */ },
size_t memsize; /* members size */ .destroy = bitmap_ip_destroy,
u8 netmask; /* subnet netmask */ .flush = bitmap_ip_flush,
.head = bitmap_ip_head,
u32 timeout; /* timeout parameter */ .list = bitmap_ip_tlist,
struct timer_list gc; /* garbage collection */ .same_set = bitmap_ip_same_set,
};
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 (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_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
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_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
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 &= ip_set_hostmask(cidr);
ip_to = ip | ~ip_set_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 void static void
bitmap_ip_gc(unsigned long ul_set) bitmap_ip_gc(unsigned long ul_set)
{ {
struct ip_set *set = (struct ip_set *) 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; unsigned long *table = map->members;
u32 id; u32 id;
@@ -552,7 +415,7 @@ bitmap_ip_gc(unsigned long ul_set)
static void static void
bitmap_ip_gc_init(struct ip_set *set) 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); init_timer(&map->gc);
map->gc.data = (unsigned long) set; map->gc.data = (unsigned long) set;
@@ -563,21 +426,12 @@ bitmap_ip_gc_init(struct ip_set *set)
/* Create bitmap:ip type of sets */ /* 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 static bool
init_map_ip(struct ip_set *set, struct bitmap_ip *map, init_map_ip(struct ip_set *set, struct bitmap_ip *map,
u32 first_ip, u32 last_ip, u32 first_ip, u32 last_ip,
u32 elements, u32 hosts, u8 netmask) 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) if (!map->members)
return false; return false;
map->first_ip = first_ip; map->first_ip = first_ip;
@@ -585,6 +439,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
map->elements = elements; map->elements = elements;
map->hosts = hosts; map->hosts = hosts;
map->netmask = netmask; map->netmask = netmask;
map->timeout = IPSET_NO_TIMEOUT;
set->data = map; set->data = map;
set->family = AF_INET; set->family = AF_INET;
@@ -593,18 +448,13 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
} }
static int static int
bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len, bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
u32 flags)
{ {
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1]; struct bitmap_ip *map;
u32 first_ip, last_ip, hosts, elements; u32 first_ip, last_ip, hosts, elements;
u8 netmask = 32; u8 netmask = 32;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_ip_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@@ -628,7 +478,7 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
if (cidr >= 32) if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
last_ip = first_ip | ~ip_set_hostmask(cidr); ip_set_mask_from_to(first_ip, last_ip, cidr);
} else } else
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@@ -664,37 +514,27 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
pr_debug("hosts %u, elements %u\n", hosts, elements); pr_debug("hosts %u, elements %u\n", hosts, elements);
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
struct bitmap_ip_timeout *map;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map->memsize = elements * sizeof(unsigned long); map->memsize = elements * sizeof(unsigned long);
if (!init_map_ip(set, (struct bitmap_ip *)map, if (!init_map_ip(set, map, first_ip, last_ip,
first_ip, last_ip,
elements, hosts, netmask)) { elements, hosts, netmask)) {
kfree(map); kfree(map);
return -ENOMEM; return -ENOMEM;
} }
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_ip_timeout; set->variant = &bitmap_tip;
bitmap_ip_gc_init(set); bitmap_ip_gc_init(set);
} else { } else {
struct bitmap_ip *map;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map->memsize = bitmap_bytes(0, elements - 1); map->memsize = bitmap_bytes(0, elements - 1);
if (!init_map_ip(set, map, if (!init_map_ip(set, map, first_ip, last_ip,
first_ip, last_ip,
elements, hosts, netmask)) { elements, hosts, netmask)) {
kfree(map); kfree(map);
return -ENOMEM; return -ENOMEM;
@@ -711,8 +551,23 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
.features = IPSET_TYPE_IP, .features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = AF_INET, .family = AF_INET,
.revision = 0, .revision_min = 0,
.revision_max = 0,
.create = bitmap_ip_create, .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, .me = THIS_MODULE,
}; };

View File

@@ -15,9 +15,6 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
@@ -102,7 +99,7 @@ bitmap_ipmac_exist(const struct ipmac_telem *elem)
/* Base variant */ /* Base variant */
static int 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 bitmap_ipmac *map = set->data;
const struct ipmac *data = value; const struct ipmac *data = value;
@@ -120,7 +117,7 @@ bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout)
} }
static int 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; struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value; const struct ipmac *data = value;
@@ -149,7 +146,7 @@ bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout)
} }
static int 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; struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value; 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); atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd) if (!atd)
return -EFAULT; return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) { for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2]; id = cb->args[2];
elem = bitmap_ipmac_elem(map, id); elem = bitmap_ipmac_elem(map, id);
@@ -185,7 +182,7 @@ bitmap_ipmac_list(const struct ip_set *set,
if (!nested) { if (!nested) {
if (id == first) { if (id == first) {
nla_nest_cancel(skb, atd); nla_nest_cancel(skb, atd);
return -EFAULT; return -EMSGSIZE;
} else } else
goto nla_put_failure; goto nla_put_failure;
} }
@@ -205,13 +202,17 @@ bitmap_ipmac_list(const struct ip_set *set,
nla_put_failure: nla_put_failure:
nla_nest_cancel(skb, nested); nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd); ipset_nest_end(skb, atd);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0; return 0;
} }
/* Timeout variant */ /* Timeout variant */
static int 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 bitmap_ipmac *map = set->data;
const struct ipmac *data = value; const struct ipmac *data = value;
@@ -230,15 +231,16 @@ bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout)
} }
static int 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; struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value; const struct ipmac *data = value;
struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id); struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
bool flag_exist = flags & IPSET_FLAG_EXIST;
switch (elem->match) { switch (elem->match) {
case MAC_UNSET: case MAC_UNSET:
if (!data->ether) if (!(data->ether || flag_exist))
/* Already added without ethernet address */ /* Already added without ethernet address */
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
/* Fill the MAC address and activate the timer */ /* 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); elem->timeout = ip_set_timeout_set(timeout);
break; break;
case MAC_FILLED: case MAC_FILLED:
if (!bitmap_expired(map, data->id)) if (!(bitmap_expired(map, data->id) || flag_exist))
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
/* Fall through */ /* Fall through */
case MAC_EMPTY: case MAC_EMPTY:
@@ -272,7 +274,7 @@ bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout)
} }
static int 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; struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value; 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); atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd) if (!atd)
return -EFAULT; return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) { for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2]; id = cb->args[2];
elem = bitmap_ipmac_elem(map, id); elem = bitmap_ipmac_elem(map, id);
@@ -308,7 +310,7 @@ bitmap_ipmac_tlist(const struct ip_set *set,
if (!nested) { if (!nested) {
if (id == first) { if (id == first) {
nla_nest_cancel(skb, atd); nla_nest_cancel(skb, atd);
return -EFAULT; return -EMSGSIZE;
} else } else
goto nla_put_failure; goto nla_put_failure;
} }
@@ -331,18 +333,23 @@ bitmap_ipmac_tlist(const struct ip_set *set,
nla_put_failure: nla_put_failure:
nla_nest_cancel(skb, nested); nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd); ipset_nest_end(skb, atd);
return 0; return -EMSGSIZE;
} }
static int static int
bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, 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; struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data; 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) if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE; return -IPSET_ERR_BITMAP_RANGE;
@@ -354,32 +361,19 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
data.id -= map->first_ip; data.id -= map->first_ip;
data.ether = eth_hdr(skb)->h_source; 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 static int
bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct bitmap_ipmac *map = set->data; const struct bitmap_ipmac *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data; struct ipmac data;
u32 timeout = map->timeout; u32 timeout = map->timeout;
int ret = 0; int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_ipmac_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@@ -407,7 +401,7 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
data.id -= map->first_ip; data.id -= map->first_ip;
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
@@ -446,8 +440,7 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb)
goto nla_put_failure; goto nla_put_failure;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); 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_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) htonl(sizeof(*map)
+ (map->last_ip - map->first_ip + 1) * map->dsize)); + (map->last_ip - map->first_ip + 1) * map->dsize));
@@ -457,7 +450,7 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb)
return 0; return 0;
nla_put_failure: nla_put_failure:
return -EFAULT; return -EMSGSIZE;
} }
static bool static bool
@@ -538,20 +531,11 @@ bitmap_ipmac_gc_init(struct ip_set *set)
/* Create bitmap:ip,mac type of sets */ /* 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_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static bool static bool
init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
u32 first_ip, u32 last_ip) u32 first_ip, u32 last_ip)
{ {
map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize, map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize);
GFP_KERNEL);
if (!map->members) if (!map->members)
return false; return false;
map->first_ip = first_ip; map->first_ip = first_ip;
@@ -565,18 +549,13 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
} }
static int 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) u32 flags)
{ {
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 first_ip, last_ip, elements; u32 first_ip, last_ip, elements;
struct bitmap_ipmac *map; struct bitmap_ipmac *map;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_ipmac_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@@ -600,7 +579,7 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
if (cidr >= 32) if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
last_ip = first_ip | ~ip_set_hostmask(cidr); ip_set_mask_from_to(first_ip, last_ip, cidr);
} else } else
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@@ -645,8 +624,22 @@ static struct ip_set_type bitmap_ipmac_type = {
.features = IPSET_TYPE_IP | IPSET_TYPE_MAC, .features = IPSET_TYPE_IP | IPSET_TYPE_MAC,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.family = AF_INET, .family = AF_INET,
.revision = 0, .revision_min = 0,
.revision_max = 0,
.create = bitmap_ipmac_create, .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, .me = THIS_MODULE,
}; };

View File

@@ -9,13 +9,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/timer.h> #include <linux/timer.h>
@@ -32,24 +27,33 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("bitmap:port type of IP sets"); MODULE_DESCRIPTION("bitmap:port type of IP sets");
MODULE_ALIAS("ip_set_bitmap:port"); MODULE_ALIAS("ip_set_bitmap:port");
/* Base variant */ /* Type structure */
struct bitmap_port { struct bitmap_port {
void *members; /* the set members */ void *members; /* the set members */
u16 first_port; /* host byte order, included in range */ u16 first_port; /* host byte order, included in range */
u16 last_port; /* host byte order, included in range */ u16 last_port; /* host byte order, included in range */
size_t memsize; /* members size */ size_t memsize; /* members size */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
}; };
static inline int /* Base variant */
bitmap_port_test(const struct bitmap_port *map, u16 id)
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); return !!test_bit(id, map->members);
} }
static inline int static int
bitmap_port_add(struct bitmap_port *map, u16 id) 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)) if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
@@ -57,8 +61,11 @@ bitmap_port_add(struct bitmap_port *map, u16 id)
} }
static int static int
bitmap_port_del(struct bitmap_port *map, u16 id) 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)) if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
@@ -66,14 +73,151 @@ bitmap_port_del(struct bitmap_port *map, u16 id)
} }
static int static int
bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, bitmap_port_list(const struct ip_set *set,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags) 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; 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; __be16 __port;
u16 port = 0; u16 port = 0;
if (!ip_set_get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &__port)) if (!ip_set_get_ip_port(skb, opt->family,
opt->flags & IPSET_DIM_ONE_SRC, &__port))
return -EINVAL; return -EINVAL;
port = ntohs(__port); port = ntohs(__port);
@@ -83,41 +227,23 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
port -= map->first_port; port -= map->first_port;
switch (adt) { return adtfn(set, &port, opt_timeout(opt, map), opt->cmdflags);
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 static int
bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
struct bitmap_port *map = set->data; struct bitmap_port *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1]; ipset_adtfn adtfn = set->variant->adt[adt];
u32 timeout = map->timeout;
u32 port; /* wraparound */ u32 port; /* wraparound */
u16 id, port_to; u16 id, port_to;
int ret = 0; int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_port_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 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_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
@@ -127,11 +253,16 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port < map->first_port || port > map->last_port) if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE; return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT]) if (tb[IPSET_ATTR_TIMEOUT]) {
return -IPSET_ERR_TIMEOUT; if (!with_timeout(map->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST) if (adt == IPSET_TEST) {
return bitmap_port_test(map, port - map->first_port); id = port - map->first_port;
return adtfn(set, &id, timeout, flags);
}
if (tb[IPSET_ATTR_PORT_TO]) { if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
@@ -148,8 +279,7 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len,
for (; port <= port_to; port++) { for (; port <= port_to; port++) {
id = port - map->first_port; id = port - map->first_port;
ret = adt == IPSET_ADD ? bitmap_port_add(map, id) ret = adtfn(set, &id, timeout, flags);
: bitmap_port_del(map, id);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
@@ -164,6 +294,9 @@ bitmap_port_destroy(struct ip_set *set)
{ {
struct bitmap_port *map = set->data; struct bitmap_port *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
ip_set_free(map->members); ip_set_free(map->members);
kfree(map); kfree(map);
@@ -189,55 +322,16 @@ bitmap_port_head(struct ip_set *set, struct sk_buff *skb)
goto nla_put_failure; goto nla_put_failure;
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port)); 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_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->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); ipset_nest_end(skb, nested);
return 0; return 0;
nla_put_failure: nla_put_failure:
return -EFAULT; return -EMSGSIZE;
}
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 static bool
@@ -247,12 +341,18 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
const struct bitmap_port *y = b->data; const struct bitmap_port *y = b->data;
return x->first_port == y->first_port && return x->first_port == y->first_port &&
x->last_port == y->last_port; x->last_port == y->last_port &&
x->timeout == y->timeout;
} }
static const struct ip_set_type_variant bitmap_port = { static const struct ip_set_type_variant bitmap_port = {
.kadt = bitmap_port_kadt, .kadt = bitmap_port_kadt,
.uadt = bitmap_port_uadt, .uadt = bitmap_port_uadt,
.adt = {
[IPSET_ADD] = bitmap_port_add,
[IPSET_DEL] = bitmap_port_del,
[IPSET_TEST] = bitmap_port_test,
},
.destroy = bitmap_port_destroy, .destroy = bitmap_port_destroy,
.flush = bitmap_port_flush, .flush = bitmap_port_flush,
.head = bitmap_port_head, .head = bitmap_port_head,
@@ -260,251 +360,26 @@ static const struct ip_set_type_variant bitmap_port = {
.same_set = bitmap_port_same_set, .same_set = bitmap_port_same_set,
}; };
/* Timeout variant */ static const struct ip_set_type_variant bitmap_tport = {
.kadt = bitmap_port_kadt,
struct bitmap_port_timeout { .uadt = bitmap_port_uadt,
unsigned long *members; /* the set members */ .adt = {
u16 first_port; /* host byte order, included in range */ [IPSET_ADD] = bitmap_port_tadd,
u16 last_port; /* host byte order, included in range */ [IPSET_DEL] = bitmap_port_tdel,
size_t memsize; /* members size */ [IPSET_TEST] = bitmap_port_ttest,
},
u32 timeout; /* timeout parameter */ .destroy = bitmap_port_destroy,
struct timer_list gc; /* garbage collection */ .flush = bitmap_port_flush,
}; .head = bitmap_port_head,
.list = bitmap_port_tlist,
static inline bool .same_set = bitmap_port_same_set,
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;
__be16 __port;
u16 port = 0;
if (!ip_set_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 (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 (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;
}
static 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 static void
bitmap_port_gc(unsigned long ul_set) bitmap_port_gc(unsigned long ul_set)
{ {
struct ip_set *set = (struct ip_set *) ul_set; struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_port_timeout *map = set->data; struct bitmap_port *map = set->data;
unsigned long *table = map->members; unsigned long *table = map->members;
u32 id; /* wraparound */ u32 id; /* wraparound */
u16 last = map->last_port - map->first_port; u16 last = map->last_port - map->first_port;
@@ -524,7 +399,7 @@ bitmap_port_gc(unsigned long ul_set)
static void static void
bitmap_port_gc_init(struct ip_set *set) bitmap_port_gc_init(struct ip_set *set)
{ {
struct bitmap_port_timeout *map = set->data; struct bitmap_port *map = set->data;
init_timer(&map->gc); init_timer(&map->gc);
map->gc.data = (unsigned long) set; map->gc.data = (unsigned long) set;
@@ -535,22 +410,16 @@ bitmap_port_gc_init(struct ip_set *set)
/* Create bitmap:ip type of sets */ /* 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 static bool
init_map_port(struct ip_set *set, struct bitmap_port *map, init_map_port(struct ip_set *set, struct bitmap_port *map,
u16 first_port, u16 last_port) u16 first_port, u16 last_port)
{ {
map->members = ip_set_alloc(map->memsize, GFP_KERNEL); map->members = ip_set_alloc(map->memsize);
if (!map->members) if (!map->members)
return false; return false;
map->first_port = first_port; map->first_port = first_port;
map->last_port = last_port; map->last_port = last_port;
map->timeout = IPSET_NO_TIMEOUT;
set->data = map; set->data = map;
set->family = AF_UNSPEC; set->family = AF_UNSPEC;
@@ -559,16 +428,12 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
} }
static int static int
bitmap_port_create(struct ip_set *set, struct nlattr *head, int len, bitmap_port_create(struct ip_set *set, struct nlattr *tb[],
u32 flags) u32 flags)
{ {
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1]; struct bitmap_port *map;
u16 first_port, last_port; u16 first_port, last_port;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_port_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -583,33 +448,24 @@ bitmap_port_create(struct ip_set *set, struct nlattr *head, int len,
last_port = tmp; last_port = tmp;
} }
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) { 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) map->memsize = (last_port - first_port + 1)
* sizeof(unsigned long); * sizeof(unsigned long);
if (!init_map_port(set, (struct bitmap_port *) map, if (!init_map_port(set, map, first_port, last_port)) {
first_port, last_port)) {
kfree(map); kfree(map);
return -ENOMEM; return -ENOMEM;
} }
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_port_timeout; set->variant = &bitmap_tport;
bitmap_port_gc_init(set); bitmap_port_gc_init(set);
} else { } else {
struct bitmap_port *map;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map->memsize = bitmap_bytes(0, last_port - first_port); map->memsize = bitmap_bytes(0, last_port - first_port);
pr_debug("memsize: %zu\n", map->memsize); pr_debug("memsize: %zu\n", map->memsize);
if (!init_map_port(set, map, first_port, last_port)) { if (!init_map_port(set, map, first_port, last_port)) {
@@ -628,8 +484,20 @@ static struct ip_set_type bitmap_port_type = {
.features = IPSET_TYPE_PORT, .features = IPSET_TYPE_PORT,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC, .family = AF_UNSPEC,
.revision = 0, .revision_min = 0,
.revision_max = 0,
.create = bitmap_port_create, .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, .me = THIS_MODULE,
}; };

View File

@@ -11,8 +11,10 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/icmp.h> #include <linux/icmp.h>
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <linux/sctp.h>
#include <linux/netfilter_ipv6/ip6_tables.h> #include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h>
#include "ip_set_getport.h" #include "ip_set_getport.h"
@@ -34,7 +36,20 @@ get_port(const struct sk_buff *skb, int protocol, unsigned int protooff,
*port = src ? th->source : th->dest; *port = src ? th->source : th->dest;
break; break;
} }
case IPPROTO_UDP: { 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; struct udphdr _udph;
const struct udphdr *uh; const struct udphdr *uh;
@@ -93,21 +108,23 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
} }
EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
bool bool
ip_set_get_ip6_port(const struct sk_buff *skb, bool src, ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
__be16 *port, u8 *proto) __be16 *port, u8 *proto)
{ {
unsigned int protooff = 0; int protoff;
int protocol; u8 nexthdr;
unsigned short fragoff;
protocol = ipv6_find_hdr(skb, &protooff, -1, &fragoff); nexthdr = ipv6_hdr(skb)->nexthdr;
if (protocol <= 0 || fragoff) protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr);
if (protoff < 0)
return false; return false;
return get_port(skb, protocol, protooff, src, port, proto); return get_port(skb, nexthdr, protoff, src, port, proto);
} }
EXPORT_SYMBOL_GPL(ip_set_get_ip6_port); EXPORT_SYMBOL_GPL(ip_set_get_ip6_port);
#endif
bool bool
ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
@@ -118,8 +135,10 @@ ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
switch (pf) { switch (pf) {
case AF_INET: case AF_INET:
ret = ip_set_get_ip4_port(skb, src, port, &proto); ret = ip_set_get_ip4_port(skb, src, port, &proto);
break;
case AF_INET6: case AF_INET6:
ret = ip_set_get_ip6_port(skb, src, port, &proto); ret = ip_set_get_ip6_port(skb, src, port, &proto);
break;
default: default:
return false; return false;
} }

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, IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */ /* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO, IPSET_ERR_MISSING_PROTO,
/* Range not supported */
IPSET_ERR_HASH_RANGE_UNSUPPORTED,
/* Invalid range */
IPSET_ERR_HASH_RANGE,
}; };
#ifdef __KERNEL__ #ifdef __KERNEL__

View File

@@ -12,9 +12,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h> #include <linux/random.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
@@ -56,7 +53,8 @@ struct hash_ip4_telem {
static inline bool static inline bool
hash_ip4_data_equal(const struct hash_ip4_elem *ip1, hash_ip4_data_equal(const struct hash_ip4_elem *ip1,
const struct hash_ip4_elem *ip2) const struct hash_ip4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip; return ip1->ip == ip2->ip;
} }
@@ -111,45 +109,39 @@ nla_put_failure:
#define HOST_MASK 32 #define HOST_MASK 32
#include "ip_set_ahash.h" #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 static int
hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
__be32 ip; __be32 ip;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
ip &= ip_set_netmask(h->netmask); ip &= ip_set_netmask(h->netmask);
if (ip == 0) if (ip == 0)
return -EINVAL; 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 static int
hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip, ip_to, hosts, timeout = h->timeout; u32 ip, ip_to, hosts, timeout = h->timeout;
__be32 nip; __be32 nip;
int ret = 0; int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip4_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@@ -173,7 +165,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
nip = htonl(ip); nip = htonl(ip);
if (nip == 0) if (nip == 0)
return -IPSET_ERR_HASH_ELEM; return -IPSET_ERR_HASH_ELEM;
return adtfn(set, &nip, timeout); return adtfn(set, &nip, timeout, flags);
} }
if (tb[IPSET_ATTR_IP_TO]) { if (tb[IPSET_ATTR_IP_TO]) {
@@ -187,18 +179,19 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32) if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
ip &= ip_set_hostmask(cidr); ip_set_mask_from_to(ip, ip_to, cidr);
ip_to = ip | ~ip_set_hostmask(cidr);
} else } else
ip_to = ip; ip_to = ip;
hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1); hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
if (retried)
ip = h->next.ip;
for (; !before(ip_to, ip); ip += hosts) { for (; !before(ip_to, ip); ip += hosts) {
nip = htonl(ip); nip = htonl(ip);
if (nip == 0) if (nip == 0)
return -IPSET_ERR_HASH_ELEM; return -IPSET_ERR_HASH_ELEM;
ret = adtfn(set, &nip, timeout); ret = adtfn(set, &nip, timeout, flags);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
@@ -233,7 +226,8 @@ struct hash_ip6_telem {
static inline bool static inline bool
hash_ip6_data_equal(const struct hash_ip6_elem *ip1, hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
const struct hash_ip6_elem *ip2) const struct hash_ip6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0;
} }
@@ -297,20 +291,26 @@ nla_put_failure:
#define HOST_MASK 128 #define HOST_MASK 128
#include "ip_set_ahash.h" #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 static int
hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip; 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); ip6_netmask(&ip, h->netmask);
if (ipv6_addr_any(&ip.in6)) if (ipv6_addr_any(&ip.in6))
return -EINVAL; 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] = { static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
@@ -320,22 +320,19 @@ static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
}; };
static int static int
hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip; union nf_inet_addr ip;
u32 timeout = h->timeout; u32 timeout = h->timeout;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip6_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
@@ -355,27 +352,16 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); 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; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
/* Create hash:ip type of sets */ /* 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 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; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 netmask, hbits; u8 netmask, hbits;
struct ip_set_hash *h; struct ip_set_hash *h;
@@ -386,10 +372,6 @@ hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
pr_debug("Create set %s with family %s\n", pr_debug("Create set %s with family %s\n",
set->name, set->family == AF_INET ? "inet" : "inet6"); set->name, set->family == AF_INET ? "inet" : "inet6");
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ip_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || 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_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -425,8 +407,7 @@ hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hbits = htable_bits(hashsize); hbits = htable_bits(hashsize);
h->table = ip_set_alloc( h->table = ip_set_alloc(
sizeof(struct htable) sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket), + jhash_size(hbits) * sizeof(struct hbucket));
GFP_KERNEL);
if (!h->table) { if (!h->table) {
kfree(h); kfree(h);
return -ENOMEM; return -ENOMEM;
@@ -463,8 +444,24 @@ static struct ip_set_type hash_ip_type __read_mostly = {
.features = IPSET_TYPE_IP, .features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC, .family = AF_UNSPEC,
.revision = 0, .revision_min = 0,
.revision_max = 0,
.create = hash_ip_create, .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, .me = THIS_MODULE,
}; };

View File

@@ -12,9 +12,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h> #include <linux/random.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
@@ -63,7 +60,8 @@ struct hash_ipport4_telem {
static inline bool static inline bool
hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1, hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
const struct hash_ipport4_elem *ip2) const struct hash_ipport4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->port == ip2->port && ip1->port == ip2->port &&
@@ -127,51 +125,44 @@ nla_put_failure:
#define HOST_MASK 32 #define HOST_MASK 32
#include "ip_set_ahash.h" #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 static int
hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { }; struct hash_ipport4_elem data = { };
if (!ip_set_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)) &data.port, &data.proto))
return -EINVAL; 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 static int
hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { }; 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; u32 timeout = h->timeout;
bool with_ports = false;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
@@ -192,21 +183,15 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(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) if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO; return -IPSET_ERR_INVALID_PROTO;
} else } else
return -IPSET_ERR_MISSING_PROTO; return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) { if (!(with_ports || data.proto == IPPROTO_ICMP))
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
data.port = 0; data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -215,10 +200,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
} }
if (adt == IPSET_TEST || if (adt == IPSET_TEST ||
!(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) ||
!(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
tb[IPSET_ATTR_PORT_TO])) { tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
@@ -234,30 +218,32 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32) if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
ip &= ip_set_hostmask(cidr); ip_set_mask_from_to(ip, ip_to, cidr);
ip_to = ip | ~ip_set_hostmask(cidr);
} else } else
ip_to = ip; ip_to = ip;
port = ntohs(data.port); port_to = port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) { if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to) if (port > port_to)
swap(port, port_to); swap(port, port_to);
} else }
port_to = port;
for (; !before(ip_to, ip); ip++) if (retried)
for (p = port; p <= port_to; p++) { 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.ip = htonl(ip);
data.port = htons(p); data.port = htons(p);
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
else else
ret = 0; ret = 0;
} }
}
return ret; return ret;
} }
@@ -291,7 +277,8 @@ struct hash_ipport6_telem {
static inline bool static inline bool
hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1, hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
const struct hash_ipport6_elem *ip2) const struct hash_ipport6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->port == ip2->port && ip1->port == ip2->port &&
@@ -355,43 +342,49 @@ nla_put_failure:
#define HOST_MASK 128 #define HOST_MASK 128
#include "ip_set_ahash.h" #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 static int
hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { }; struct hash_ipport6_elem data = { };
if (!ip_set_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)) &data.port, &data.proto))
return -EINVAL; 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 static int
hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { }; struct hash_ipport6_elem data = { };
u32 port, port_to; u32 port, port_to;
u32 timeout = h->timeout; u32 timeout = h->timeout;
bool with_ports = false;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
@@ -408,21 +401,15 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(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) if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO; return -IPSET_ERR_INVALID_PROTO;
} else } else
return -IPSET_ERR_MISSING_PROTO; return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) { if (!(with_ports || data.proto == IPPROTO_ICMPV6))
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
data.port = 0; data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -430,10 +417,8 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
} }
if (adt == IPSET_TEST || if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
!(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || ret = adtfn(set, &data, timeout, flags);
!tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
@@ -442,9 +427,11 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port > port_to) if (port > port_to)
swap(port, port_to); swap(port, port_to);
if (retried)
port = h->next.port;
for (; port <= port_to; port++) { for (; port <= port_to; port++) {
data.port = htons(port); data.port = htons(port);
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
@@ -456,20 +443,9 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
/* Create hash:ip type of sets */ /* 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 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; struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits; u8 hbits;
@@ -477,10 +453,6 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (!(set->family == AF_INET || set->family == AF_INET6)) if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY; return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipport_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || 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_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -506,8 +478,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hbits = htable_bits(hashsize); hbits = htable_bits(hashsize);
h->table = ip_set_alloc( h->table = ip_set_alloc(
sizeof(struct htable) sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket), + jhash_size(hbits) * sizeof(struct hbucket));
GFP_KERNEL);
if (!h->table) { if (!h->table) {
kfree(h); kfree(h);
return -ENOMEM; return -ENOMEM;
@@ -544,8 +515,27 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC, .family = AF_UNSPEC,
.revision = 0, .revision_min = 0,
.revision_max = 1, /* SCTP and UDPLITE support added */
.create = hash_ipport_create, .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, .me = THIS_MODULE,
}; };

View File

@@ -12,9 +12,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h> #include <linux/random.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
@@ -65,7 +62,8 @@ struct hash_ipportip4_telem {
static inline bool static inline bool
hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
const struct hash_ipportip4_elem *ip2) const struct hash_ipportip4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->ip2 == ip2->ip2 && ip1->ip2 == ip2->ip2 &&
@@ -130,53 +128,45 @@ nla_put_failure:
#define HOST_MASK 32 #define HOST_MASK 32
#include "ip_set_ahash.h" #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 static int
hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { }; struct hash_ipportip4_elem data = { };
if (!ip_set_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)) &data.port, &data.proto))
return -EINVAL; return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); 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 static int
hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { }; 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; u32 timeout = h->timeout;
bool with_ports = false;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
@@ -201,21 +191,15 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(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) if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO; return -IPSET_ERR_INVALID_PROTO;
} else } else
return -IPSET_ERR_MISSING_PROTO; return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) { if (!(with_ports || data.proto == IPPROTO_ICMP))
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
data.port = 0; data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -224,10 +208,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
} }
if (adt == IPSET_TEST || if (adt == IPSET_TEST ||
!(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) ||
!(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
tb[IPSET_ATTR_PORT_TO])) { tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
@@ -243,30 +226,32 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32) if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
ip &= ip_set_hostmask(cidr); ip_set_mask_from_to(ip, ip_to, cidr);
ip_to = ip | ~ip_set_hostmask(cidr);
} else } else
ip_to = ip; ip_to = ip;
port = ntohs(data.port); port_to = port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) { if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to) if (port > port_to)
swap(port, port_to); swap(port, port_to);
} else }
port_to = port;
for (; !before(ip_to, ip); ip++) if (retried)
for (p = port; p <= port_to; p++) { 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.ip = htonl(ip);
data.port = htons(p); data.port = htons(p);
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
else else
ret = 0; ret = 0;
} }
}
return ret; return ret;
} }
@@ -302,7 +287,8 @@ struct hash_ipportip6_telem {
static inline bool static inline bool
hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1, hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
const struct hash_ipportip6_elem *ip2) const struct hash_ipportip6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 &&
@@ -369,44 +355,50 @@ nla_put_failure:
#define HOST_MASK 128 #define HOST_MASK 128
#include "ip_set_ahash.h" #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 static int
hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { }; struct hash_ipportip6_elem data = { };
if (!ip_set_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)) &data.port, &data.proto))
return -EINVAL; return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.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 static int
hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { }; struct hash_ipportip6_elem data = { };
u32 port, port_to; u32 port, port_to;
u32 timeout = h->timeout; u32 timeout = h->timeout;
bool with_ports = false;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
@@ -427,21 +419,15 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(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) if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO; return -IPSET_ERR_INVALID_PROTO;
} else } else
return -IPSET_ERR_MISSING_PROTO; return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) { if (!(with_ports || data.proto == IPPROTO_ICMPV6))
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
data.port = 0; data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -449,10 +435,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
} }
if (adt == IPSET_TEST || if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
!(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || ret = adtfn(set, &data, timeout, flags);
!tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
@@ -461,9 +445,11 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port > port_to) if (port > port_to)
swap(port, port_to); swap(port, port_to);
if (retried)
port = h->next.port;
for (; port <= port_to; port++) { for (; port <= port_to; port++) {
data.port = htons(port); data.port = htons(port);
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
@@ -475,20 +461,9 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
/* Create hash:ip type of sets */ /* 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 static int
hash_ipportip_create(struct ip_set *set, struct nlattr *head, hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
int len, u32 flags)
{ {
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct ip_set_hash *h; struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits; u8 hbits;
@@ -496,10 +471,6 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head,
if (!(set->family == AF_INET || set->family == AF_INET6)) if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY; return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipportip_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || 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_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -525,8 +496,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head,
hbits = htable_bits(hashsize); hbits = htable_bits(hashsize);
h->table = ip_set_alloc( h->table = ip_set_alloc(
sizeof(struct htable) sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket), + jhash_size(hbits) * sizeof(struct hbucket));
GFP_KERNEL);
if (!h->table) { if (!h->table) {
kfree(h); kfree(h);
return -ENOMEM; return -ENOMEM;
@@ -563,8 +533,27 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE, .dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC, .family = AF_UNSPEC,
.revision = 0, .revision_min = 0,
.revision_max = 1, /* SCTP and UDPLITE support added */
.create = hash_ipportip_create, .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, .me = THIS_MODULE,
}; };

View File

@@ -12,9 +12,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h> #include <linux/random.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
@@ -65,7 +62,8 @@ struct hash_ipportnet4_telem {
static inline bool static inline bool
hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
const struct hash_ipportnet4_elem *ip2) const struct hash_ipportnet4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->ip2 == ip2->ip2 && ip1->ip2 == ip2->ip2 &&
@@ -143,61 +141,55 @@ nla_put_failure:
#define HOST_MASK 32 #define HOST_MASK 32
#include "ip_set_ahash.h" #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 static int
hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = struct hash_ipportnet4_elem data = {
{ .cidr = h->nets[0].cidr || HOST_MASK }; .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
if (data.cidr == 0) if (data.cidr == 0)
return -EINVAL; return -EINVAL;
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
data.cidr = HOST_MASK; data.cidr = HOST_MASK;
if (!ip_set_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)) &data.port, &data.proto))
return -EINVAL; return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
data.ip2 &= ip_set_netmask(data.cidr); 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 static int
hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; 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; u32 timeout = h->timeout;
bool with_ports = false;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportnet_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
@@ -207,21 +199,19 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(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) if (ret)
return 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) if (ret)
return ret; return ret;
if (tb[IPSET_ATTR_CIDR2]) if (tb[IPSET_ATTR_CIDR2]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!data.cidr)
if (!data.cidr) return -IPSET_ERR_INVALID_CIDR;
return -IPSET_ERR_INVALID_CIDR; }
data.ip2 &= ip_set_netmask(data.cidr);
if (tb[IPSET_ATTR_PORT]) if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
@@ -230,21 +220,15 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(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) if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO; return -IPSET_ERR_INVALID_PROTO;
} else } else
return -IPSET_ERR_MISSING_PROTO; return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) { if (!(with_ports || data.proto == IPPROTO_ICMP))
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
data.port = 0; data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -252,15 +236,16 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
} }
with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
if (adt == IPSET_TEST || if (adt == IPSET_TEST ||
!(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
!(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP2_TO])) {
tb[IPSET_ATTR_PORT_TO])) { data.ip = htonl(ip);
ret = adtfn(set, &data, timeout); data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) { if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret) if (ret)
@@ -272,30 +257,50 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (cidr > 32) if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
ip &= ip_set_hostmask(cidr); ip_set_mask_from_to(ip, ip_to, cidr);
ip_to = ip | ~ip_set_hostmask(cidr); }
} else
ip_to = ip;
port = ntohs(data.port); port_to = port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) { if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to) if (port > port_to)
swap(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++) if (retried)
for (p = port; p <= port_to; p++) { ip = h->next.ip;
data.ip = htonl(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); 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)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
else else
ret = 0; ret = 0;
ip2 = ip2_last + 1;
}
} }
}
return ret; return ret;
} }
@@ -331,7 +336,8 @@ struct hash_ipportnet6_telem {
static inline bool static inline bool
hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
const struct hash_ipportnet6_elem *ip2) const struct hash_ipportnet6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 &&
@@ -417,52 +423,61 @@ nla_put_failure:
#define HOST_MASK 128 #define HOST_MASK 128
#include "ip_set_ahash.h" #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 static int
hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data = struct hash_ipportnet6_elem data = {
{ .cidr = h->nets[0].cidr || HOST_MASK }; .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
if (data.cidr == 0) if (data.cidr == 0)
return -EINVAL; return -EINVAL;
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
data.cidr = HOST_MASK; data.cidr = HOST_MASK;
if (!ip_set_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)) &data.port, &data.proto))
return -EINVAL; return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
ip6_netmask(&data.ip2, data.cidr); ip6_netmask(&data.ip2, data.cidr);
return adtfn(set, &data, h->timeout); return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
} }
static int static int
hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data = { .cidr = HOST_MASK }; struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
u32 port, port_to; u32 port, port_to;
u32 timeout = h->timeout; u32 timeout = h->timeout;
bool with_ports = false;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportnet_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
@@ -490,21 +505,15 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(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) if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO; return -IPSET_ERR_INVALID_PROTO;
} else } else
return -IPSET_ERR_MISSING_PROTO; return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) { if (!(with_ports || data.proto == IPPROTO_ICMPV6))
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
data.port = 0; data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -512,10 +521,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
} }
if (adt == IPSET_TEST || if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
!(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || ret = adtfn(set, &data, timeout, flags);
!tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
@@ -524,9 +531,11 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port > port_to) if (port > port_to)
swap(port, port_to); swap(port, port_to);
if (retried)
port = h->next.port;
for (; port <= port_to; port++) { for (; port <= port_to; port++) {
data.port = htons(port); data.port = htons(port);
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
@@ -538,20 +547,9 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
/* Create hash:ip type of sets */ /* 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 static int
hash_ipportnet_create(struct ip_set *set, struct nlattr *head, hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
int len, u32 flags)
{ {
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct ip_set_hash *h; struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits; u8 hbits;
@@ -559,10 +557,6 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
if (!(set->family == AF_INET || set->family == AF_INET6)) if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY; return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipportnet_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || 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_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -590,8 +584,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
hbits = htable_bits(hashsize); hbits = htable_bits(hashsize);
h->table = ip_set_alloc( h->table = ip_set_alloc(
sizeof(struct htable) sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket), + jhash_size(hbits) * sizeof(struct hbucket));
GFP_KERNEL);
if (!h->table) { if (!h->table) {
kfree(h); kfree(h);
return -ENOMEM; return -ENOMEM;
@@ -629,8 +622,30 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE, .dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC, .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 = 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, .me = THIS_MODULE,
}; };

View File

@@ -12,9 +12,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h> #include <linux/random.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
@@ -61,7 +58,8 @@ struct hash_net4_telem {
static inline bool static inline bool
hash_net4_data_equal(const struct hash_net4_elem *ip1, hash_net4_data_equal(const struct hash_net4_elem *ip1,
const struct hash_net4_elem *ip2) const struct hash_net4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr; return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr;
} }
@@ -128,46 +126,46 @@ nla_put_failure:
#define HOST_MASK 32 #define HOST_MASK 32
#include "ip_set_ahash.h" #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 static int
hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; 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) if (data.cidr == 0)
return -EINVAL; return -EINVAL;
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
data.cidr = HOST_MASK; data.cidr = HOST_MASK;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr); 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 static int
hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = HOST_MASK }; struct hash_net4_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout; u32 timeout = h->timeout;
u32 ip = 0, ip_to, last;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_net_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@@ -175,17 +173,15 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(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) if (ret)
return ret; return ret;
if (tb[IPSET_ATTR_CIDR]) if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!data.cidr)
if (!data.cidr) return -IPSET_ERR_INVALID_CIDR;
return -IPSET_ERR_INVALID_CIDR; }
data.ip &= ip_set_netmask(data.cidr);
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -193,9 +189,35 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); 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;
}
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 static bool
@@ -228,7 +250,8 @@ struct hash_net6_telem {
static inline bool static inline bool
hash_net6_data_equal(const struct hash_net6_elem *ip1, hash_net6_data_equal(const struct hash_net6_elem *ip1,
const struct hash_net6_elem *ip2) const struct hash_net6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->cidr == ip2->cidr; ip1->cidr == ip2->cidr;
@@ -304,43 +327,49 @@ nla_put_failure:
#define HOST_MASK 128 #define HOST_MASK 128
#include "ip_set_ahash.h" #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 static int
hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; 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) if (data.cidr == 0)
return -EINVAL; return -EINVAL;
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
data.cidr = HOST_MASK; 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); ip6_netmask(&data.ip, data.cidr);
return adtfn(set, &data, h->timeout); return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
} }
static int static int
hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len, hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem data = { .cidr = HOST_MASK }; struct hash_net6_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout; u32 timeout = h->timeout;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_net_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
@@ -363,26 +392,16 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); 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; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
/* Create hash:ip type of sets */ /* 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 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; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
struct ip_set_hash *h; struct ip_set_hash *h;
u8 hbits; u8 hbits;
@@ -390,10 +409,6 @@ hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (!(set->family == AF_INET || set->family == AF_INET6)) if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY; return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_net_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || 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_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -421,8 +436,7 @@ hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hbits = htable_bits(hashsize); hbits = htable_bits(hashsize);
h->table = ip_set_alloc( h->table = ip_set_alloc(
sizeof(struct htable) sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket), + jhash_size(hbits) * sizeof(struct hbucket));
GFP_KERNEL);
if (!h->table) { if (!h->table) {
kfree(h); kfree(h);
return -ENOMEM; return -ENOMEM;
@@ -459,8 +473,22 @@ static struct ip_set_type hash_net_type __read_mostly = {
.features = IPSET_TYPE_IP, .features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC, .family = AF_UNSPEC,
.revision = 0, .revision_min = 0,
.revision_max = 1, /* Range as input support for IPv4 added */
.create = hash_net_create, .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, .me = THIS_MODULE,
}; };

View File

@@ -0,0 +1,786 @@
/* 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);
long 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);
long 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 */
struct hash_netiface4_elem_hashed {
__be32 ip;
u8 physdev;
u8 cidr;
u16 padding;
};
#define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed)
/* Member elements without timeout */
struct hash_netiface4_elem {
__be32 ip;
u8 physdev;
u8 cidr;
u16 padding;
const char *iface;
};
/* Member elements with timeout support */
struct hash_netiface4_telem {
__be32 ip;
u8 physdev;
u8 cidr;
u16 padding;
const char *iface;
unsigned long timeout;
};
static inline bool
hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1,
const struct hash_netiface4_elem *ip2,
u32 *multi)
{
return ip1->ip == ip2->ip &&
ip1->cidr == ip2->cidr &&
(++*multi) &&
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 IP_SET_HASH_WITH_MULTI
#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_hashed {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
u16 padding;
};
#define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed)
struct hash_netiface6_elem {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
u16 padding;
const char *iface;
};
struct hash_netiface6_telem {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
u16 padding;
const char *iface;
unsigned long timeout;
};
static inline bool
hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1,
const struct hash_netiface6_elem *ip2,
u32 *multi)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->cidr == ip2->cidr &&
(++*multi) &&
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;
h->ahash_max = AHASH_MAX_SIZE;
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

@@ -12,9 +12,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h> #include <linux/random.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
@@ -62,7 +59,8 @@ struct hash_netport4_telem {
static inline bool static inline bool
hash_netport4_data_equal(const struct hash_netport4_elem *ip1, hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
const struct hash_netport4_elem *ip2) const struct hash_netport4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->port == ip2->port && ip1->port == ip2->port &&
@@ -140,57 +138,52 @@ nla_put_failure:
#define HOST_MASK 32 #define HOST_MASK 32
#include "ip_set_ahash.h" #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 static int
hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = { 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) if (data.cidr == 0)
return -EINVAL; return -EINVAL;
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
data.cidr = HOST_MASK; data.cidr = HOST_MASK;
if (!ip_set_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)) &data.port, &data.proto))
return -EINVAL; return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr); 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 static int
hash_netport4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = { .cidr = HOST_MASK }; 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; u32 timeout = h->timeout;
bool with_ports = false;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_netport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
@@ -200,15 +193,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(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) if (ret)
return ret; return ret;
if (tb[IPSET_ATTR_CIDR]) if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!data.cidr) if (!data.cidr)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
data.ip &= ip_set_netmask(data.cidr); }
if (tb[IPSET_ATTR_PORT]) if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
@@ -217,21 +210,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(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) if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO; return -IPSET_ERR_INVALID_PROTO;
} else } else
return -IPSET_ERR_MISSING_PROTO; return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) { if (!(with_ports || data.proto == IPPROTO_ICMP))
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
data.port = 0; data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -239,26 +226,47 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
} }
if (adt == IPSET_TEST || with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
!(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
!tb[IPSET_ATTR_PORT_TO]) { data.ip = htonl(ip & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
port = ntohs(data.port); port = port_to = ntohs(data.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); if (tb[IPSET_ATTR_PORT_TO]) {
if (port > port_to) port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
swap(port, port_to); if (port_to < port)
swap(port, port_to);
for (; port <= port_to; port++) { }
data.port = htons(port); if (tb[IPSET_ATTR_IP_TO]) {
ret = adtfn(set, &data, timeout); ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
else if (ip_to < ip)
ret = 0; 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);
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; return ret;
} }
@@ -293,7 +301,8 @@ struct hash_netport6_telem {
static inline bool static inline bool
hash_netport6_data_equal(const struct hash_netport6_elem *ip1, hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
const struct hash_netport6_elem *ip2) const struct hash_netport6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->port == ip2->port && ip1->port == ip2->port &&
@@ -376,51 +385,58 @@ nla_put_failure:
#define HOST_MASK 128 #define HOST_MASK 128
#include "ip_set_ahash.h" #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 static int
hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, 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; const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = { 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) if (data.cidr == 0)
return -EINVAL; return -EINVAL;
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
data.cidr = HOST_MASK; data.cidr = HOST_MASK;
if (!ip_set_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)) &data.port, &data.proto))
return -EINVAL; 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); ip6_netmask(&data.ip, data.cidr);
return adtfn(set, &data, h->timeout); return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
} }
static int static int
hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len, hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct ip_set_hash *h = set->data; const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = { .cidr = HOST_MASK }; struct hash_netport6_elem data = { .cidr = HOST_MASK };
u32 port, port_to; u32 port, port_to;
u32 timeout = h->timeout; u32 timeout = h->timeout;
bool with_ports = false;
int ret; int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_netport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
@@ -442,21 +458,15 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(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) if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO; return -IPSET_ERR_INVALID_PROTO;
} else } else
return -IPSET_ERR_MISSING_PROTO; return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) { if (!(with_ports || data.proto == IPPROTO_ICMPV6))
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
data.port = 0; data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) { if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout)) if (!with_timeout(h->timeout))
@@ -464,10 +474,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
} }
if (adt == IPSET_TEST || if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
!(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || ret = adtfn(set, &data, timeout, flags);
!tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
@@ -476,9 +484,11 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
if (port > port_to) if (port > port_to)
swap(port, port_to); swap(port, port_to);
if (retried)
port = h->next.port;
for (; port <= port_to; port++) { for (; port <= port_to; port++) {
data.port = htons(port); data.port = htons(port);
ret = adtfn(set, &data, timeout); ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags)) if (ret && !ip_set_eexist(ret, flags))
return ret; return ret;
@@ -490,20 +500,9 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
/* Create hash:ip type of sets */ /* 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 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; struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits; u8 hbits;
@@ -511,10 +510,6 @@ hash_netport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (!(set->family == AF_INET || set->family == AF_INET6)) if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY; return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_netport_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || 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_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -542,8 +537,7 @@ hash_netport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
hbits = htable_bits(hashsize); hbits = htable_bits(hashsize);
h->table = ip_set_alloc( h->table = ip_set_alloc(
sizeof(struct htable) sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket), + jhash_size(hbits) * sizeof(struct hbucket));
GFP_KERNEL);
if (!h->table) { if (!h->table) {
kfree(h); kfree(h);
return -ENOMEM; return -ENOMEM;
@@ -580,8 +574,28 @@ static struct ip_set_type hash_netport_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC, .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 = 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, .me = THIS_MODULE,
}; };

View File

@@ -43,14 +43,19 @@ struct list_set {
static inline struct set_elem * static inline struct set_elem *
list_set_elem(const struct list_set *map, u32 id) 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 static inline bool
list_set_timeout(const struct list_set *map, u32 id) list_set_timeout(const struct list_set *map, u32 id)
{ {
const struct set_telem *elem = const struct set_telem *elem = list_set_telem(map, id);
(const struct set_telem *) list_set_elem(map, id);
return ip_set_timeout_test(elem->timeout); return ip_set_timeout_test(elem->timeout);
} }
@@ -58,24 +63,17 @@ list_set_timeout(const struct list_set *map, u32 id)
static inline bool static inline bool
list_set_expired(const struct list_set *map, u32 id) list_set_expired(const struct list_set *map, u32 id)
{ {
const struct set_telem *elem = const struct set_telem *elem = list_set_telem(map, id);
(const struct set_telem *) list_set_elem(map, id);
return ip_set_timeout_expired(elem->timeout); 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 */ /* Set list without and with timeout */
static int static int
list_set_kadt(struct ip_set *set, const struct sk_buff *skb, 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 list_set *map = set->data;
struct set_elem *elem; struct set_elem *elem;
@@ -90,17 +88,17 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
continue; continue;
switch (adt) { switch (adt) {
case IPSET_TEST: 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) if (ret > 0)
return ret; return ret;
break; break;
case IPSET_ADD: 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) if (ret == 0)
return ret; return ret;
break; break;
case IPSET_DEL: 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) if (ret == 0)
return ret; return ret;
break; break;
@@ -111,26 +109,29 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
return -EINVAL; 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 bool static bool
next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id) id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
{ {
const struct set_elem *elem; const struct set_elem *elem;
if (i + 1 < map->size) { if (i < map->size) {
elem = list_set_elem(map, i + 1); elem = list_set_elem(map, i);
return elem->id == id;
}
return 0;
}
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 && return !!(elem->id == id &&
!(with_timeout(map->timeout) && !(with_timeout(map->timeout) &&
list_set_expired(map, i + 1))); list_set_expired(map, i)));
} }
return 0; return 0;
@@ -156,11 +157,11 @@ list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
struct set_telem *e; struct set_telem *e;
for (; i < map->size; i++) { 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->id, id);
swap(e->timeout, timeout);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
break; break;
swap(e->timeout, timeout);
} }
} }
@@ -174,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 */ /* Last element replaced: e.g. add new,before,last */
ip_set_put_byindex(e->id); ip_set_put_byindex(e->id);
if (with_timeout(map->timeout)) if (with_timeout(map->timeout))
list_elem_tadd(map, i, id, timeout); list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
else else
list_elem_add(map, i, id); list_elem_add(map, i, id);
@@ -182,11 +183,11 @@ list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
} }
static int 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; 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++) { for (; i < map->size - 1; i++) {
b = list_set_elem(map, i + 1); b = list_set_elem(map, i + 1);
@@ -203,13 +204,26 @@ list_set_del(struct list_set *map, ip_set_id_t id, u32 i)
return 0; 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 static int
list_set_uadt(struct ip_set *set, struct nlattr *head, int len, list_set_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
struct list_set *map = set->data; struct list_set *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
bool with_timeout = with_timeout(map->timeout); bool with_timeout = with_timeout(map->timeout);
bool flag_exist = flags & IPSET_FLAG_EXIST;
int before = 0; int before = 0;
u32 timeout = map->timeout; u32 timeout = map->timeout;
ip_set_id_t id, refid = IPSET_INVALID_ID; ip_set_id_t id, refid = IPSET_INVALID_ID;
@@ -218,10 +232,6 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
u32 i; u32 i;
int ret = 0; int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
list_set_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!tb[IPSET_ATTR_NAME] || if (unlikely(!tb[IPSET_ATTR_NAME] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
@@ -266,6 +276,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
} }
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
} }
if (with_timeout && adt != IPSET_TEST)
cleanup_entries(map);
switch (adt) { switch (adt) {
case IPSET_TEST: case IPSET_TEST:
@@ -277,22 +289,37 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
else if (with_timeout && list_set_expired(map, i)) else if (with_timeout && list_set_expired(map, i))
continue; continue;
else if (before > 0 && elem->id == id) 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) 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) else if (before == 0 && elem->id == id)
ret = 1; ret = 1;
} }
break; break;
case IPSET_ADD: case IPSET_ADD:
for (i = 0; i < map->size && !ret; i++) { for (i = 0; i < map->size; i++) {
elem = list_set_elem(map, i); elem = list_set_elem(map, i);
if (elem->id == id && if (elem->id != id)
!(with_timeout && list_set_expired(map, i))) continue;
if (!(with_timeout && flag_exist)) {
ret = -IPSET_ERR_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; ret = -IPSET_ERR_LIST_FULL;
for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
elem = list_set_elem(map, i); elem = list_set_elem(map, i);
@@ -301,9 +328,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
: list_set_add(map, i, id, timeout); : list_set_add(map, i, id, timeout);
else if (elem->id != refid) else if (elem->id != refid)
continue; continue;
else if (with_timeout && list_set_expired(map, i)) else if (before > 0)
ret = -IPSET_ERR_REF_EXIST;
else if (before)
ret = list_set_add(map, i, id, timeout); ret = list_set_add(map, i, id, timeout);
else if (i + 1 < map->size) else if (i + 1 < map->size)
ret = list_set_add(map, i + 1, id, timeout); ret = list_set_add(map, i + 1, id, timeout);
@@ -317,17 +342,13 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = before != 0 ? -IPSET_ERR_REF_EXIST ret = before != 0 ? -IPSET_ERR_REF_EXIST
: -IPSET_ERR_EXIST; : -IPSET_ERR_EXIST;
break; break;
} else if (with_timeout && list_set_expired(map, i)) } else if (elem->id == id &&
continue; (before == 0 ||
else if (elem->id == id && (before > 0 && id_eq(map, i + 1, refid))))
(before == 0 || ret = list_set_del(map, i);
(before > 0 && else if (elem->id == refid &&
next_id_eq(map, i, refid)))) before < 0 && id_eq(map, i + 1, id))
ret = list_set_del(map, id, i); ret = list_set_del(map, i + 1);
else if (before < 0 &&
elem->id == refid &&
next_id_eq(map, i, id))
ret = list_set_del(map, id, i + 1);
} }
break; break;
default: default:
@@ -384,15 +405,14 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size));
if (with_timeout(map->timeout)) if (with_timeout(map->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->size * map->dsize)); htonl(sizeof(*map) + map->size * map->dsize));
ipset_nest_end(skb, nested); ipset_nest_end(skb, nested);
return 0; return 0;
nla_put_failure: nla_put_failure:
return -EFAULT; return -EMSGSIZE;
} }
static int static int
@@ -406,7 +426,7 @@ list_set_list(const struct ip_set *set,
atd = ipset_nest_start(skb, IPSET_ATTR_ADT); atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd) if (!atd)
return -EFAULT; return -EMSGSIZE;
for (; cb->args[2] < map->size; cb->args[2]++) { for (; cb->args[2] < map->size; cb->args[2]++) {
i = cb->args[2]; i = cb->args[2];
e = list_set_elem(map, i); e = list_set_elem(map, i);
@@ -418,7 +438,7 @@ list_set_list(const struct ip_set *set,
if (!nested) { if (!nested) {
if (i == first) { if (i == first) {
nla_nest_cancel(skb, atd); nla_nest_cancel(skb, atd);
return -EFAULT; return -EMSGSIZE;
} else } else
goto nla_put_failure; goto nla_put_failure;
} }
@@ -441,6 +461,10 @@ finish:
nla_put_failure: nla_put_failure:
nla_nest_cancel(skb, nested); nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd); ipset_nest_end(skb, atd);
if (unlikely(i == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0; return 0;
} }
@@ -469,19 +493,10 @@ list_set_gc(unsigned long ul_set)
{ {
struct ip_set *set = (struct ip_set *) ul_set; struct ip_set *set = (struct ip_set *) ul_set;
struct list_set *map = set->data; struct list_set *map = set->data;
struct set_telem *e;
u32 i;
/* We run parallel with other readers (test element) write_lock_bh(&set->lock);
* but adding/deleting new entries is locked out */ cleanup_entries(map);
read_lock_bh(&set->lock); write_unlock_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);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc); add_timer(&map->gc);
@@ -501,12 +516,6 @@ list_set_gc_init(struct ip_set *set)
/* Create list:set type of sets */ /* 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 bool static bool
init_list_set(struct ip_set *set, u32 size, size_t dsize, init_list_set(struct ip_set *set, u32 size, size_t dsize,
unsigned long timeout) unsigned long timeout)
@@ -533,16 +542,10 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize,
} }
static int static int
list_set_create(struct ip_set *set, struct nlattr *head, int len, list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
u32 flags)
{ {
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 size = IP_SET_LIST_DEFAULT_SIZE; u32 size = IP_SET_LIST_DEFAULT_SIZE;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
list_set_create_policy))
return -IPSET_ERR_PROTOCOL;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@@ -573,8 +576,22 @@ static struct ip_set_type list_set_type __read_mostly = {
.features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC, .family = AF_UNSPEC,
.revision = 0, .revision_min = 0,
.revision_max = 0,
.create = list_set_create, .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, .me = THIS_MODULE,
}; };

View File

@@ -22,6 +22,9 @@
#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT) #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 static inline unsigned int
ip_set_timeout_uget(struct nlattr *tb) ip_set_timeout_uget(struct nlattr *tb)
{ {
@@ -45,7 +48,7 @@ ip_set_timeout_test(unsigned long timeout)
{ {
return timeout != IPSET_ELEM_UNSET && return timeout != IPSET_ELEM_UNSET &&
(timeout == IPSET_ELEM_PERMANENT || (timeout == IPSET_ELEM_PERMANENT ||
time_after(timeout, jiffies)); time_is_after_jiffies(timeout));
} }
static inline bool static inline bool
@@ -53,7 +56,7 @@ ip_set_timeout_expired(unsigned long timeout)
{ {
return timeout != IPSET_ELEM_UNSET && return timeout != IPSET_ELEM_UNSET &&
timeout != IPSET_ELEM_PERMANENT && timeout != IPSET_ELEM_PERMANENT &&
time_before(timeout, jiffies); time_is_before_jiffies(timeout);
} }
static inline unsigned long static inline unsigned long
@@ -64,7 +67,7 @@ ip_set_timeout_set(u32 timeout)
if (!timeout) if (!timeout)
return IPSET_ELEM_PERMANENT; return IPSET_ELEM_PERMANENT;
t = timeout * HZ + jiffies; t = msecs_to_jiffies(timeout * 1000) + jiffies;
if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT) if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT)
/* Bingo! */ /* Bingo! */
t++; t++;
@@ -75,7 +78,8 @@ ip_set_timeout_set(u32 timeout)
static inline u32 static inline u32
ip_set_timeout_get(unsigned long timeout) 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 #else
@@ -89,14 +93,14 @@ static inline bool
ip_set_timeout_test(unsigned long timeout) ip_set_timeout_test(unsigned long timeout)
{ {
return timeout == IPSET_ELEM_PERMANENT || return timeout == IPSET_ELEM_PERMANENT ||
time_after(timeout, jiffies); time_is_after_jiffies(timeout);
} }
static inline bool static inline bool
ip_set_timeout_expired(unsigned long timeout) ip_set_timeout_expired(unsigned long timeout)
{ {
return timeout != IPSET_ELEM_PERMANENT && return timeout != IPSET_ELEM_PERMANENT &&
time_before(timeout, jiffies); time_is_before_jiffies(timeout);
} }
static inline unsigned long static inline unsigned long
@@ -107,7 +111,7 @@ ip_set_timeout_set(u32 timeout)
if (!timeout) if (!timeout)
return IPSET_ELEM_PERMANENT; return IPSET_ELEM_PERMANENT;
t = timeout * HZ + jiffies; t = msecs_to_jiffies(timeout * 1000) + jiffies;
if (t == IPSET_ELEM_PERMANENT) if (t == IPSET_ELEM_PERMANENT)
/* Bingo! :-) */ /* Bingo! :-) */
t++; t++;
@@ -118,7 +122,8 @@ ip_set_timeout_set(u32 timeout)
static inline u32 static inline u32
ip_set_timeout_get(unsigned long timeout) 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 */ #endif /* ! IP_SET_BITMAP_TIMEOUT */

View File

@@ -1,12 +1,13 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <assert.h> /* assert */ #include <assert.h> /* assert */
#include <arpa/inet.h> /* ntoh* */ #include <arpa/inet.h> /* ntoh* */
#include <net/ethernet.h> /* ETH_ALEN */ #include <net/ethernet.h> /* ETH_ALEN */
#include <net/if.h> /* IFNAMSIZ */
#include <sys/socket.h> /* AF_ */ #include <sys/socket.h> /* AF_ */
#include <stdlib.h> /* malloc, free */ #include <stdlib.h> /* malloc, free */
#include <string.h> /* memset */ #include <string.h> /* memset */
@@ -17,7 +18,7 @@
#include <libipset/utils.h> /* inXcpy */ #include <libipset/utils.h> /* inXcpy */
#include <libipset/data.h> /* prototypes */ #include <libipset/data.h> /* prototypes */
/* Internal data structure to hold /* Internal data structure to hold
* a) input data entered by the user or * a) input data entered by the user or
* b) data received from kernel * b) data received from kernel
* *
@@ -66,11 +67,13 @@ struct ipset_data {
/* ADT/LIST/SAVE */ /* ADT/LIST/SAVE */
struct { struct {
union nf_inet_addr ip2; union nf_inet_addr ip2;
union nf_inet_addr ip2_to;
uint8_t cidr2; uint8_t cidr2;
uint8_t proto; uint8_t proto;
char ether[ETH_ALEN]; char ether[ETH_ALEN];
char name[IPSET_MAXNAMELEN]; char name[IPSET_MAXNAMELEN];
char nameref[IPSET_MAXNAMELEN]; char nameref[IPSET_MAXNAMELEN];
char iface[IFNAMSIZ];
} adt; } adt;
}; };
}; };
@@ -171,7 +174,7 @@ ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt)
{ {
bool ignored; bool ignored;
assert(data); assert(data);
ignored = data->ignored & IPSET_FLAG(opt); ignored = data->ignored & IPSET_FLAG(opt);
data->ignored |= IPSET_FLAG(opt); data->ignored |= IPSET_FLAG(opt);
@@ -289,12 +292,20 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
return -1; return -1;
copy_addr(data->family, &data->adt.ip2, value); copy_addr(data->family, &data->adt.ip2, value);
break; 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: case IPSET_OPT_CIDR2:
data->adt.cidr2 = *(const uint8_t *) value; data->adt.cidr2 = *(const uint8_t *) value;
break; break;
case IPSET_OPT_PROTO: case IPSET_OPT_PROTO:
data->adt.proto = *(const uint8_t *) value; data->adt.proto = *(const uint8_t *) value;
break; break;
case IPSET_OPT_IFACE:
ipset_strlcpy(data->adt.iface, value, IFNAMSIZ);
break;
/* Swap/rename */ /* Swap/rename */
case IPSET_OPT_SETNAME2: case IPSET_OPT_SETNAME2:
ipset_strlcpy(data->setname2, value, IPSET_MAXNAMELEN); 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: case IPSET_OPT_BEFORE:
cadt_flag_type_attr(data, opt, IPSET_FLAG_BEFORE); cadt_flag_type_attr(data, opt, IPSET_FLAG_BEFORE);
break; break;
case IPSET_OPT_PHYSDEV:
cadt_flag_type_attr(data, opt, IPSET_FLAG_PHYSDEV);
break;
case IPSET_OPT_FLAGS: case IPSET_OPT_FLAGS:
data->flags = *(const uint32_t *)value; data->flags = *(const uint32_t *)value;
break; break;
@@ -315,7 +329,7 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
default: default:
return -1; return -1;
}; };
ipset_data_flags_set(data, IPSET_FLAG(opt)); ipset_data_flags_set(data, IPSET_FLAG(opt));
return 0; return 0;
} }
@@ -334,7 +348,7 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
{ {
assert(data); assert(data);
assert(opt != IPSET_OPT_NONE); assert(opt != IPSET_OPT_NONE);
if (!(opt == IPSET_OPT_TYPENAME || ipset_data_test(data, opt))) if (!(opt == IPSET_OPT_TYPENAME || ipset_data_test(data, opt)))
return NULL; return NULL;
@@ -401,10 +415,14 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
return data->adt.nameref; return data->adt.nameref;
case IPSET_OPT_IP2: case IPSET_OPT_IP2:
return &data->adt.ip2; return &data->adt.ip2;
case IPSET_OPT_IP2_TO:
return &data->adt.ip2_to;
case IPSET_OPT_CIDR2: case IPSET_OPT_CIDR2:
return &data->adt.cidr2; return &data->adt.cidr2;
case IPSET_OPT_PROTO: case IPSET_OPT_PROTO:
return &data->adt.proto; return &data->adt.proto;
case IPSET_OPT_IFACE:
return &data->adt.iface;
/* Swap/rename */ /* Swap/rename */
case IPSET_OPT_SETNAME2: case IPSET_OPT_SETNAME2:
return data->setname2; return data->setname2;
@@ -414,6 +432,7 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
return &data->flags; return &data->flags;
case IPSET_OPT_CADT_FLAGS: case IPSET_OPT_CADT_FLAGS:
case IPSET_OPT_BEFORE: case IPSET_OPT_BEFORE:
case IPSET_OPT_PHYSDEV:
return &data->cadt_flags; return &data->cadt_flags;
default: default:
return NULL; return NULL;
@@ -436,6 +455,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
case IPSET_OPT_IP: case IPSET_OPT_IP:
case IPSET_OPT_IP_TO: case IPSET_OPT_IP_TO:
case IPSET_OPT_IP2: case IPSET_OPT_IP2:
case IPSET_OPT_IP2_TO:
return family == AF_INET ? sizeof(uint32_t) return family == AF_INET ? sizeof(uint32_t)
: sizeof(struct in6_addr); : sizeof(struct in6_addr);
case IPSET_OPT_PORT: case IPSET_OPT_PORT:
@@ -463,8 +483,9 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
return sizeof(uint8_t); return sizeof(uint8_t);
case IPSET_OPT_ETHER: case IPSET_OPT_ETHER:
return ETH_ALEN; return ETH_ALEN;
/* Flags counted once */ /* Flags doesn't counted once :-( */
case IPSET_OPT_BEFORE: case IPSET_OPT_BEFORE:
case IPSET_OPT_PHYSDEV:
return sizeof(uint32_t); return sizeof(uint32_t);
default: default:
return 0; return 0;
@@ -512,8 +533,8 @@ uint8_t
ipset_data_cidr(const struct ipset_data *data) ipset_data_cidr(const struct ipset_data *data)
{ {
assert(data); assert(data);
return ipset_data_test(data, IPSET_OPT_CIDR) ? data->cidr : return ipset_data_test(data, IPSET_OPT_CIDR) ? data->cidr :
data->family == AF_INET ? 32 : data->family == AF_INET ? 32 :
data->family == AF_INET6 ? 128 : 0; data->family == AF_INET6 ? 128 : 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

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/utils.h> /* STRNEQ */ #include <libipset/utils.h> /* STRNEQ */
@@ -49,19 +49,20 @@ static const struct icmp_names icmp_typecodes[] = {
{ "address-mask-reply", 18, 0 }, { "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; 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; unsigned int i;
for (i = 0; i < ARRAY_SIZE(icmp_typecodes); 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 icmp_typecodes[i].name;
return NULL; return NULL;
} }
@@ -71,9 +72,10 @@ int name_to_icmp(const char *str, uint16_t *typecode)
for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++) for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
if (STRNCASEQ(icmp_typecodes[i].name, str, strlen(str))) { 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; return 0;
} }
return -1; return -1;
} }

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/utils.h> /* STRNEQ */ #include <libipset/utils.h> /* STRNEQ */
@@ -36,19 +36,21 @@ static const struct icmpv6_names icmpv6_typecodes[] = {
{ "redirect", 137, 0 }, { "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; unsigned int i;
for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); 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 icmpv6_typecodes[i].name;
return NULL; return NULL;
} }
@@ -58,9 +60,10 @@ int name_to_icmpv6(const char *str, uint16_t *typecode)
for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++) for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
if (STRNCASEQ(icmpv6_typecodes[i].name, str, strlen(str))) { 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; return 0;
} }
return -1; return -1;
} }

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <assert.h> /* assert */ #include <assert.h> /* assert */
@@ -38,13 +38,14 @@ struct ipset_handle {
/* Netlink flags of the commands */ /* Netlink flags of the commands */
static const uint16_t cmdflags[] = { 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_DESTROY-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_FLUSH-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_RENAME-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_SWAP-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_LIST-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_SAVE-1] = NLM_F_REQUEST, [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_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_DEL-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
[IPSET_CMD_TEST-1] = NLM_F_REQUEST|NLM_F_ACK, [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; nlh->nlmsg_flags = NLM_F_REQUEST;
if (cmdflags[cmd-1] & NLM_F_ACK) if (cmdflags[cmd-1] & NLM_F_ACK)
nlh->nlmsg_flags |= 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 = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
ghdr->cmd = cmd; ghdr->cmd = cmd;
@@ -102,19 +102,24 @@ ipset_mnl_query(struct ipset_handle *handle, void *buffer, size_t len)
assert(handle); assert(handle);
assert(buffer); 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) if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0)
return -ECOMM; return -ECOMM;
D("message sent");
ret = mnl_socket_recvfrom(handle->h, buffer, len); 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) { while (ret > 0) {
ret = mnl_cb_run2(buffer, ret, ret = mnl_cb_run2(buffer, ret,
handle->seq, handle->portid, handle->seq, handle->portid,
handle->cb_ctl[NLMSG_MIN_TYPE], handle->cb_ctl[NLMSG_MIN_TYPE],
handle->data, handle->data,
handle->cb_ctl, NLMSG_MIN_TYPE); 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) if (ret <= 0)
break; break;
ret = mnl_socket_recvfrom(handle->h, buffer, len); ret = mnl_socket_recvfrom(handle->h, buffer, len);
@@ -205,27 +210,28 @@ static int ipset_mnl_getid(struct ipset_handle *h, bool modprobe)
static struct ipset_handle * static struct ipset_handle *
ipset_mnl_init(mnl_cb_t *cb_ctl, void *data) ipset_mnl_init(mnl_cb_t *cb_ctl, void *data)
{ {
struct ipset_handle *handle; struct ipset_handle *handle;
assert(cb_ctl); assert(cb_ctl);
assert(data); assert(data);
handle = calloc(1, sizeof(*handle)); handle = calloc(1, sizeof(*handle));
if (!handle) if (!handle)
return NULL; return NULL;
handle->h = mnl_socket_open(NETLINK_GENERIC); handle->h = mnl_socket_open(NETLINK_GENERIC);
if (!handle->h) if (!handle->h)
goto free_handle; goto free_handle;
if (mnl_socket_bind(handle->h, 0, MNL_SOCKET_AUTOPID) < 0) if (mnl_socket_bind(handle->h, 0, MNL_SOCKET_AUTOPID) < 0)
goto close_nl; goto close_nl;
handle->portid = mnl_socket_get_portid(handle->h); handle->portid = mnl_socket_get_portid(handle->h);
handle->cb_ctl = cb_ctl; handle->cb_ctl = cb_ctl;
handle->data = data; handle->data = data;
handle->seq = time(NULL);
if (ipset_mnl_getid(handle, false) < 0) if (ipset_mnl_getid(handle, false) < 0)
goto close_nl; goto close_nl;
return handle; return handle;
@@ -235,7 +241,7 @@ close_nl:
free_handle: free_handle:
free(handle); free(handle);
return NULL; return NULL;
} }
static int static int

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <assert.h> /* assert */ #include <assert.h> /* assert */
@@ -12,6 +12,7 @@
#include <sys/types.h> /* getaddrinfo */ #include <sys/types.h> /* getaddrinfo */
#include <sys/socket.h> /* getaddrinfo, AF_ */ #include <sys/socket.h> /* getaddrinfo, AF_ */
#include <net/ethernet.h> /* ETH_ALEN */ #include <net/ethernet.h> /* ETH_ALEN */
#include <net/if.h> /* IFNAMSIZ */
#include <netinet/in.h> /* IPPROTO_ */ #include <netinet/in.h> /* IPPROTO_ */
#include <libipset/debug.h> /* D() */ #include <libipset/debug.h> /* D() */
@@ -43,21 +44,23 @@ static char *
ipset_strchr(const char *str, const char *sep) ipset_strchr(const char *str, const char *sep)
{ {
char *match; char *match;
assert(str); assert(str);
assert(sep); assert(sep);
for (; *sep != '\0'; sep++) for (; *sep != '\0'; sep++) {
if ((match = strchr(str, sep[0])) != NULL match = strchr(str, sep[0]);
&& str[0] != sep[0] if (match != NULL &&
&& str[strlen(str)-1] != sep[0]) str[0] != sep[0] &&
str[strlen(str)-1] != sep[0])
return match; return match;
}
return NULL; return NULL;
} }
/* /*
* Parser functions, shamelessly taken from iptables.c, ip6tables.c * Parser functions, shamelessly taken from iptables.c, ip6tables.c
* and parser.c from libnetfilter_conntrack. * and parser.c from libnetfilter_conntrack.
*/ */
@@ -66,7 +69,7 @@ ipset_strchr(const char *str, const char *sep)
*/ */
static int static int
string_to_number_ll(struct ipset_session *session, string_to_number_ll(struct ipset_session *session,
const char *str, const char *str,
unsigned long long min, unsigned long long min,
unsigned long long max, unsigned long long max,
unsigned long long *ret) unsigned long long *ret)
@@ -113,7 +116,7 @@ string_to_cidr(struct ipset_session *session,
const char *str, uint8_t min, uint8_t max, uint8_t *ret) const char *str, uint8_t min, uint8_t max, uint8_t *ret)
{ {
int err = string_to_u8(session, str, ret); int err = string_to_u8(session, str, ret);
if (!err && (*ret < min || *ret > max)) if (!err && (*ret < min || *ret > max))
return syntax_err("'%s' is out of range %u-%u", return syntax_err("'%s' is out of range %u-%u",
str, min, max); str, min, max);
@@ -164,7 +167,7 @@ ipset_parse_ether(struct ipset_session *session,
{ {
unsigned int i = 0; unsigned int i = 0;
unsigned char ether[ETH_ALEN]; unsigned char ether[ETH_ALEN];
assert(session); assert(session);
assert(opt == IPSET_OPT_ETHER); assert(opt == IPSET_OPT_ETHER);
assert(str); assert(str);
@@ -178,9 +181,9 @@ ipset_parse_ether(struct ipset_session *session,
number = strtol(str + i * 3, &end, 16); number = strtol(str + i * 3, &end, 16);
if (end == str + i * 3 + 2 if (end == str + i * 3 + 2 &&
&& (*end == ':' || *end == '\0') (*end == ':' || *end == '\0') &&
&& number >= 0 && number <= 255) number >= 0 && number <= 255)
ether[i] = number; ether[i] = number;
else else
goto error; goto error;
@@ -198,13 +201,13 @@ static int
parse_portname(struct ipset_session *session, const char *str, parse_portname(struct ipset_session *session, const char *str,
uint16_t *port, const char *proto) 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); *port = ntohs((uint16_t) service->s_port);
return 0; return 0;
} }
return syntax_err("cannot parse '%s' as a %s port", str, proto); return syntax_err("cannot parse '%s' as a %s port", str, proto);
} }
@@ -232,8 +235,8 @@ ipset_parse_port(struct ipset_session *session,
assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO); assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
assert(str); assert(str);
if ((err = string_to_u16(session, str, &port)) == 0 if ((err = string_to_u16(session, str, &port)) == 0 ||
|| (err = parse_portname(session, str, &port, proto)) == 0) (err = parse_portname(session, str, &port, proto)) == 0)
err = ipset_session_data_set(session, opt, &port); err = ipset_session_data_set(session, opt, &port);
if (!err) if (!err)
@@ -351,7 +354,7 @@ ipset_parse_proto(struct ipset_session *session,
assert(session); assert(session);
assert(opt == IPSET_OPT_PROTO); assert(opt == IPSET_OPT_PROTO);
assert(str); assert(str);
protoent = getprotobyname(strcasecmp(str, "icmpv6") == 0 protoent = getprotobyname(strcasecmp(str, "icmpv6") == 0
? "ipv6-icmp" : str); ? "ipv6-icmp" : str);
if (protoent == NULL) if (protoent == NULL)
@@ -360,7 +363,7 @@ ipset_parse_proto(struct ipset_session *session,
proto = protoent->p_proto; proto = protoent->p_proto;
if (!proto) if (!proto)
return syntax_err("Unsupported protocol '%s'", str); return syntax_err("Unsupported protocol '%s'", str);
return ipset_session_data_set(session, opt, &proto); return ipset_session_data_set(session, opt, &proto);
} }
@@ -374,7 +377,7 @@ parse_icmp_typecode(struct ipset_session *session,
uint8_t type, code; uint8_t type, code;
char *a, *saved, *tmp; char *a, *saved, *tmp;
int err; int err;
saved = tmp = strdup(str); saved = tmp = strdup(str);
if (tmp == NULL) if (tmp == NULL)
return ipset_err(session, return ipset_err(session,
@@ -384,13 +387,14 @@ parse_icmp_typecode(struct ipset_session *session,
if (a == NULL) { if (a == NULL) {
free(saved); free(saved);
return ipset_err(session, 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'; *a++ = '\0';
if ((err = string_to_u8(session, a, &type)) != 0 if ((err = string_to_u8(session, a, &type)) != 0 ||
|| (err = string_to_u8(session, tmp, &code)) != 0) (err = string_to_u8(session, tmp, &code)) != 0)
goto error; goto error;
typecode = (type << 8) | code; typecode = (type << 8) | code;
err = ipset_session_data_set(session, opt, &typecode); err = ipset_session_data_set(session, opt, &typecode);
@@ -496,41 +500,43 @@ ipset_parse_proto_port(struct ipset_session *session,
err = ipset_parse_proto(session, IPSET_OPT_PROTO, tmp); err = ipset_parse_proto(session, IPSET_OPT_PROTO, tmp);
if (err) if (err)
goto error; goto error;
p = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO); p = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
switch (p) { switch (p) {
case IPPROTO_TCP: case IPPROTO_TCP:
proto = tmp; case IPPROTO_SCTP:
tmp = a;
goto parse_port;
case IPPROTO_UDP: case IPPROTO_UDP:
case IPPROTO_UDPLITE:
proto = tmp; proto = tmp;
tmp = a; tmp = a;
goto parse_port; goto parse_port;
case IPPROTO_ICMP: case IPPROTO_ICMP:
if (family != AF_INET) { 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; goto error;
} }
err = ipset_parse_icmp(session, opt, a); err = ipset_parse_icmp(session, opt, a);
break; break;
case IPPROTO_ICMPV6: case IPPROTO_ICMPV6:
if (family != AF_INET6) { 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; goto error;
} }
err = ipset_parse_icmpv6(session, opt, a); err = ipset_parse_icmpv6(session, opt, a);
break; break;
default: default:
if (!STREQ(a, "0")) { 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; goto error;
} }
ipset_data_flags_set(data, IPSET_FLAG(opt)); ipset_data_flags_set(data, IPSET_FLAG(opt));
} }
goto error; goto error;
} else { } else {
proto = "TCP"; proto = "TCP";
err = ipset_data_set(data, IPSET_OPT_PROTO, &p); err = ipset_data_set(data, IPSET_OPT_PROTO, &p);
if (err) if (err)
goto error; goto error;
@@ -560,7 +566,7 @@ ipset_parse_family(struct ipset_session *session,
{ {
struct ipset_data *data; struct ipset_data *data;
uint8_t family; uint8_t family;
assert(session); assert(session);
assert(opt == IPSET_OPT_FAMILY); assert(opt == IPSET_OPT_FAMILY);
assert(str); assert(str);
@@ -578,7 +584,7 @@ ipset_parse_family(struct ipset_session *session,
family = AF_UNSPEC; family = AF_UNSPEC;
else else
return syntax_err("unknown INET family %s", str); return syntax_err("unknown INET family %s", str);
return ipset_data_set(data, opt, &family); return ipset_data_set(data, opt, &family);
} }
@@ -586,27 +592,27 @@ ipset_parse_family(struct ipset_session *session,
* Parse IPv4/IPv6 addresses, networks and ranges. * Parse IPv4/IPv6 addresses, networks and ranges.
* We resolve hostnames but just the first IP address is used. * We resolve hostnames but just the first IP address is used.
*/ */
static struct addrinfo * static struct addrinfo *
call_getaddrinfo(struct ipset_session *session, const char *str, call_getaddrinfo(struct ipset_session *session, const char *str,
uint8_t family) uint8_t family)
{ {
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *res; struct addrinfo *res;
int err; int err;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME; hints.ai_flags = AI_CANONNAME;
hints.ai_family = family; hints.ai_family = family;
hints.ai_socktype = SOCK_RAW; hints.ai_socktype = SOCK_RAW;
hints.ai_protocol = 0; hints.ai_protocol = 0;
hints.ai_next = NULL; hints.ai_next = NULL;
if ((err = getaddrinfo(str, NULL, &hints, &res)) != 0) { if ((err = getaddrinfo(str, NULL, &hints, &res)) != 0) {
syntax_err("cannot resolve '%s' to an %s address: %s", syntax_err("cannot resolve '%s' to an %s address: %s",
str, family == AF_INET6 ? "IPv6" : "IPv4", str, family == AF_INET6 ? "IPv6" : "IPv4",
gai_strerror(err)); gai_strerror(err));
return NULL; return NULL;
} else } else
return res; return res;
} }
@@ -618,10 +624,10 @@ get_addrinfo(struct ipset_session *session,
struct addrinfo **info, struct addrinfo **info,
uint8_t family) uint8_t family)
{ {
struct addrinfo *i; struct addrinfo *i;
size_t addrlen = family == AF_INET ? sizeof(struct sockaddr_in) size_t addrlen = family == AF_INET ? sizeof(struct sockaddr_in)
: sizeof(struct sockaddr_in6); : sizeof(struct sockaddr_in6);
int found, err = 0; int found, err = 0;
if ((*info = call_getaddrinfo(session, str, family)) == NULL) { if ((*info = call_getaddrinfo(session, str, family)) == NULL) {
syntax_err("cannot parse %s: resolving to %s address failed", syntax_err("cannot parse %s: resolving to %s address failed",
@@ -634,13 +640,21 @@ get_addrinfo(struct ipset_session *session,
continue; continue;
if (found == 0) { if (found == 0) {
if (family == AF_INET) { if (family == AF_INET) {
/* Workaround: direct cast increases required alignment on Sparc */ /* Workaround: direct cast increases
const struct sockaddr_in *saddr = (void *)i->ai_addr; * required alignment on Sparc
err = ipset_session_data_set(session, opt, &saddr->sin_addr); */
const struct sockaddr_in *saddr =
(void *)i->ai_addr;
err = ipset_session_data_set(session,
opt, &saddr->sin_addr);
} else { } else {
/* Workaround: direct cast increases required alignment on Sparc */ /* Workaround: direct cast increases
const struct sockaddr_in6 *saddr = (void *)i->ai_addr; * required alignment on Sparc
err = ipset_session_data_set(session, opt, &saddr->sin6_addr); */
const struct sockaddr_in6 *saddr =
(void *)i->ai_addr;
err = ipset_session_data_set(session,
opt, &saddr->sin6_addr);
} }
} else if (found == 1) { } else if (found == 1) {
ipset_warn(session, ipset_warn(session,
@@ -663,13 +677,20 @@ parse_ipaddr(struct ipset_session *session,
enum ipset_opt opt, const char *str, enum ipset_opt opt, const char *str,
uint8_t family) uint8_t family)
{ {
uint8_t m = family == AF_INET ? 32 : 128; uint8_t m = family == AF_INET ? 32 : 128;
int aerr = EINVAL, err = 0, range = 0; int aerr = EINVAL, err = 0, range = 0;
char *saved = strdup(str); char *saved = strdup(str);
char *a, *tmp = saved; char *a, *tmp = saved;
struct addrinfo *info; struct addrinfo *info;
enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR enum ipset_opt copt, opt2;
: IPSET_OPT_CIDR2;
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) if (tmp == NULL)
return ipset_err(session, return ipset_err(session,
@@ -679,8 +700,8 @@ parse_ipaddr(struct ipset_session *session,
/* IP/mask */ /* IP/mask */
*a++ = '\0'; *a++ = '\0';
if ((err = string_to_cidr(session, a, 0, m, &m)) != 0 if ((err = string_to_cidr(session, a, 0, m, &m)) != 0 ||
|| (err = ipset_session_data_set(session, copt, &m)) != 0) (err = ipset_session_data_set(session, copt, &m)) != 0)
goto out; goto out;
} else if ((a = range_separator(tmp)) != NULL) { } else if ((a = range_separator(tmp)) != NULL) {
/* IP-IP */ /* IP-IP */
@@ -688,11 +709,11 @@ parse_ipaddr(struct ipset_session *session,
D("range %s", a); D("range %s", a);
range++; range++;
} }
if ((aerr = get_addrinfo(session, opt, tmp, &info, family)) != 0 if ((aerr = get_addrinfo(session, opt, tmp, &info, family)) != 0 ||
|| !range) !range)
goto out; goto out;
freeaddrinfo(info); freeaddrinfo(info);
aerr = get_addrinfo(session, IPSET_OPT_IP_TO, a, &info, family); aerr = get_addrinfo(session, opt2, a, &info, family);
out: out:
if (aerr != EINVAL) if (aerr != EINVAL)
@@ -702,7 +723,7 @@ out:
err = -1; err = -1;
free(saved); free(saved);
return err; return err;
} }
enum ipaddr_type { enum ipaddr_type {
IPADDR_ANY, IPADDR_ANY,
@@ -715,7 +736,7 @@ static inline bool
cidr_hostaddr(const char *str, uint8_t family) cidr_hostaddr(const char *str, uint8_t family)
{ {
char *a = cidr_separator(str); char *a = cidr_separator(str);
return family == AF_INET ? STREQ(a, "/32") : STREQ(a, "/128"); return family == AF_INET ? STREQ(a, "/32") : STREQ(a, "/128");
} }
@@ -733,10 +754,10 @@ parse_ip(struct ipset_session *session,
switch (addrtype) { switch (addrtype) {
case IPADDR_PLAIN: case IPADDR_PLAIN:
if (range_separator(str) if (range_separator(str) ||
|| (cidr_separator(str) && !cidr_hostaddr(str, family))) (cidr_separator(str) && !cidr_hostaddr(str, family)))
return syntax_err("plain IP address must be supplied: %s", return syntax_err("plain IP address must be supplied: "
str); "%s", str);
break; break;
case IPADDR_NET: case IPADDR_NET:
if (!cidr_separator(str) || range_separator(str)) if (!cidr_separator(str) || range_separator(str))
@@ -789,7 +810,7 @@ ipset_parse_ip(struct ipset_session *session,
* @opt: option kind of the data * @opt: option kind of the data
* @str: string to parse * @str: string to parse
* *
* Parse string as an IPv4|IPv6 address or hostname. If family * Parse string as an IPv4|IPv6 address or hostname. If family
* is not set yet in the data blob, INET is assumed. * is not set yet in the data blob, INET is assumed.
* The value is stored in the data blob of the session. * The value is stored in the data blob of the session.
* *
@@ -800,9 +821,9 @@ ipset_parse_single_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str) enum ipset_opt opt, const char *str)
{ {
assert(session); assert(session);
assert(opt == IPSET_OPT_IP assert(opt == IPSET_OPT_IP ||
|| opt == IPSET_OPT_IP_TO opt == IPSET_OPT_IP_TO ||
|| opt == IPSET_OPT_IP2); opt == IPSET_OPT_IP2);
assert(str); assert(str);
return parse_ip(session, opt, str, IPADDR_PLAIN); return parse_ip(session, opt, str, IPADDR_PLAIN);
@@ -814,7 +835,7 @@ ipset_parse_single_ip(struct ipset_session *session,
* @opt: option kind of the data * @opt: option kind of the data
* @str: string to parse * @str: string to parse
* *
* Parse string as an IPv4|IPv6 address/cidr pattern. If family * Parse string as an IPv4|IPv6 address/cidr pattern. If family
* is not set yet in the data blob, INET is assumed. * is not set yet in the data blob, INET is assumed.
* The value is stored in the data blob of the session. * The value is stored in the data blob of the session.
* *
@@ -876,8 +897,8 @@ ipset_parse_netrange(struct ipset_session *session,
assert(str); assert(str);
if (!(range_separator(str) || cidr_separator(str))) if (!(range_separator(str) || cidr_separator(str)))
return syntax_err("IP/cidr or IP-IP range must be specified: %s", return syntax_err("IP/cidr or IP-IP range must be specified: "
str); "%s", str);
return parse_ip(session, opt, str, IPADDR_ANY); return parse_ip(session, opt, str, IPADDR_ANY);
} }
@@ -903,8 +924,8 @@ ipset_parse_iprange(struct ipset_session *session,
assert(str); assert(str);
if (cidr_separator(str)) if (cidr_separator(str))
return syntax_err("IP address or IP-IP range must be specified: %s", return syntax_err("IP address or IP-IP range must be "
str); "specified: %s", str);
return parse_ip(session, opt, str, IPADDR_ANY); return parse_ip(session, opt, str, IPADDR_ANY);
} }
@@ -960,20 +981,60 @@ ipset_parse_ip4_single6(struct ipset_session *session,
assert(session); assert(session);
assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
assert(str); assert(str);
data = ipset_session_data(session); data = ipset_session_data(session);
family = ipset_data_family(data); family = ipset_data_family(data);
if (family == AF_UNSPEC) { if (family == AF_UNSPEC) {
family = AF_INET; family = AF_INET;
ipset_data_set(data, IPSET_OPT_FAMILY, &family); ipset_data_set(data, IPSET_OPT_FAMILY, &family);
} }
return family == AF_INET ? ipset_parse_ip(session, opt, str) return family == AF_INET ? ipset_parse_ip(session, opt, str)
: ipset_parse_single_ip(session, opt, str); : ipset_parse_single_ip(session, opt, str);
} }
/**
* 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 * ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout
* @session: session structure * @session: session structure
@@ -1003,8 +1064,8 @@ ipset_parse_iptimeout(struct ipset_session *session,
if (ipset_data_flags_test(ipset_session_data(session), if (ipset_data_flags_test(ipset_session_data(session),
IPSET_FLAG(IPSET_OPT_TIMEOUT))) IPSET_FLAG(IPSET_OPT_TIMEOUT)))
return syntax_err("mixed syntax, timeout already specified"); return syntax_err("mixed syntax, timeout already specified");
tmp = saved = strdup(str); tmp = saved = strdup(str);
if (saved == NULL) if (saved == NULL)
return ipset_err(session, return ipset_err(session,
"Cannot allocate memory to duplicate %s.", "Cannot allocate memory to duplicate %s.",
@@ -1026,12 +1087,12 @@ ipset_parse_iptimeout(struct ipset_session *session,
#define check_setname(str, saved) \ #define check_setname(str, saved) \
do { \ do { \
if (strlen(str) > IPSET_MAXNAMELEN - 1) { \ if (strlen(str) > IPSET_MAXNAMELEN - 1) { \
if (saved != NULL) \ if (saved != NULL) \
free(saved); \ 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); \ str, IPSET_MAXNAMELEN - 1); \
} \ } \
} while (0) } while (0)
@@ -1066,7 +1127,7 @@ ipset_parse_name_compat(struct ipset_session *session,
if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF)))
syntax_err("mixed syntax, before|after option already used"); syntax_err("mixed syntax, before|after option already used");
tmp = saved = strdup(str); tmp = saved = strdup(str);
if (saved == NULL) if (saved == NULL)
return ipset_err(session, return ipset_err(session,
"Cannot allocate memory to duplicate %s.", "Cannot allocate memory to duplicate %s.",
@@ -1076,8 +1137,8 @@ ipset_parse_name_compat(struct ipset_session *session,
*a++ = '\0'; *a++ = '\0';
if ((b = elem_separator(a)) != NULL) if ((b = elem_separator(a)) != NULL)
*b++ = '\0'; *b++ = '\0';
if (b == NULL if (b == NULL ||
|| !(STREQ(a, "before") || STREQ(a, "after"))) { !(STREQ(a, "before") || STREQ(a, "after"))) {
err = ipset_err(session, "you must specify elements " err = ipset_err(session, "you must specify elements "
"as setname%s[before|after]%ssetname", "as setname%s[before|after]%ssetname",
sep, sep); sep, sep);
@@ -1118,9 +1179,9 @@ ipset_parse_setname(struct ipset_session *session,
enum ipset_opt opt, const char *str) enum ipset_opt opt, const char *str)
{ {
assert(session); assert(session);
assert(opt == IPSET_SETNAME assert(opt == IPSET_SETNAME ||
|| opt == IPSET_OPT_NAME opt == IPSET_OPT_NAME ||
|| opt == IPSET_OPT_SETNAME2); opt == IPSET_OPT_SETNAME2);
assert(str); assert(str);
check_setname(str, NULL); check_setname(str, NULL);
@@ -1206,13 +1267,13 @@ ipset_parse_uint32(struct ipset_session *session,
{ {
uint32_t value; uint32_t value;
int err; int err;
assert(session); assert(session);
assert(str); assert(str);
if ((err = string_to_u32(session, str, &value)) == 0) if ((err = string_to_u32(session, str, &value)) == 0)
return ipset_session_data_set(session, opt, &value); return ipset_session_data_set(session, opt, &value);
return err; return err;
} }
@@ -1233,7 +1294,7 @@ ipset_parse_uint8(struct ipset_session *session,
{ {
uint8_t value; uint8_t value;
int err; int err;
assert(session); assert(session);
assert(str); assert(str);
@@ -1262,7 +1323,7 @@ ipset_parse_netmask(struct ipset_session *session,
uint8_t family, cidr; uint8_t family, cidr;
struct ipset_data *data; struct ipset_data *data;
int err = 0; int err = 0;
assert(session); assert(session);
assert(opt == IPSET_OPT_NETMASK); assert(opt == IPSET_OPT_NETMASK);
assert(str); assert(str);
@@ -1275,7 +1336,7 @@ ipset_parse_netmask(struct ipset_session *session,
} }
err = string_to_cidr(session, str, err = string_to_cidr(session, str,
family == AF_INET ? 1 : 4, family == AF_INET ? 1 : 4,
family == AF_INET ? 31 : 124, family == AF_INET ? 31 : 124,
&cidr); &cidr);
@@ -1304,7 +1365,7 @@ ipset_parse_flag(struct ipset_session *session,
enum ipset_opt opt, const char *str UNUSED) enum ipset_opt opt, const char *str UNUSED)
{ {
assert(session); assert(session);
return ipset_session_data_set(session, opt, NULL); return ipset_session_data_set(session, opt, NULL);
} }
@@ -1344,10 +1405,45 @@ ipset_parse_typename(struct ipset_session *session,
if (type == NULL) if (type == NULL)
return -1; return -1;
return ipset_session_data_set(session, IPSET_OPT_TYPE, type); 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 * ipset_parse_output - parse output format name
* @session: session structure * @session: session structure
@@ -1396,7 +1492,8 @@ ipset_parse_ignored(struct ipset_session *session,
if (!ipset_data_ignored(ipset_session_data(session), opt)) if (!ipset_data_ignored(ipset_session_data(session), opt))
ipset_warn(session, ipset_warn(session,
"Option %s is ignored. Please upgrade your syntax.", str); "Option %s is ignored. "
"Please upgrade your syntax.", str);
return 0; return 0;
} }
@@ -1416,19 +1513,18 @@ ipset_parse_ignored(struct ipset_session *session,
*/ */
int int
ipset_call_parser(struct ipset_session *session, ipset_call_parser(struct ipset_session *session,
ipset_parsefn parse, const char *optstr, const struct ipset_arg *arg,
enum ipset_opt opt, const char *str) const char *str)
{ {
if (ipset_data_flags_test(ipset_session_data(session), if (ipset_data_flags_test(ipset_session_data(session),
IPSET_FLAG(opt))) IPSET_FLAG(arg->opt)))
syntax_err("%s already specified", optstr); syntax_err("%s already specified", arg->name[0]);
return parse(session, opt, parse == ipset_parse_ignored return arg->parse(session, arg->opt, str);
? optstr : str);
} }
#define parse_elem(s, t, d, str) \ #define parse_elem(s, t, d, str) \
do { \ do { \
if (!(t)->elem[d].parse) \ if (!(t)->elem[d].parse) \
goto internal; \ goto internal; \
ret = (t)->elem[d].parse(s, (t)->elem[d].opt, str); \ ret = (t)->elem[d].parse(s, (t)->elem[d].opt, str); \
@@ -1486,8 +1582,8 @@ ipset_parse_elem(struct ipset_session *session,
} else if (a != NULL) { } else if (a != NULL) {
if (type->compat_parse_elem) { if (type->compat_parse_elem) {
ret = type->compat_parse_elem(session, ret = type->compat_parse_elem(session,
type->elem[IPSET_DIM_ONE].opt, type->elem[IPSET_DIM_ONE].opt,
saved); saved);
goto out; goto out;
} }
elem_syntax_err("Elem separator in %s, " elem_syntax_err("Elem separator in %s, "

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <assert.h> /* assert */ #include <assert.h> /* assert */
@@ -12,6 +12,7 @@
#include <sys/socket.h> /* inet_ntop */ #include <sys/socket.h> /* inet_ntop */
#include <arpa/inet.h> /* inet_ntop */ #include <arpa/inet.h> /* inet_ntop */
#include <net/ethernet.h> /* ETH_ALEN */ #include <net/ethernet.h> /* ETH_ALEN */
#include <net/if.h> /* IFNAMSIZ */
#include <libipset/debug.h> /* D() */ #include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_* */ #include <libipset/data.h> /* ipset_data_* */
@@ -53,15 +54,15 @@ ipset_print_ether(char *buf, unsigned int len,
{ {
const unsigned char *ether; const unsigned char *ether;
int i, size, offset = 0; int i, size, offset = 0;
assert(buf); assert(buf);
assert(len > 0); assert(len > 0);
assert(data); assert(data);
assert(opt == IPSET_OPT_ETHER); assert(opt == IPSET_OPT_ETHER);
if (len < ETH_ALEN*3) if (len < ETH_ALEN*3)
return -1; return -1;
ether = ipset_data_get(data, opt); ether = ipset_data_get(data, opt);
assert(ether); assert(ether);
@@ -71,7 +72,7 @@ ipset_print_ether(char *buf, unsigned int len,
size = snprintf(buf + offset, len, ":%02X", ether[i]); size = snprintf(buf + offset, len, ":%02X", ether[i]);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
} }
return offset; return offset;
} }
@@ -138,34 +139,60 @@ ipset_print_type(char *buf, unsigned int len,
assert(type); assert(type);
if (len < strlen(type->name) + 1) if (len < strlen(type->name) + 1)
return -1; return -1;
return snprintf(buf, len, "%s", type->name); return snprintf(buf, len, "%s", type->name);
} }
#define GETNAMEINFO(family, f, n) \ static inline int
static inline int \ __getnameinfo4(char *buf, unsigned int len,
__getnameinfo##f(char *buf, unsigned int len, \ int flags, const union nf_inet_addr *addr)
int flags, const union nf_inet_addr *addr) \ {
{ \ struct sockaddr_in saddr;
struct sockaddr_in##n saddr; \ int err;
int err; \
\ memset(&saddr, 0, sizeof(saddr));
memset(&saddr, 0, sizeof(saddr)); \ in4cpy(&saddr.sin_addr, &addr->in);
in##f##cpy(&saddr.sin##n##_addr, &addr->in##n); \ saddr.sin_family = AF_INET;
saddr.sin##n##_family = family; \
\ err = getnameinfo((const struct sockaddr *)&saddr,
err = getnameinfo((const struct sockaddr *)&saddr, \ sizeof(saddr),
sizeof(saddr), \ buf, len, NULL, 0, flags);
buf, len, NULL, 0, flags); \
\ if (!(flags & NI_NUMERICHOST) &&
if (err == EAI_AGAIN && !(flags & NI_NUMERICHOST)) \ (err == EAI_AGAIN || (err == 0 && strchr(buf, '-') != NULL)))
err = getnameinfo((const struct sockaddr *)&saddr, \ err = getnameinfo((const struct sockaddr *)&saddr,
sizeof(saddr), \ sizeof(saddr),
buf, len, NULL, 0, \ buf, len, NULL, 0,
flags | NI_NUMERICHOST); \ flags | NI_NUMERICHOST);
D("getnameinfo err: %i, errno %i", err, errno); \ D("getnameinfo err: %i, errno %i", err, errno);
return (err == 0 ? (int)strlen(buf) : \ return (err == 0 ? (int)strlen(buf) :
(err == EAI_OVERFLOW || err == EAI_SYSTEM) ? (int)len : -1);\ (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) \ #define SNPRINTF_IP(mask, f) \
@@ -188,10 +215,8 @@ snprintf_ipv##f(char *buf, unsigned int len, int flags, \
return offset; \ return offset; \
} }
GETNAMEINFO(AF_INET, 4, )
SNPRINTF_IP(32, 4) SNPRINTF_IP(32, 4)
GETNAMEINFO(AF_INET6, 6, 6)
SNPRINTF_IP(128, 6) SNPRINTF_IP(128, 6)
/** /**
@@ -229,8 +254,8 @@ ipset_print_ip(char *buf, unsigned int len,
D("CIDR: %u", cidr); D("CIDR: %u", cidr);
} else } else
cidr = family == AF_INET6 ? 128 : 32; 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); ip = ipset_data_get(data, opt);
assert(ip); assert(ip);
if (family == AF_INET) if (family == AF_INET)
@@ -256,8 +281,8 @@ ipset_print_ip(char *buf, unsigned int len,
size = snprintf_ipv6(buf + offset, len, flags, ip, cidr); size = snprintf_ipv6(buf + offset, len, flags, ip, cidr);
else else
return -1; return -1;
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
return offset; return offset;
} }
@@ -286,9 +311,9 @@ ipset_print_ipaddr(char *buf, unsigned int len,
assert(buf); assert(buf);
assert(len > 0); assert(len > 0);
assert(data); assert(data);
assert(opt == IPSET_OPT_IP assert(opt == IPSET_OPT_IP ||
|| opt == IPSET_OPT_IP_TO opt == IPSET_OPT_IP_TO ||
|| opt == IPSET_OPT_IP2); opt == IPSET_OPT_IP2);
family = ipset_data_family(data); family = ipset_data_family(data);
cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2; cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
@@ -296,7 +321,7 @@ ipset_print_ipaddr(char *buf, unsigned int len,
cidr = *(const uint8_t *) ipset_data_get(data, cidropt); cidr = *(const uint8_t *) ipset_data_get(data, cidropt);
else else
cidr = family == AF_INET6 ? 128 : 32; 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); ip = ipset_data_get(data, opt);
assert(ip); assert(ip);
@@ -331,7 +356,7 @@ ipset_print_number(char *buf, unsigned int len,
assert(buf); assert(buf);
assert(len > 0); assert(len > 0);
assert(data); assert(data);
number = ipset_data_get(data, opt); number = ipset_data_get(data, opt);
maxsize = ipset_data_sizeof(opt, AF_INET); maxsize = ipset_data_sizeof(opt, AF_INET);
D("opt: %u, maxsize %zu", opt, maxsize); D("opt: %u, maxsize %zu", opt, maxsize);
@@ -378,7 +403,7 @@ ipset_print_name(char *buf, unsigned int len,
name = ipset_data_get(data, opt); name = ipset_data_get(data, opt);
assert(name); assert(name);
size = snprintf(buf, len, "%s", name); size = snprintf(buf, len, "%s", name);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
if (ipset_data_test(data, IPSET_OPT_NAMEREF)) { if (ipset_data_test(data, IPSET_OPT_NAMEREF)) {
bool before = false; bool before = false;
@@ -388,10 +413,10 @@ ipset_print_name(char *buf, unsigned int len,
before = (*flags) & IPSET_FLAG_BEFORE; before = (*flags) & IPSET_FLAG_BEFORE;
} }
size = snprintf(buf + offset, len, size = snprintf(buf + offset, len,
" %s %s", before ? "before" : "after", " %s %s", before ? "before" : "after",
(const char *) ipset_data_get(data, (const char *) ipset_data_get(data,
IPSET_OPT_NAMEREF)); IPSET_OPT_NAMEREF));
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
} }
return offset; return offset;
@@ -429,19 +454,58 @@ ipset_print_port(char *buf, unsigned int len,
port = ipset_data_get(data, IPSET_OPT_PORT); port = ipset_data_get(data, IPSET_OPT_PORT);
assert(port); assert(port);
size = snprintf(buf, len, "%u", *port); size = snprintf(buf, len, "%u", *port);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
if (ipset_data_test(data, IPSET_OPT_PORT_TO)) { if (ipset_data_test(data, IPSET_OPT_PORT_TO)) {
port = ipset_data_get(data, IPSET_OPT_PORT_TO); port = ipset_data_get(data, IPSET_OPT_PORT_TO);
size = snprintf(buf + offset, len, size = snprintf(buf + offset, len,
"%s%u", "%s%u",
IPSET_RANGE_SEPARATOR, *port); IPSET_RANGE_SEPARATOR, *port);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
} }
return offset; 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 * ipset_print_proto - print protocol name
* @buf: printing buffer * @buf: printing buffer
@@ -470,12 +534,12 @@ ipset_print_proto(char *buf, unsigned int len,
proto = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO); proto = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
assert(proto); assert(proto);
protoent = getprotobynumber(proto); protoent = getprotobynumber(proto);
if (protoent) if (protoent)
return snprintf(buf, len, "%s", protoent->p_name); return snprintf(buf, len, "%s", protoent->p_name);
/* Should not happen */ /* Should not happen */
return snprintf(buf, len, "%u", proto); return snprintf(buf, len, "%u", proto);
} }
@@ -510,7 +574,8 @@ ipset_print_icmp(char *buf, unsigned int len,
if (name != NULL) if (name != NULL)
return snprintf(buf, len, "%s", name); return snprintf(buf, len, "%s", name);
else else
return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF); return snprintf(buf, len, "%u/%u",
typecode >> 8, typecode & 0xFF);
} }
/** /**
@@ -544,7 +609,8 @@ ipset_print_icmpv6(char *buf, unsigned int len,
if (name != NULL) if (name != NULL)
return snprintf(buf, len, "%s", name); return snprintf(buf, len, "%s", name);
else else
return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF); return snprintf(buf, len, "%u/%u",
typecode >> 8, typecode & 0xFF);
} }
/** /**
@@ -573,8 +639,8 @@ ipset_print_proto_port(char *buf, unsigned int len,
assert(opt == IPSET_OPT_PORT); assert(opt == IPSET_OPT_PORT);
if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) { if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) {
uint8_t proto = *(const uint8_t *) ipset_data_get(data, uint8_t proto = *(const uint8_t *) ipset_data_get(data,
IPSET_OPT_PROTO); IPSET_OPT_PROTO);
size = ipset_print_proto(buf, len, data, IPSET_OPT_PROTO, env); size = ipset_print_proto(buf, len, data, IPSET_OPT_PROTO, env);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
if (len < 2) if (len < 2)
@@ -584,7 +650,9 @@ ipset_print_proto_port(char *buf, unsigned int len,
switch (proto) { switch (proto) {
case IPPROTO_TCP: case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_UDP: case IPPROTO_UDP:
case IPPROTO_UDPLITE:
break; break;
case IPPROTO_ICMP: case IPPROTO_ICMP:
return ipset_print_icmp(buf + offset, len, data, return ipset_print_icmp(buf + offset, len, data,
@@ -598,16 +666,16 @@ ipset_print_proto_port(char *buf, unsigned int len,
} }
size = ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env); size = ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
return offset; return offset;
} }
#define print_second(data) \ #define print_second(data) \
ipset_data_flags_test(data, \ ipset_data_flags_test(data, \
IPSET_FLAG(IPSET_OPT_PORT)|IPSET_FLAG(IPSET_OPT_ETHER)) IPSET_FLAG(IPSET_OPT_PORT)|IPSET_FLAG(IPSET_OPT_ETHER))
#define print_third(data) \ #define print_third(data) \
ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_IP2)) ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_IP2))
/** /**
* ipset_print_elem - print ADT elem according to settype * ipset_print_elem - print ADT elem according to settype
@@ -636,25 +704,25 @@ ipset_print_elem(char *buf, unsigned int len,
type = ipset_data_get(data, IPSET_OPT_TYPE); type = ipset_data_get(data, IPSET_OPT_TYPE);
if (!type) if (!type)
return -1; return -1;
size = type->elem[IPSET_DIM_ONE].print(buf, len, data, size = type->elem[IPSET_DIM_ONE].print(buf, len, data,
type->elem[IPSET_DIM_ONE].opt, env); type->elem[IPSET_DIM_ONE].opt, env);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
IF_D(ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt), IF_D(ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt),
"print second elem"); "print second elem");
if (type->dimension == IPSET_DIM_ONE if (type->dimension == IPSET_DIM_ONE ||
|| (type->last_elem_optional (type->last_elem_optional &&
&& !ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt))) !ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt)))
return offset; return offset;
size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR); size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
size = type->elem[IPSET_DIM_TWO].print(buf + offset, len, data, size = type->elem[IPSET_DIM_TWO].print(buf + offset, len, data,
type->elem[IPSET_DIM_TWO].opt, env); type->elem[IPSET_DIM_TWO].opt, env);
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
if (type->dimension == IPSET_DIM_TWO if (type->dimension == IPSET_DIM_TWO ||
|| (type->last_elem_optional (type->last_elem_optional &&
&& !ipset_data_test(data, type->elem[IPSET_DIM_THREE].opt))) !ipset_data_test(data, type->elem[IPSET_DIM_THREE].opt)))
return offset; return offset;
size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR); size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
@@ -682,7 +750,7 @@ int
ipset_print_flag(char *buf UNUSED, unsigned int len UNUSED, ipset_print_flag(char *buf UNUSED, unsigned int len UNUSED,
const struct ipset_data *data UNUSED, const struct ipset_data *data UNUSED,
enum ipset_opt opt UNUSED, uint8_t env UNUSED) enum ipset_opt opt UNUSED, uint8_t env UNUSED)
{ {
return 0; return 0;
} }
@@ -728,6 +796,9 @@ ipset_print_data(char *buf, unsigned int len,
case IPSET_OPT_PORT: case IPSET_OPT_PORT:
size = ipset_print_port(buf, len, data, opt, env); size = ipset_print_port(buf, len, data, opt, env);
break; break;
case IPSET_OPT_IFACE:
size = ipset_print_iface(buf, len, data, opt, env);
break;
case IPSET_OPT_GC: case IPSET_OPT_GC:
case IPSET_OPT_HASHSIZE: case IPSET_OPT_HASHSIZE:
case IPSET_OPT_MAXELEM: case IPSET_OPT_MAXELEM:
@@ -744,6 +815,6 @@ ipset_print_data(char *buf, unsigned int len,
return -1; return -1;
} }
SNPRINTF_FAILURE(size, len, offset); SNPRINTF_FAILURE(size, len, offset);
return offset; return offset;
} }

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <assert.h> /* assert */ #include <assert.h> /* assert */
@@ -13,6 +13,7 @@
#include <string.h> /* str* */ #include <string.h> /* str* */
#include <unistd.h> /* getpagesize */ #include <unistd.h> /* getpagesize */
#include <net/ethernet.h> /* ETH_ALEN */ #include <net/ethernet.h> /* ETH_ALEN */
#include <net/if.h> /* IFNAMSIZ */
#include <libipset/debug.h> /* D() */ #include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -37,6 +38,7 @@ struct ipset_session {
/* Command state */ /* Command state */
enum ipset_cmd cmd; /* Current command */ enum ipset_cmd cmd; /* Current command */
uint32_t lineno; /* Current lineno in restore mode */ uint32_t lineno; /* Current lineno in restore mode */
uint32_t printed_set; /* Printed sets so far */
char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */ char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */
const struct ipset_type *saved_type; /* Saved type */ const struct ipset_type *saved_type; /* Saved type */
struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */ struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */
@@ -138,6 +140,8 @@ ipset_envopt_parse(struct ipset_session *session, int opt,
case IPSET_ENV_QUIET: case IPSET_ENV_QUIET:
case IPSET_ENV_RESOLVE: case IPSET_ENV_RESOLVE:
case IPSET_ENV_EXIST: case IPSET_ENV_EXIST:
case IPSET_ENV_LIST_SETNAME:
case IPSET_ENV_LIST_HEADER:
session->envopts |= opt; session->envopts |= opt;
return 0; return 0;
default: default:
@@ -196,14 +200,14 @@ ipset_session_output(struct ipset_session *session,
* *
* Returns -1. * Returns -1.
*/ */
int __attribute__((format(printf,3,4))) int __attribute__((format(printf, 3, 4)))
ipset_session_report(struct ipset_session *session, ipset_session_report(struct ipset_session *session,
enum ipset_err_type type, enum ipset_err_type type,
const char *fmt, ...) const char *fmt, ...)
{ {
int len, offset = 0; int len, offset = 0;
va_list args; va_list args;
assert(session); assert(session);
assert(fmt); assert(fmt);
@@ -212,13 +216,13 @@ ipset_session_report(struct ipset_session *session,
session->lineno); session->lineno);
} }
offset = strlen(session->report); offset = strlen(session->report);
va_start(args, fmt); va_start(args, fmt);
len = vsnprintf(session->report + offset, len = vsnprintf(session->report + offset,
IPSET_ERRORBUFLEN - 1 - offset, IPSET_ERRORBUFLEN - 1 - offset,
fmt, args); fmt, args);
va_end(args); va_end(args);
if (len >= IPSET_ERRORBUFLEN - 1 - offset) if (len >= IPSET_ERRORBUFLEN - 1 - offset)
session->report[IPSET_ERRORBUFLEN - 1] = '\0'; session->report[IPSET_ERRORBUFLEN - 1] = '\0';
if (strlen(session->report) < IPSET_ERRORBUFLEN - 1) if (strlen(session->report) < IPSET_ERRORBUFLEN - 1)
@@ -468,6 +472,15 @@ static const struct ipset_attr_policy adt_attrs[] = {
.type = MNL_TYPE_U8, .type = MNL_TYPE_U8,
.opt = IPSET_OPT_CIDR2, .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[] = { static const struct ipset_attr_policy ipaddr_attrs[] = {
@@ -480,26 +493,30 @@ static const struct ipset_attr_policy ipaddr_attrs[] = {
}, },
}; };
#ifdef IPSET_DEBUG
static int debug = 1;
#endif
static int static int
generic_data_attr_cb(const struct nlattr *attr, void *data, generic_data_attr_cb(const struct nlattr *attr, void *data,
int attr_max, const struct ipset_attr_policy *policy) int attr_max, const struct ipset_attr_policy *policy)
{ {
const struct nlattr **tb = data; const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr); 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) { 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; return MNL_CB_ERROR;
} }
if (mnl_attr_validate(attr, policy[type].type) < 0) { 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)); mnl_attr_get_payload_len(attr));
return MNL_CB_ERROR; return MNL_CB_ERROR;
} }
if (policy[type].type == MNL_TYPE_NUL_STRING if (policy[type].type == MNL_TYPE_NUL_STRING &&
&& mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN) mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN)
return MNL_CB_ERROR; return MNL_CB_ERROR;
tb[type] = attr; tb[type] = attr;
return MNL_CB_OK; return MNL_CB_OK;
} }
@@ -583,7 +600,7 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
switch (attr->type) { switch (attr->type) {
case MNL_TYPE_U32: { case MNL_TYPE_U32: {
uint32_t value; uint32_t value;
value = ntohl(*(const uint32_t *)d); value = ntohl(*(const uint32_t *)d);
d = &value; d = &value;
@@ -591,7 +608,7 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
} }
case MNL_TYPE_U16: { case MNL_TYPE_U16: {
uint16_t value; uint16_t value;
value = ntohs(*(const uint16_t *)d); value = ntohs(*(const uint16_t *)d);
d = &value; d = &value;
@@ -602,15 +619,15 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
} }
} }
#ifdef IPSET_DEBUG #ifdef IPSET_DEBUG
if (type == IPSET_ATTR_TYPENAME) if (type == IPSET_ATTR_TYPENAME)
D("nla typename %s", (const char *) d); D("nla typename %s", (const char *) d);
#endif #endif
ret = ipset_data_set(data, attr->opt, d); ret = ipset_data_set(data, attr->opt, d);
#ifdef IPSET_DEBUG #ifdef IPSET_DEBUG
if (type == IPSET_ATTR_TYPENAME) if (type == IPSET_ATTR_TYPENAME)
D("nla typename %s", D("nla typename %s",
(const char *) ipset_data_get(data, IPSET_OPT_TYPENAME)); (const char *) ipset_data_get(data, IPSET_OPT_TYPENAME));
#endif #endif
return ret; return ret;
} }
@@ -639,16 +656,16 @@ static inline int
call_outfn(struct ipset_session *session) call_outfn(struct ipset_session *session)
{ {
int ret = session->outfn("%s", session->outbuf); int ret = session->outfn("%s", session->outbuf);
session->outbuf[0] = '\0'; session->outbuf[0] = '\0';
return ret < 0 ? ret : 0; return ret < 0 ? ret : 0;
} }
/* Handle printing failures */ /* Handle printing failures */
static jmp_buf printf_failure; 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, ...) safe_snprintf(struct ipset_session *session, const char *fmt, ...)
{ {
va_list args; va_list args;
@@ -661,7 +678,7 @@ retry:
ret = vsnprintf(session->outbuf + len, IPSET_OUTBUFLEN - len, ret = vsnprintf(session->outbuf + len, IPSET_OUTBUFLEN - len,
fmt, args); fmt, args);
va_end(args); va_end(args);
if (ret < 0) { if (ret < 0) {
ipset_err(session, ipset_err(session,
"Internal error at printing to output buffer"); "Internal error at printing to output buffer");
@@ -695,7 +712,7 @@ retry:
D("len: %u, retry %u", len, loop); D("len: %u, retry %u", len, loop);
ret = fn(session->outbuf + len, IPSET_OUTBUFLEN - len, ret = fn(session->outbuf + len, IPSET_OUTBUFLEN - len,
session->data, opt, session->envopts); session->data, opt, session->envopts);
if (ret < 0) { if (ret < 0) {
ipset_err(session, ipset_err(session,
"Internal error at printing to output buffer"); "Internal error at printing to output buffer");
@@ -717,7 +734,7 @@ retry:
} }
return ret; return ret;
} }
static int static int
list_adt(struct ipset_session *session, struct nlattr *nla[]) list_adt(struct ipset_session *session, struct nlattr *nla[])
{ {
@@ -727,7 +744,7 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
uint8_t family; uint8_t family;
int i, found = 0; int i, found = 0;
D("enter"); D("enter");
/* Check and load type, family */ /* Check and load type, family */
if (!ipset_data_test(data, IPSET_OPT_TYPE)) if (!ipset_data_test(data, IPSET_OPT_TYPE))
type = ipset_type_get(session, IPSET_CMD_ADD); type = ipset_type_get(session, IPSET_CMD_ADD);
@@ -758,7 +775,7 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
default: default:
break; break;
} }
safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM); safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM);
for (arg = type->args[IPSET_ADD]; arg != NULL && arg->print; arg++) { for (arg = type->args[IPSET_ADD]; arg != NULL && arg->print; arg++) {
@@ -789,7 +806,7 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
break; break;
} }
} }
if (session->mode == IPSET_LIST_XML) if (session->mode == IPSET_LIST_XML)
safe_snprintf(session, "</member>\n"); safe_snprintf(session, "</member>\n");
else else
@@ -829,8 +846,9 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
type->name); type->name);
break; break;
case IPSET_LIST_PLAIN: case IPSET_LIST_PLAIN:
safe_snprintf(session, "Name: %s\n" safe_snprintf(session, "%sName: %s\n"
"Type: %s\nHeader: ", "Type: %s\nHeader: ",
session->printed_set ? "\n" : "",
ipset_data_setname(data), ipset_data_setname(data),
type->name); type->name);
break; break;
@@ -847,10 +865,10 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
} }
for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->opt; arg++) { for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->opt; arg++) {
if (!arg->print if (!arg->print ||
|| !ipset_data_test(data, arg->opt) !ipset_data_test(data, arg->opt) ||
|| (arg->opt == IPSET_OPT_FAMILY (arg->opt == IPSET_OPT_FAMILY &&
&& family == type->family)) family == type->family))
continue; continue;
switch (session->mode) { switch (session->mode) {
case IPSET_LIST_SAVE: case IPSET_LIST_SAVE:
@@ -887,18 +905,24 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE); safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
safe_snprintf(session, "\nReferences: "); safe_snprintf(session, "\nReferences: ");
safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES); 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; break;
case IPSET_LIST_XML: case IPSET_LIST_XML:
safe_snprintf(session, "</elements>\n <memsize>"); safe_snprintf(session, " <memsize>");
safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE); safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
safe_snprintf(session, "</memsize>\n <references>"); safe_snprintf(session, "</memsize>\n <references>");
safe_dprintf(session, ipset_print_number, IPSET_OPT_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; break;
default: default:
break; break;
} }
session->printed_set++;
return MNL_CB_OK; return MNL_CB_OK;
} }
@@ -910,16 +934,17 @@ print_set_done(struct ipset_session *session)
? "NONE" : session->saved_setname); ? "NONE" : session->saved_setname);
switch (session->mode) { switch (session->mode) {
case IPSET_LIST_XML: case IPSET_LIST_XML:
if (session->saved_setname[0] == '\0') if (session->envopts & IPSET_ENV_LIST_SETNAME)
safe_snprintf(session, "\n"); break;
else 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"); safe_snprintf(session, " </members>\n</ipset>\n");
break; break;
case IPSET_LIST_SAVE:
/* No empty lines between the sets */
break;
default: default:
safe_snprintf(session, "\n");
break; break;
} }
return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP; return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
@@ -931,8 +956,11 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
{ {
struct ipset_data *data = session->data; 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; return MNL_CB_ERROR;
}
if (!nla[IPSET_ATTR_SETNAME]) if (!nla[IPSET_ATTR_SETNAME])
FAILURE("Broken %s kernel message: missing setname!", FAILURE("Broken %s kernel message: missing setname!",
@@ -940,17 +968,28 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs); ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs);
D("setname %s", ipset_data_setname(data)); 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)) { if (STREQ(ipset_data_setname(data), session->saved_setname)) {
/* Header part already seen */ /* Header part already seen */
if (ipset_data_test(data, IPSET_OPT_TYPE) if (ipset_data_test(data, IPSET_OPT_TYPE) &&
&& nla[IPSET_ATTR_DATA] != NULL) nla[IPSET_ATTR_DATA] != NULL)
FAILURE("Broken %s kernel message: " FAILURE("Broken %s kernel message: "
"extra DATA received!", cmd2name[cmd]); "extra DATA received!", cmd2name[cmd]);
} else { } else {
if (nla[IPSET_ATTR_DATA] == NULL) if (nla[IPSET_ATTR_DATA] == NULL)
FAILURE("Broken %s kernel message: " FAILURE("Broken %s kernel message: "
"missing DATA part!", cmd2name[cmd]); "missing DATA part!", cmd2name[cmd]);
/* Close previous set printing */ /* Close previous set printing */
if (session->saved_setname[0] != '\0') if (session->saved_setname[0] != '\0')
print_set_done(session); print_set_done(session);
@@ -959,13 +998,14 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
if (nla[IPSET_ATTR_DATA] != NULL) { if (nla[IPSET_ATTR_DATA] != NULL) {
struct nlattr *cattr[IPSET_ATTR_CREATE_MAX+1] = {}; struct nlattr *cattr[IPSET_ATTR_CREATE_MAX+1] = {};
if (!(nla[IPSET_ATTR_TYPENAME] if (!(nla[IPSET_ATTR_TYPENAME] &&
&& nla[IPSET_ATTR_FAMILY] nla[IPSET_ATTR_FAMILY] &&
&& nla[IPSET_ATTR_REVISION])) nla[IPSET_ATTR_REVISION]))
FAILURE("Broken %s kernel message: missing %s!", FAILURE("Broken %s kernel message: missing %s!",
cmd2name[cmd], cmd2name[cmd],
!nla[IPSET_ATTR_TYPENAME] ? "typename" : !nla[IPSET_ATTR_TYPENAME] ? "typename" :
!nla[IPSET_ATTR_FAMILY] ? "family" : "revision"); !nla[IPSET_ATTR_FAMILY] ? "family" :
"revision");
/* Reset CREATE specific flags */ /* Reset CREATE specific flags */
ipset_data_flags_unset(data, IPSET_CREATE_FLAGS); ipset_data_flags_unset(data, IPSET_CREATE_FLAGS);
@@ -988,7 +1028,7 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
return MNL_CB_ERROR; return MNL_CB_ERROR;
strcpy(session->saved_setname, ipset_data_setname(data)); strcpy(session->saved_setname, ipset_data_setname(data));
} }
if (nla[IPSET_ATTR_ADT] != NULL) { if (nla[IPSET_ATTR_ADT] != NULL) {
struct nlattr *tb, *adt[IPSET_ATTR_ADT_MAX+1]; struct nlattr *tb, *adt[IPSET_ATTR_ADT_MAX+1];
@@ -1020,7 +1060,7 @@ static int
callback_version(struct ipset_session *session, struct nlattr *nla[]) callback_version(struct ipset_session *session, struct nlattr *nla[])
{ {
uint8_t min, max; uint8_t min, max;
min = max = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]); min = max = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]);
if (nla[IPSET_ATTR_PROTOCOL_MIN]) { if (nla[IPSET_ATTR_PROTOCOL_MIN]) {
@@ -1034,8 +1074,8 @@ callback_version(struct ipset_session *session, struct nlattr *nla[])
"while userspace supports protocol versions %u-%u", "while userspace supports protocol versions %u-%u",
min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX); min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX);
if (!(session->envopts & IPSET_ENV_QUIET) if (!(session->envopts & IPSET_ENV_QUIET) &&
&& max != IPSET_PROTOCOL_MAX) max != IPSET_PROTOCOL_MAX)
ipset_warn(session, ipset_warn(session,
"Kernel support protocol versions %u-%u " "Kernel support protocol versions %u-%u "
"while userspace supports protocol versions %u-%u", "while userspace supports protocol versions %u-%u",
@@ -1051,7 +1091,7 @@ callback_header(struct ipset_session *session, struct nlattr *nla[])
{ {
const char *setname; const char *setname;
const struct ipset_data *data = session->data; const struct ipset_data *data = session->data;
if (!nla[IPSET_ATTR_SETNAME]) if (!nla[IPSET_ATTR_SETNAME])
FAILURE("Broken HEADER kernel message: missing setname!"); FAILURE("Broken HEADER kernel message: missing setname!");
@@ -1060,10 +1100,10 @@ callback_header(struct ipset_session *session, struct nlattr *nla[])
FAILURE("Broken HEADER kernel message: sent setname `%s' " FAILURE("Broken HEADER kernel message: sent setname `%s' "
"does not match with received one `%s'!", "does not match with received one `%s'!",
ipset_data_setname(data), setname); ipset_data_setname(data), setname);
if (!(nla[IPSET_ATTR_TYPENAME] if (!(nla[IPSET_ATTR_TYPENAME] &&
&& nla[IPSET_ATTR_REVISION] nla[IPSET_ATTR_REVISION] &&
&& nla[IPSET_ATTR_FAMILY])) nla[IPSET_ATTR_FAMILY]))
FAILURE("Broken HEADER kernel message: " FAILURE("Broken HEADER kernel message: "
"missing attribute '%s'!", "missing attribute '%s'!",
!nla[IPSET_ATTR_TYPENAME] ? "typename" : !nla[IPSET_ATTR_TYPENAME] ? "typename" :
@@ -1083,10 +1123,10 @@ callback_type(struct ipset_session *session, struct nlattr *nla[])
{ {
const struct ipset_data *data = session->data; const struct ipset_data *data = session->data;
const char *typename, *orig; const char *typename, *orig;
if (!(nla[IPSET_ATTR_TYPENAME] if (!(nla[IPSET_ATTR_TYPENAME] &&
&& nla[IPSET_ATTR_REVISION] nla[IPSET_ATTR_REVISION] &&
&& nla[IPSET_ATTR_FAMILY])) nla[IPSET_ATTR_FAMILY]))
FAILURE("Broken TYPE kernel message: " FAILURE("Broken TYPE kernel message: "
"missing attribute '%s'!", "missing attribute '%s'!",
!nla[IPSET_ATTR_TYPENAME] ? "typename" : !nla[IPSET_ATTR_TYPENAME] ? "typename" :
@@ -1099,7 +1139,7 @@ callback_type(struct ipset_session *session, struct nlattr *nla[])
FAILURE("Broken TYPE kernel message: sent typename `%s' " FAILURE("Broken TYPE kernel message: sent typename `%s' "
"does not match with received one `%s'!", "does not match with received one `%s'!",
orig, typename); orig, typename);
ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs); ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs); ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs); ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
@@ -1123,7 +1163,7 @@ mnl_attr_parse_dbg(const struct nlmsghdr *nlh, int offset,
int ret = MNL_CB_OK; int ret = MNL_CB_OK;
struct nlattr *attr = mnl_nlmsg_get_payload_offset(nlh, offset); struct nlattr *attr = mnl_nlmsg_get_payload_offset(nlh, offset);
int len = nlh->nlmsg_len - MNL_NLMSG_HDRLEN - MNL_ALIGN(offset); int len = nlh->nlmsg_len - MNL_NLMSG_HDRLEN - MNL_ALIGN(offset);
while (mnl_attr_ok(attr, len)) { while (mnl_attr_ok(attr, len)) {
D("attr: type %u, attrlen %u, len %u", D("attr: type %u, attrlen %u, len %u",
mnl_attr_get_type(attr), attr->nla_len, len); mnl_attr_get_type(attr), attr->nla_len, len);
@@ -1142,7 +1182,7 @@ callback_data(const struct nlmsghdr *nlh, void *data)
struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {}; struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
uint8_t proto, cmd; uint8_t proto, cmd;
int ret = MNL_CB_OK, nfmsglen = MNL_ALIGN(sizeof(struct genlmsghdr)); int ret = MNL_CB_OK, nfmsglen = MNL_ALIGN(sizeof(struct genlmsghdr));
D("called, nlmsg_len %u", nlh->nlmsg_len); D("called, nlmsg_len %u", nlh->nlmsg_len);
cmd = ipset_get_nlmsg_type(nlh); cmd = ipset_get_nlmsg_type(nlh);
if (cmd == IPSET_CMD_LIST && session->cmd == IPSET_CMD_SAVE) if (cmd == IPSET_CMD_LIST && session->cmd == IPSET_CMD_SAVE)
@@ -1162,8 +1202,8 @@ callback_data(const struct nlmsghdr *nlh, void *data)
if (!nla[IPSET_ATTR_PROTOCOL]) if (!nla[IPSET_ATTR_PROTOCOL])
FAILURE("Sad, sad day: kernel message %s " FAILURE("Sad, sad day: kernel message %s "
"does not carry the protocol version.", "does not carry the protocol version.",
cmd2name[cmd]); cmd2name[cmd]);
proto = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]); proto = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]);
@@ -1208,8 +1248,8 @@ callback_done(const struct nlmsghdr *nlh UNUSED, void *data)
D(" called"); D(" called");
if (session->cmd == IPSET_CMD_LIST || session->cmd == IPSET_CMD_SAVE) if (session->cmd == IPSET_CMD_LIST || session->cmd == IPSET_CMD_SAVE)
return print_set_done(session); return print_set_done(session);
FAILURE("Invalid message received in non LIST or SAVE state."); FAILURE("Invalid message received in non LIST or SAVE state.");
} }
@@ -1221,7 +1261,7 @@ decode_errmsg(struct ipset_session *session, const struct nlmsghdr *nlh)
struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {}; struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
enum ipset_cmd cmd; enum ipset_cmd cmd;
int nfmsglen = MNL_ALIGN(sizeof(struct genlmsghdr)); int nfmsglen = MNL_ALIGN(sizeof(struct genlmsghdr));
if (nlh->nlmsg_len < (uint32_t) MNL_ALIGN(sizeof(struct nlmsgerr)) if (nlh->nlmsg_len < (uint32_t) MNL_ALIGN(sizeof(struct nlmsgerr))
|| nlh->nlmsg_len < MNL_ALIGN(sizeof(struct nlmsgerr)) || nlh->nlmsg_len < MNL_ALIGN(sizeof(struct nlmsgerr))
+ msg->nlmsg_len) + msg->nlmsg_len)
@@ -1244,15 +1284,15 @@ decode_errmsg(struct ipset_session *session, const struct nlmsghdr *nlh)
FAILURE("Broken %s error report message: " FAILURE("Broken %s error report message: "
"missing protocol attribute", "missing protocol attribute",
cmd2name[cmd]); cmd2name[cmd]);
if (nla[IPSET_ATTR_LINENO]) { if (nla[IPSET_ATTR_LINENO]) {
session->lineno = mnl_attr_get_u32(nla[IPSET_ATTR_LINENO]); session->lineno = mnl_attr_get_u32(nla[IPSET_ATTR_LINENO]);
if (nla[IPSET_ATTR_LINENO]->nla_type & NLA_F_NET_BYTEORDER) if (nla[IPSET_ATTR_LINENO]->nla_type & NLA_F_NET_BYTEORDER)
session->lineno = ntohl(session->lineno); session->lineno = ntohl(session->lineno);
} }
return ipset_errcode(session, cmd, -err->error); return ipset_errcode(session, cmd, -err->error);
} }
static int static int
callback_error(const struct nlmsghdr *nlh, void *cbdata) callback_error(const struct nlmsghdr *nlh, void *cbdata)
@@ -1285,16 +1325,20 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata)
break; break;
case IPSET_CMD_RENAME: case IPSET_CMD_RENAME:
ipset_cache_rename(ipset_data_setname(data), ipset_cache_rename(ipset_data_setname(data),
ipset_data_get(data, IPSET_OPT_SETNAME2)); ipset_data_get(data,
IPSET_OPT_SETNAME2));
break; break;
case IPSET_CMD_SWAP: case IPSET_CMD_SWAP:
ipset_cache_swap(ipset_data_setname(data), ipset_cache_swap(ipset_data_setname(data),
ipset_data_get(data, IPSET_OPT_SETNAME2)); ipset_data_get(data,
IPSET_OPT_SETNAME2));
break; break;
case IPSET_CMD_TEST: case IPSET_CMD_TEST:
if (!(session->envopts & IPSET_ENV_QUIET)) { if (!(session->envopts & IPSET_ENV_QUIET)) {
ipset_print_elem(session->report, IPSET_ERRORBUFLEN, ipset_print_elem(session->report,
session->data, IPSET_OPT_NONE, 0); IPSET_ERRORBUFLEN,
session->data,
IPSET_OPT_NONE, 0);
ipset_warn(session, " is in set %s.", ipset_warn(session, " is in set %s.",
ipset_data_setname(data)); ipset_data_setname(data));
} }
@@ -1308,7 +1352,8 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata)
print_set_done(session); print_set_done(session);
break; break;
default: 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 session->cmd < IPSET_MSG_MAX
? cmd2name[session->cmd] : "unknown", ? cmd2name[session->cmd] : "unknown",
session->cmd); session->cmd);
@@ -1318,10 +1363,10 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata)
D("nlmsgerr error: %u", -err->error); D("nlmsgerr error: %u", -err->error);
/* Error messages */ /* Error messages */
/* Special case for IPSET_CMD_TEST */ /* Special case for IPSET_CMD_TEST */
if (session->cmd == IPSET_CMD_TEST if (session->cmd == IPSET_CMD_TEST &&
&& err->error == -IPSET_ERR_EXIST) { err->error == -IPSET_ERR_EXIST) {
if (!(session->envopts & IPSET_ENV_QUIET)) { if (!(session->envopts & IPSET_ENV_QUIET)) {
ipset_print_elem(session->report, IPSET_ERRORBUFLEN, ipset_print_elem(session->report, IPSET_ERRORBUFLEN,
session->data, IPSET_OPT_NONE, 0); session->data, IPSET_OPT_NONE, 0);
@@ -1332,7 +1377,7 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata)
} }
decode_errmsg(session, nlh); decode_errmsg(session, nlh);
return ret; return ret;
} }
@@ -1386,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) \ #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 static int
rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh, rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
@@ -1406,14 +1452,15 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
: IPSET_ATTR_IPADDR_IPV6; : IPSET_ATTR_IPADDR_IPV6;
alen = attr_len(attr, family, &flags); 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; return 1;
nested = mnl_attr_nest_start(nlh, type); nested = mnl_attr_nest_start(nlh, type);
D("family: %s", family == AF_INET ? "INET" : D("family: %s", family == AF_INET ? "INET" :
family == AF_INET6 ? "INET6" : "UNSPEC"); family == AF_INET6 ? "INET6" : "UNSPEC");
mnl_attr_put(nlh, atype | flags, alen, d); mnl_attr_put(nlh, atype | flags, alen, d);
mnl_attr_nest_end(nlh, nested); mnl_attr_nest_end(nlh, nested);
return 0; return 0;
} }
@@ -1422,22 +1469,25 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
return 1; return 1;
switch (attr->type) { switch (attr->type) {
case MNL_TYPE_NUL_STRING:
alen = strlen((const char *)d) + 1;
break;
case MNL_TYPE_U32: { case MNL_TYPE_U32: {
uint32_t value = htonl(*(const uint32_t *)d); uint32_t value = htonl(*(const uint32_t *)d);
d = &value; d = &value;
break; break;
} }
case MNL_TYPE_U16: { case MNL_TYPE_U16: {
uint16_t value = htons(*(const uint16_t *)d); uint16_t value = htons(*(const uint16_t *)d);
d = &value; d = &value;
break; break;
} }
default: default:
break; break;
} }
mnl_attr_put(nlh, type | flags, alen, d); mnl_attr_put(nlh, type | flags, alen, d);
return 0; return 0;
@@ -1464,7 +1514,7 @@ data2attr(struct ipset_session *session, struct nlmsghdr *nlh,
data2attr(session, nlh, data, IPSET_ATTR_SETNAME, AF_INET, cmd_attrs) data2attr(session, nlh, data, IPSET_ATTR_SETNAME, AF_INET, cmd_attrs)
#define ADDATTR_IF(session, nlh, data, type, family, attrs) \ #define ADDATTR_IF(session, nlh, data, type, family, attrs) \
ipset_data_test(data, attrs[type].opt) ? \ ipset_data_test(data, attrs[type].opt) ? \
data2attr(session, nlh, data, type, family, attrs) : 0 data2attr(session, nlh, data, type, family, attrs) : 0
#define ADDATTR_RAW(session, nlh, data, type, attrs) \ #define ADDATTR_RAW(session, nlh, data, type, attrs) \
@@ -1485,7 +1535,7 @@ addattr_adt(struct ipset_session *session,
struct nlmsghdr *nlh, struct ipset_data *data, uint8_t family) struct nlmsghdr *nlh, struct ipset_data *data, uint8_t family)
{ {
int i; int i;
for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++) for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
if (ADDATTR_IF(session, nlh, data, i, family, adt_attrs)) if (ADDATTR_IF(session, nlh, data, i, family, adt_attrs))
return 1; return 1;
@@ -1497,7 +1547,7 @@ addattr_adt(struct ipset_session *session,
static int static int
build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd) 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 nlmsghdr *nlh = (void *)buffer;
struct ipset_data *data = session->data; struct ipset_data *data = session->data;
int len = PRIVATE_MSG_BUFLEN, ret; int len = PRIVATE_MSG_BUFLEN, ret;
@@ -1505,7 +1555,7 @@ build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
/* Initialize header */ /* Initialize header */
session->transport->fill_hdr(session->handle, cmd, buffer, len, 0); session->transport->fill_hdr(session->handle, cmd, buffer, len, 0);
ADDATTR_PROTOCOL(nlh); ADDATTR_PROTOCOL(nlh);
switch (cmd) { switch (cmd) {
@@ -1523,9 +1573,11 @@ build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
return ipset_err(session, return ipset_err(session,
"Invalid internal TYPE command: " "Invalid internal TYPE command: "
"missing settype"); "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)) 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 else
/* bitmap:port and list:set types */ /* bitmap:port and list:set types */
mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, AF_UNSPEC); mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, AF_UNSPEC);
@@ -1546,10 +1598,10 @@ build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
static inline bool static inline bool
may_aggregate_ad(struct ipset_session *session, enum ipset_cmd cmd) may_aggregate_ad(struct ipset_session *session, enum ipset_cmd cmd)
{ {
return session->lineno != 0 return session->lineno != 0 &&
&& (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL) (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL) &&
&& cmd == session->cmd cmd == session->cmd &&
&& STREQ(ipset_data_setname(session->data), session->saved_setname); STREQ(ipset_data_setname(session->data), session->saved_setname);
} }
static int static int
@@ -1582,17 +1634,23 @@ build_msg(struct ipset_session *session, bool aggregate)
if (!ipset_data_test(data, IPSET_OPT_TYPE)) if (!ipset_data_test(data, IPSET_OPT_TYPE))
return ipset_err(session, return ipset_err(session,
"Invalid create command: missing settype"); "Invalid create command: missing settype");
type = ipset_data_get(data, IPSET_OPT_TYPE); type = ipset_data_get(data, IPSET_OPT_TYPE);
/* Core attributes: /* Core attributes:
* setname, typename, revision, family, flags (optional) */ * setname, typename, revision, family, flags (optional) */
ADDATTR_SETNAME(session, nlh, data); 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, ADDATTR_RAW(session, nlh, &type->revision,
IPSET_ATTR_REVISION, cmd_attrs); IPSET_ATTR_REVISION, cmd_attrs);
D("family: %u, type family %u", D("family: %u, type family %u",
ipset_data_family(data), type->family); 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 */ /* Type-specific create attributes */
D("call open_nested"); D("call open_nested");
@@ -1604,23 +1662,41 @@ build_msg(struct ipset_session *session, bool aggregate)
} }
case IPSET_CMD_DESTROY: case IPSET_CMD_DESTROY:
case IPSET_CMD_FLUSH: case IPSET_CMD_FLUSH:
case IPSET_CMD_LIST:
case IPSET_CMD_SAVE: case IPSET_CMD_SAVE:
if (ipset_data_test(data, IPSET_SETNAME)) if (ipset_data_test(data, IPSET_SETNAME))
ADDATTR_SETNAME(session, nlh, data); ADDATTR_SETNAME(session, nlh, data);
break; 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_RENAME:
case IPSET_CMD_SWAP: case IPSET_CMD_SWAP:
if (!ipset_data_test(data, IPSET_SETNAME)) if (!ipset_data_test(data, IPSET_SETNAME))
return ipset_err(session, return ipset_err(session,
"Invalid %s command: missing from-setname", "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)) if (!ipset_data_test(data, IPSET_OPT_SETNAME2))
return ipset_err(session, return ipset_err(session,
"Invalid %s command: missing to-setname", "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_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); IPSET_ATTR_SETNAME2, cmd_attrs);
break; break;
case IPSET_CMD_ADD: case IPSET_CMD_ADD:
@@ -1632,12 +1708,14 @@ build_msg(struct ipset_session *session, bool aggregate)
if (!ipset_data_test(data, IPSET_SETNAME)) if (!ipset_data_test(data, IPSET_SETNAME))
return ipset_err(session, return ipset_err(session,
"Invalid %s command: missing setname", "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)) if (!ipset_data_test(data, IPSET_OPT_TYPE))
return ipset_err(session, return ipset_err(session,
"Invalid %s command: missing settype", "Invalid %s command: missing settype",
session->cmd == IPSET_CMD_ADD ? "add" : "del"); session->cmd == IPSET_CMD_ADD ? "add" :
"del");
/* Core options: setname */ /* Core options: setname */
ADDATTR_SETNAME(session, nlh, data); ADDATTR_SETNAME(session, nlh, data);
@@ -1655,11 +1733,12 @@ build_msg(struct ipset_session *session, bool aggregate)
D("open_nested failed"); D("open_nested failed");
return 1; return 1;
} }
if (addattr_adt(session, nlh, data, ipset_data_family(data)) if (addattr_adt(session, nlh, data, ipset_data_family(data)) ||
|| ADDATTR_RAW(session, nlh, &session->lineno, ADDATTR_RAW(session, nlh, &session->lineno,
IPSET_ATTR_LINENO, cmd_attrs)) { IPSET_ATTR_LINENO, cmd_attrs)) {
/* Cancel last, unfinished nested attribute */ /* 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; session->nested[--session->nestid] = NULL;
return 1; return 1;
} }
@@ -1671,7 +1750,7 @@ build_msg(struct ipset_session *session, bool aggregate)
/* Return codes are not aggregated, so tests cannot be either */ /* Return codes are not aggregated, so tests cannot be either */
/* Setname, type not checked/added yet */ /* Setname, type not checked/added yet */
if (!ipset_data_test(data, IPSET_SETNAME)) if (!ipset_data_test(data, IPSET_SETNAME))
return ipset_err(session, return ipset_err(session,
"Invalid test command: missing setname"); "Invalid test command: missing setname");
@@ -1679,7 +1758,7 @@ build_msg(struct ipset_session *session, bool aggregate)
if (!ipset_data_test(data, IPSET_OPT_TYPE)) if (!ipset_data_test(data, IPSET_OPT_TYPE))
return ipset_err(session, return ipset_err(session,
"Invalid test command: missing settype"); "Invalid test command: missing settype");
type = ipset_data_get(data, IPSET_OPT_TYPE); type = ipset_data_get(data, IPSET_OPT_TYPE);
D("family: %u, type family %u", D("family: %u, type family %u",
ipset_data_family(data), type->family); ipset_data_family(data), type->family);
@@ -1713,7 +1792,8 @@ ipset_commit(struct ipset_session *session)
assert(session); assert(session);
nlh = session->buffer; 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) if (nlh->nlmsg_len == 0)
/* Nothing to do */ /* Nothing to do */
return 0; return 0;
@@ -1729,6 +1809,7 @@ ipset_commit(struct ipset_session *session)
/* Reset saved data and nested state */ /* Reset saved data and nested state */
session->saved_setname[0] = '\0'; session->saved_setname[0] = '\0';
session->printed_set = 0;
for (i = session->nestid - 1; i >= 0; i--) for (i = session->nestid - 1; i >= 0; i--)
session->nested[i] = NULL; session->nested[i] = NULL;
session->nestid = 0; session->nestid = 0;
@@ -1741,7 +1822,7 @@ ipset_commit(struct ipset_session *session)
return -1; return -1;
else else
return ipset_err(session, return ipset_err(session,
"Internal protocol error"); "Internal protocol error");
} }
return 0; return 0;
} }
@@ -1753,7 +1834,7 @@ static mnl_cb_t cb_ctl[] = {
[NLMSG_OVERRUN] = callback_noop, [NLMSG_OVERRUN] = callback_noop,
[NLMSG_MIN_TYPE] = callback_data, [NLMSG_MIN_TYPE] = callback_data,
}; };
static inline struct ipset_handle * static inline struct ipset_handle *
init_transport(struct ipset_session *session) init_transport(struct ipset_session *session)
{ {
@@ -1782,7 +1863,7 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
struct ipset_data *data; struct ipset_data *data;
bool aggregate = false; bool aggregate = false;
int ret = -1; int ret = -1;
assert(session); assert(session);
if (cmd <= IPSET_CMD_NONE || cmd >= IPSET_MSG_MAX) if (cmd <= IPSET_CMD_NONE || cmd >= IPSET_MSG_MAX)
@@ -1802,9 +1883,9 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
} }
/* Private commands */ /* Private commands */
if (cmd == IPSET_CMD_TYPE || cmd == IPSET_CMD_HEADER) if (cmd == IPSET_CMD_TYPE || cmd == IPSET_CMD_HEADER)
return build_send_private_msg(session, cmd); return build_send_private_msg(session, cmd);
/* Check aggregatable commands */ /* Check aggregatable commands */
aggregate = may_aggregate_ad(session, cmd); aggregate = may_aggregate_ad(session, cmd);
if (!aggregate) { if (!aggregate) {
@@ -1817,7 +1898,7 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
/* Real command: update lineno too */ /* Real command: update lineno too */
session->cmd = cmd; session->cmd = cmd;
session->lineno = lineno; session->lineno = lineno;
/* Set default output mode */ /* Set default output mode */
if (cmd == IPSET_CMD_LIST) { if (cmd == IPSET_CMD_LIST) {
if (session->mode == IPSET_LIST_NONE) if (session->mode == IPSET_LIST_NONE)
@@ -1845,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 */ /* We have to save the type for error handling */
session->saved_type = ipset_data_get(data, IPSET_OPT_TYPE); session->saved_type = ipset_data_get(data, IPSET_OPT_TYPE);
if (session->lineno != 0 if (session->lineno != 0 &&
&& (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) { (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) {
/* Save setname for the next possible aggregated restore line */ /* Save setname for the next possible aggregated restore line */
strcpy(session->saved_setname, ipset_data_setname(data)); strcpy(session->saved_setname, ipset_data_setname(data));
ipset_data_reset(data); ipset_data_reset(data);
@@ -1855,7 +1936,7 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
goto cleanup; goto cleanup;
} }
D("call commit"); D("call commit");
ret = ipset_commit(session); ret = ipset_commit(session);
cleanup: cleanup:
@@ -1887,10 +1968,10 @@ ipset_session_init(ipset_outfn outfn)
/* The single transport method yet */ /* The single transport method yet */
session->transport = &ipset_mnl_transport; session->transport = &ipset_mnl_transport;
/* Output function */ /* Output function */
session->outfn = outfn; session->outfn = outfn;
/* Initialize data structures */ /* Initialize data structures */
session->data = ipset_data_init(); session->data = ipset_data_init();
if (session->data == NULL) if (session->data == NULL)
@@ -1900,8 +1981,8 @@ ipset_session_init(ipset_outfn outfn)
return session; return session;
free_session: free_session:
free(session); free(session);
return NULL; return NULL;
} }
/** /**
@@ -1926,3 +2007,7 @@ ipset_session_fini(struct ipset_session *session)
free(session); free(session);
return 0; return 0;
} }
#ifdef IPSET_DEBUG
#include "debug.c"
#endif

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <assert.h> /* assert */ #include <assert.h> /* assert */
@@ -28,8 +28,8 @@ struct ipset {
struct ipset *next; struct ipset *next;
}; };
static struct ipset_type *typelist = NULL; /* registered set types */ static struct ipset_type *typelist; /* registered set types */
static struct ipset *setlist = NULL; /* cached sets */ static struct ipset *setlist; /* cached sets */
/** /**
* ipset_cache_add - add a set to the cache * ipset_cache_add - add a set to the cache
@@ -57,7 +57,7 @@ ipset_cache_add(const char *name, const struct ipset_type *type,
ipset_strlcpy(n->name, name, IPSET_MAXNAMELEN); ipset_strlcpy(n->name, name, IPSET_MAXNAMELEN);
n->type = type; n->type = type;
n->family = family; n->family = family;
n->next = NULL; n->next = NULL;
if (setlist == NULL) { if (setlist == NULL) {
setlist = n; setlist = n;
@@ -109,7 +109,7 @@ ipset_cache_del(const char *name)
} }
if (match == NULL) if (match == NULL)
return -EEXIST; return -EEXIST;
free(match); free(match);
return 0; return 0;
} }
@@ -168,7 +168,7 @@ ipset_cache_swap(const char *from, const char *to)
ipset_strlcpy(b->name, from, IPSET_MAXNAMELEN); ipset_strlcpy(b->name, from, IPSET_MAXNAMELEN);
return 0; return 0;
} }
return -EEXIST; return -EEXIST;
} }
@@ -178,7 +178,7 @@ ipset_cache_swap(const char *from, const char *to)
bool bool
ipset_match_typename(const char *name, const struct ipset_type *type) 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)) if (STREQ(name, type->name))
return true; return true;
@@ -189,7 +189,7 @@ ipset_match_typename(const char *name, const struct ipset_type *type)
alias++; alias++;
} }
return false; return false;
} }
static inline const struct ipset_type * static inline const struct ipset_type *
create_type_get(struct ipset_session *session) create_type_get(struct ipset_session *session)
@@ -198,7 +198,7 @@ create_type_get(struct ipset_session *session)
struct ipset_data *data; struct ipset_data *data;
const char *typename; const char *typename;
uint8_t family, tmin = 0, tmax = 0; uint8_t family, tmin = 0, tmax = 0;
const uint8_t *kmin, *kmax; uint8_t kmin, kmax;
int ret; int ret;
data = ipset_session_data(session); data = ipset_session_data(session);
@@ -215,17 +215,17 @@ create_type_get(struct ipset_session *session)
if (ipset_match_typename(typename, t) if (ipset_match_typename(typename, t)
&& MATCH_FAMILY(t, family)) { && MATCH_FAMILY(t, family)) {
if (match == NULL) { if (match == NULL) {
match = t; match = t;
tmax = t->revision; tmin = tmax = t->revision;
} else if (t->family == match->family) } else if (t->family == match->family)
tmin = t->revision; tmin = t->revision;
} }
} }
if (!match) if (!match)
return ipset_errptr(session, return ipset_errptr(session,
"Syntax error: unknown settype %s", "Syntax error: unknown settype %s",
typename); typename);
/* Family is unspecified yet: set from matching set type */ /* Family is unspecified yet: set from matching set type */
if (family == AF_UNSPEC && match->family != AF_UNSPEC) { if (family == AF_UNSPEC && match->family != AF_UNSPEC) {
family = match->family == AF_INET46 ? AF_INET : match->family; family = match->family == AF_INET46 ? AF_INET : match->family;
@@ -240,38 +240,52 @@ create_type_get(struct ipset_session *session)
if (ret != 0) if (ret != 0)
return NULL; 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)) if (ipset_data_test(data, IPSET_OPT_REVISION_MIN))
kmin = ipset_data_get(data, IPSET_OPT_REVISION_MIN); kmin = *(const uint8_t *)ipset_data_get(data,
else IPSET_OPT_REVISION_MIN);
kmin = kmax;
if (MAX(tmin, *kmin) > MIN(tmax, *kmax)) { if (MAX(tmin, kmin) > MIN(tmax, kmax)) {
if (*kmin > tmax) if (kmin > tmax)
return ipset_errptr(session, return ipset_errptr(session,
"Kernel supports %s type with family %s " "Kernel supports %s type, family %s "
"in minimal revision %u while ipset library " "with minimal revision %u while ipset program "
"in maximal revision %u. " "with maximal revision %u.\n"
"You need to upgrade your ipset library.", "You need to upgrade your ipset program.",
typename, typename,
family == AF_INET ? "INET" : family == AF_INET ? "INET" :
family == AF_INET6 ? "INET6" : "UNSPEC", family == AF_INET6 ? "INET6" : "UNSPEC",
*kmin, tmax); kmin, tmax);
else else
return ipset_errptr(session, return ipset_errptr(session,
"Kernel supports %s type with family %s " "Kernel supports %s type, family %s "
"in maximal revision %u while ipset library " "with maximal revision %u while ipset program "
"in minimal revision %u. " "with minimal revision %u.\n"
"You need to upgrade your kernel.", "You need to upgrade your kernel.",
typename, typename,
family == AF_INET ? "INET" : family == AF_INET ? "INET" :
family == AF_INET6 ? "INET6" : "UNSPEC", 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; match->kernel_check = IPSET_KERNEL_OK;
found: found:
ipset_data_set(data, IPSET_OPT_TYPE, match); ipset_data_set(data, IPSET_OPT_TYPE, match);
return match; return match;
} }
@@ -315,7 +329,7 @@ adt_type_get(struct ipset_session *session)
return NULL; return NULL;
typename = ipset_data_get(data, IPSET_OPT_TYPENAME); typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
revision = ipset_data_get(data, IPSET_OPT_REVISION); revision = ipset_data_get(data, IPSET_OPT_REVISION);
family = ipset_data_family(data); family = ipset_data_family(data);
/* Check registered types */ /* Check registered types */
@@ -441,13 +455,15 @@ ipset_type_add(struct ipset_type *type)
assert(type); assert(type);
if (strlen(type->name) > IPSET_MAXNAMELEN - 1)
return -EINVAL;
/* Add to the list: higher revision numbers first */ /* Add to the list: higher revision numbers first */
for (t = typelist, prev = NULL; t != NULL; t = t->next) { for (t = typelist, prev = NULL; t != NULL; t = t->next) {
if (STREQ(t->name, type->name)) { if (STREQ(t->name, type->name)) {
if (t->revision == type->revision) { if (t->revision == type->revision)
errno = EEXIST; return -EEXIST;
return -1; else if (t->revision < type->revision) {
} else if (t->revision < type->revision) {
type->next = t; type->next = t;
if (prev) if (prev)
prev->next = type; 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 != NULL && STREQ(t->next->name, type->name)) {
if (t->next->revision == type->revision) { if (t->next->revision == type->revision)
errno = EEXIST; return -EEXIST;
return -1; else if (t->next->revision < type->revision) {
} else if (t->next->revision < type->revision) {
type->next = t->next; type->next = t->next;
t->next = type; t->next = type;
return 0; return 0;
@@ -529,7 +544,7 @@ void
ipset_cache_fini(void) ipset_cache_fini(void)
{ {
struct ipset *set; struct ipset *set;
while (setlist) { while (setlist) {
set = setlist; set = setlist;
setlist = setlist->next; setlist = setlist->next;

View File

@@ -148,7 +148,7 @@ const union nf_inet_addr ip_set_netmask_map[] = {
EXPORT_SYMBOL_GPL(ip_set_netmask_map); EXPORT_SYMBOL_GPL(ip_set_netmask_map);
#undef E #undef E
#define E(a, b, c, d) \ #define E(a, b, c, d) \
{.ip6 = { (__force __be32) a, (__force __be32) b, \ {.ip6 = { (__force __be32) a, (__force __be32) b, \
(__force __be32) c, (__force __be32) d, \ (__force __be32) c, (__force __be32) d, \
} } } }
@@ -289,3 +289,24 @@ const union nf_inet_addr ip_set_hostmask_map[] = {
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF),
}; };
EXPORT_SYMBOL_GPL(ip_set_hostmask_map); 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

@@ -2,7 +2,8 @@
#define _PFXLEN_H #define _PFXLEN_H
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <net/tcp.h>
/* Prefixlen maps, by Jan Engelhardt */ /* Prefixlen maps, by Jan Engelhardt */
extern const union nf_inet_addr ip_set_netmask_map[]; extern const union nf_inet_addr ip_set_netmask_map[];
@@ -32,4 +33,12 @@ ip_set_hostmask6(u8 pfxlen)
return &ip_set_hostmask_map[pfxlen].ip6[0]; 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 */ #endif /*_PFXLEN_H */

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <assert.h> /* assert */ #include <assert.h> /* assert */
@@ -21,8 +21,10 @@
/* Core kernel error codes */ /* Core kernel error codes */
static const struct ipset_errcode_table core_errcode_table[] = { static const struct ipset_errcode_table core_errcode_table[] = {
/* Generic error codes */ /* Generic error codes */
{ EEXIST, 0, { ENOENT, 0,
"The set with the given name does not exist" }, "The set with the given name does not exist" },
{ EMSGSIZE, 0,
"Kernel error received: message could not be created" },
{ IPSET_ERR_PROTOCOL, 0, { IPSET_ERR_PROTOCOL, 0,
"Kernel error received: ipset protocol error" }, "Kernel error received: ipset protocol error" },
@@ -30,14 +32,14 @@ static const struct ipset_errcode_table core_errcode_table[] = {
{ EEXIST, IPSET_CMD_CREATE, { EEXIST, IPSET_CMD_CREATE,
"Set cannot be created: set with the same name already exists" }, "Set cannot be created: set with the same name already exists" },
{ IPSET_ERR_FIND_TYPE, 0, { 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, { IPSET_ERR_MAX_SETS, 0,
"Kernel error received: maximal number of sets reached, " "Kernel error received: maximal number of sets reached, "
"cannot create more." }, "cannot create more." },
{ IPSET_ERR_INVALID_NETMASK, 0, { IPSET_ERR_INVALID_NETMASK, 0,
"The value of the netmask parameter is invalid" }, "The value of the netmask parameter is invalid" },
{ IPSET_ERR_INVALID_FAMILY, 0, { 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 */ /* DESTROY specific error codes */
{ IPSET_ERR_BUSY, IPSET_CMD_DESTROY, { IPSET_ERR_BUSY, IPSET_CMD_DESTROY,
@@ -68,7 +70,7 @@ static const struct ipset_errcode_table core_errcode_table[] = {
"An IPv4 address is expected, but not received" }, "An IPv4 address is expected, but not received" },
{ IPSET_ERR_IPADDR_IPV6, 0, { IPSET_ERR_IPADDR_IPV6, 0,
"An IPv6 address is expected, but not received" }, "An IPv6 address is expected, but not received" },
/* ADD specific error codes */ /* ADD specific error codes */
{ IPSET_ERR_EXIST, IPSET_CMD_ADD, { IPSET_ERR_EXIST, IPSET_CMD_ADD,
"Element cannot be added to the set: it's already added" }, "Element cannot be added to the set: it's already added" },
@@ -111,6 +113,10 @@ static const struct ipset_errcode_table hash_errcode_table[] = {
"Invalid protocol specified" }, "Invalid protocol specified" },
{ IPSET_ERR_MISSING_PROTO, 0, { IPSET_ERR_MISSING_PROTO, 0,
"Protocol missing, but must be specified" }, "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" },
{ }, { },
}; };
@@ -151,10 +157,10 @@ ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode)
{ {
const struct ipset_errcode_table *table = core_errcode_table; const struct ipset_errcode_table *table = core_errcode_table;
int i, generic; int i, generic;
if (errcode >= IPSET_ERR_TYPE_SPECIFIC) { if (errcode >= IPSET_ERR_TYPE_SPECIFIC) {
const struct ipset_type *type; const struct ipset_type *type;
type = ipset_saved_type(session); type = ipset_saved_type(session);
if (type) { if (type) {
if (MATCH_TYPENAME(type->name, "bitmap:")) if (MATCH_TYPENAME(type->name, "bitmap:"))
@@ -168,11 +174,11 @@ ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode)
retry: retry:
for (i = 0, generic = -1; table[i].errcode; i++) { for (i = 0, generic = -1; table[i].errcode; i++) {
if (table[i].errcode == errcode if (table[i].errcode == errcode &&
&& (table[i].cmd == cmd || table[i].cmd == 0)) { (table[i].cmd == cmd || table[i].cmd == 0)) {
if (table[i].cmd == 0) { if (table[i].cmd == 0) {
generic = i; generic = i;
continue; continue;
} }
return ipset_err(session, table[i].message); return ipset_err(session, table[i].message);
} }

View File

@@ -21,7 +21,7 @@ ipset \(em administration tool for IP sets
.PP .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 } 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 .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 .PP
\fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ] \fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ]
.PP .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. If the set has got reference(s), nothing is done and no set destroyed.
.TP .TP
\fBlist\fP [ \fISETNAME\fP ] \fBlist\fP [ \fISETNAME\fP ] [ \fIOPTIONS\fP ]
List the header data and the entries for the specified set, or for List the header data and the entries for the specified set, or for
all sets if none is given. The all sets if none is given. The
\fB\-resolve\fP \fB\-resolve\fP
@@ -120,8 +120,13 @@ type supports the operation). The option
\fB\-output\fR \fB\-output\fR
can be used to control the format of the listing: can be used to control the format of the listing:
\fBplain\fR, \fBsave\fR or \fBxml\fR. \fBplain\fR, \fBsave\fR or \fBxml\fR.
The default is (The default is
\fBplain\fR. \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 .TP
\fBsave\fP [ \fISETNAME\fP ] \fBsave\fP [ \fISETNAME\fP ]
Save the given set, or all sets if none is given Save the given set, or all sets if none is given
@@ -190,6 +195,13 @@ DNS lookups.
.TP .TP
\fB\-s\fP, \fB\-sorted\fP \fB\-s\fP, \fB\-sorted\fP
Sorted output. When listing sets entries are listed sorted. Not supported yet. 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" .SH "SET TYPES"
A set type comprises of the storage method by which the data is stored and 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 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 where the current list of the methods are
\fBbitmap\fR, \fBhash\fR, and \fBlist\fR and the possible data types \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 are \fBip\fR, \fBnet\fR, \fBmac\fR, \fBport\fR and \fBiface\fR.
is equal to the number of data types in its type name. 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 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 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 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 \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. 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 .SS bitmap:ip
The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host 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 (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, 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. the timer starts off when the IP and MAC address pair is complete.
.PP .PP
Please note, the \fBset\fR match and \fBSET\fR target netfilter kernel modules The \fBbitmap:ip,mac\fR type of sets require two \fBsrc/dst\fR parameters of
\fBalways\fR use the source MAC address from the packet to match, add or delete the \fBset\fR match and \fBSET\fR target netfilter kernel modules and the second
entries from a \fBbitmap:ip,mac\fR type of set. 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 .PP
Examples: Examples:
.IP .IP
@@ -330,6 +345,9 @@ Mandatory options to use when creating a \fBbitmap:port\fR type of set:
\fBrange\fP \fIfromport\fP\-\fItoport\fR \fBrange\fP \fIfromport\fP\-\fItoport\fR
Create the set from the specified inclusive port range. Create the set from the specified inclusive port range.
.PP .PP
The \fBset\fR match and \fBSET\fR target netfilter kernel modules interpret
the stored numbers as TCP or UDP port numbers.
.PP
Examples: Examples:
.IP .IP
ipset create foo bitmap:port range 0\-1024 ipset create foo bitmap:port range 0\-1024
@@ -380,9 +398,9 @@ a range or a network:
.PP .PP
Examples: Examples:
.IP .IP
ipset create foo hash:ip netmask 24 ipset create foo hash:ip netmask 30
.IP .IP
ipset add foo 192.168.1.1\-192.168.1.2 ipset add foo 192.168.1.0/24
.IP .IP
ipset test foo 192.168.1.2 ipset test foo 192.168.1.2
.SS hash:net .SS hash:net
@@ -391,13 +409,16 @@ Network address with zero prefix size cannot be stored in this type of sets.
.PP .PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP .PP
\fIADD\-ENTRY\fR := \fIip\fR[/\fIcidr\fR] \fIADD\-ENTRY\fR := \fInetaddr\fR
.PP .PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP .PP
\fIDEL\-ENTRY\fR := \fIip\fR[/\fIcidr\fR] \fIDEL\-ENTRY\fR := \fInetaddr\fR
.PP .PP
\fITEST\-ENTRY\fR := \fIip\fR[/\fIcidr\fR] \fITEST\-ENTRY\fR := \fInetaddr\fR
.PP
where
\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP .PP
Optional \fBcreate\fR options: Optional \fBcreate\fR options:
.TP .TP
@@ -413,9 +434,16 @@ correct value.
\fBmaxelem\fR \fIvalue\fR \fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536. The maximal number of elements which can be stored in the set, default 65536.
.PP .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, 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 then the host prefix value is assumed. When adding/deleting entries, the exact
elements are not checked. 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 .PP
From the \fBset\fR netfilter match point of view the searching for a match From the \fBset\fR netfilter match point of view the searching for a match
always starts from the smallest size of netblock (most specific always starts from the smallest size of netblock (most specific
@@ -431,7 +459,7 @@ Examples:
.IP .IP
ipset create foo hash:net ipset create foo hash:net
.IP .IP
ipset add foo 192.168.0/24 ipset add foo 192.168.0.0/24
.IP .IP
ipset add foo 10.1.0.0/16 ipset add foo 10.1.0.0/16
.IP .IP
@@ -481,8 +509,8 @@ TCP port or range of ports expressed in TCP portname identifiers from /etc/servi
\fIportnumber[\-portnumber]\fR \fIportnumber[\-portnumber]\fR
TCP port or range of ports expressed in TCP port numbers TCP port or range of ports expressed in TCP port numbers
.TP .TP
\fBtcp\fR|\fBudp\fR:\fIportname\fR|\fIportnumber\fR[\-\fIportname\fR|\fIportnumber\fR] \fBtcp\fR|\fBsctp\fR|\fBudp\fR|\fBudplite\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) TCP, SCTP, UDP or UDPLITE port or port range expressed in port name(s) or port number(s)
.TP .TP
\fBicmp\fR:\fIcodename\fR|\fItype\fR/\fIcode\fR \fBicmp\fR:\fIcodename\fR|\fItype\fR/\fIcode\fR
ICMP codename or type/code. The supported ICMP codename identifiers can always 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 .IP
ipset add foo 192.168.1.1,udp:53 ipset add foo 192.168.1.1,udp:53
.IP .IP
ipset add foo 192.168.1.1,ospf:0 ipset add foo 192.168.1.1,vrrp:0
.IP .IP
ipset test foo 192.168.1.1,80 ipset test foo 192.168.1.1,80
.SS hash:net,port .SS hash:net,port
@@ -519,13 +547,16 @@ address with zero prefix size is not accepted either.
.PP .PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP .PP
\fIADD\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR \fIADD\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP .PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP .PP
\fIDEL\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR \fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP .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 .PP
Optional \fBcreate\fR options: Optional \fBcreate\fR options:
.TP .TP
@@ -541,14 +572,17 @@ correct value.
\fBmaxelem\fR \fIvalue\fR \fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536. The maximal number of elements which can be stored in the set, default 65536.
.PP .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 [\fIproto\fR:]\fIport\fR
part of the elements see the description at the part of the elements see the description at the
\fBhash:ip,port\fR set type. \fBhash:ip,port\fR set type.
.PP .PP
When adding/deleting/testing entries, if the cidr prefix parameter is not specified, 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 then the host prefix value is assumed. When adding/deleting entries, the exact
elements are not checked. 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 .PP
From the \fBset\fR netfilter match point of view the searching for a match From the \fBset\fR netfilter match point of view the searching for a match
always starts from the smallest size of netblock (most specific always starts from the smallest size of netblock (most specific
@@ -623,18 +657,22 @@ address with zero prefix size cannot be stored either.
.PP .PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP .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 .PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP .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 .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 .PP
For the first \fIipaddr\fR and where
\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
For the \fIipaddr\fR and
[\fIproto\fR:]\fIport\fR [\fIproto\fR:]\fIport\fR
parts of the elements see the descriptions at the 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 .PP
Optional \fBcreate\fR options: Optional \fBcreate\fR options:
.TP .TP
@@ -673,6 +711,77 @@ ipset add foo 192.168.1,80,10.0.0/24
ipset add foo 192.168.2,25,10.1.0.0/16 ipset add foo 192.168.2,25,10.1.0.0/16
.IP .IP
ipset test foo 192.168.1,80.10.0.0/24 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
The internal restriction of the \fBhash:net,iface\fR set type is that
the same network prefix cannot be stored with more than 64 different interfaces
in a single 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 .SS list:set
The \fBlist:set\fR type uses a simple list in which you can store The \fBlist:set\fR type uses a simple list in which you can store
set names. set names.

View File

@@ -2,8 +2,8 @@
* Patrick Schaaf (bof@bof.de) * Patrick Schaaf (bof@bof.de)
* Copyright 2003-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) * Copyright 2003-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <ctype.h> /* isspace */ #include <ctype.h> /* isspace */
@@ -23,15 +23,15 @@
#include <libipset/ui.h> /* core options, commands */ #include <libipset/ui.h> /* core options, commands */
#include <libipset/utils.h> /* STREQ */ #include <libipset/utils.h> /* STREQ */
static char program_name[] = PACKAGE; static char program_name[] = "ipset";
static char program_version[] = PACKAGE_VERSION; static char program_version[] = "6.8-genl-xta";
static struct ipset_session *session = NULL; static struct ipset_session *session;
static uint32_t restore_line = 0; static uint32_t restore_line;
static bool interactive = false; static bool interactive;
static char cmdline[1024]; static char cmdline[1024];
static char *newargv[255]; static char *newargv[255];
static int newargc = 0; static int newargc;
/* The known set types: (typename, revision, family) is unique */ /* The known set types: (typename, revision, family) is unique */
extern struct ipset_type ipset_bitmap_ip0; 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_bitmap_port0;
extern struct ipset_type ipset_hash_ip0; extern struct ipset_type ipset_hash_ip0;
extern struct ipset_type ipset_hash_net0; extern struct ipset_type ipset_hash_net0;
extern struct ipset_type ipset_hash_netport0; extern struct ipset_type ipset_hash_net1;
extern struct ipset_type ipset_hash_ipport0; extern struct ipset_type ipset_hash_netport1;
extern struct ipset_type ipset_hash_ipportip0; extern struct ipset_type ipset_hash_netport2;
extern struct ipset_type ipset_hash_ipportnet0; 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; extern struct ipset_type ipset_list_set0;
enum exittype { enum exittype {
@@ -53,12 +57,12 @@ enum exittype {
SESSION_PROBLEM, SESSION_PROBLEM,
}; };
static int __attribute__((format(printf,2,3))) static int __attribute__((format(printf, 2, 3)))
exit_error(int status, const char *msg, ...) exit_error(int status, const char *msg, ...)
{ {
bool quiet = !interactive bool quiet = !interactive &&
&& session session &&
&& ipset_envopt_test(session, IPSET_ENV_QUIET); ipset_envopt_test(session, IPSET_ENV_QUIET);
if (status && msg && !quiet) { if (status && msg && !quiet) {
va_list args; va_list args;
@@ -94,8 +98,8 @@ exit_error(int status, const char *msg, ...)
static int static int
handle_error(void) handle_error(void)
{ {
if (ipset_session_warning(session) if (ipset_session_warning(session) &&
&& !ipset_envopt_test(session, IPSET_ENV_QUIET)) !ipset_envopt_test(session, IPSET_ENV_QUIET))
fprintf(stderr, "Warning: %s\n", fprintf(stderr, "Warning: %s\n",
ipset_session_warning(session)); ipset_session_warning(session));
if (ipset_session_error(session)) if (ipset_session_error(session))
@@ -107,7 +111,7 @@ handle_error(void)
exit(OTHER_PROBLEM); exit(OTHER_PROBLEM);
} }
ipset_session_report_reset(session); ipset_session_report_reset(session);
return -1; return -1;
} }
@@ -116,16 +120,15 @@ help(void)
{ {
const struct ipset_commands *c; const struct ipset_commands *c;
const struct ipset_envopts *opt = ipset_envopts; const struct ipset_envopts *opt = ipset_envopts;
printf("%s v%s\n\n" printf("%s v%s\n\n"
"Usage: %s [options] COMMAND\n\nCommands:\n", "Usage: %s [options] COMMAND\n\nCommands:\n",
program_name, program_version, program_name); 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("%s %s\n", c->name[0], c->help);
}
printf("\nOptions:\n"); printf("\nOptions:\n");
while (opt->flag) { while (opt->flag) {
if (opt->help) if (opt->help)
printf("%s %s\n", opt->name[0], opt->help); printf("%s %s\n", opt->name[0], opt->help);
@@ -140,14 +143,14 @@ build_argv(char *buffer)
char *ptr; char *ptr;
int i; int i;
/* Reset */ /* Reset */
for (i = 1; i < newargc; i++) for (i = 1; i < newargc; i++)
newargv[i] = NULL; newargv[i] = NULL;
newargc = 1; newargc = 1;
ptr = strtok(buffer, " \t\n"); ptr = strtok(buffer, " \t\r\n");
newargv[newargc++] = ptr; 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 *))) if ((newargc + 1) < (int)(sizeof(newargv)/sizeof(char *)))
newargv[newargc++] = ptr; newargv[newargc++] = ptr;
else { else {
@@ -169,7 +172,7 @@ restore(char *argv0)
{ {
int ret = 0; int ret = 0;
char *c; char *c;
/* Initialize newargv/newargc */ /* Initialize newargv/newargc */
newargc = 0; newargc = 0;
newargv[newargc++] = argv0; newargv[newargc++] = argv0;
@@ -181,7 +184,7 @@ restore(char *argv0)
c++; c++;
if (c[0] == '\0' || c[0] == '#') if (c[0] == '\0' || c[0] == '#')
continue; continue;
else if (strcmp(c, "COMMIT\n") == 0) { else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) {
ret = ipset_commit(session); ret = ipset_commit(session);
if (ret < 0) if (ret < 0)
handle_error(); handle_error();
@@ -189,7 +192,7 @@ restore(char *argv0)
} }
/* Build faked argv, argc */ /* Build faked argv, argc */
build_argv(c); build_argv(c);
/* Execute line */ /* Execute line */
ret = parse_commandline(newargc, newargv); ret = parse_commandline(newargc, newargv);
if (ret < 0) if (ret < 0)
@@ -206,68 +209,61 @@ restore(char *argv0)
static int static int
call_parser(int *argc, char *argv[], const struct ipset_arg *args) 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 struct ipset_arg *arg;
const char *optstr; const char *optstr;
/* Currently CREATE and ADT may have got additional arguments */ /* Currently CREATE and ADT may have got additional arguments */
if (!args) if (!args && *argc > 1)
goto done; goto err_unknown;
for (arg = args; arg->opt; arg++) { while (*argc > 1) {
for (i = 1; i < *argc; ) { for (arg = args; arg->opt; arg++) {
D("argc: %u, i: %u: %s vs %s", D("argc: %u, %s vs %s", *argc, argv[1], arg->name[0]);
*argc, i, argv[i], arg->name[0]); if (!(ipset_match_option(argv[1], arg->name)))
if (!(ipset_match_option(argv[i], arg->name))) {
i++;
continue; continue;
}
optstr = argv[i]; optstr = argv[1];
/* Shift off matched option */ /* Shift off matched option */
D("match %s", arg->name[0]); D("match %s", arg->name[0]);
ipset_shift_argv(argc, argv, i); ipset_shift_argv(argc, argv, 1);
D("argc: %u, i: %u", *argc, i);
switch (arg->has_arg) { switch (arg->has_arg) {
case IPSET_MANDATORY_ARG: case IPSET_MANDATORY_ARG:
if (i + 1 > *argc) if (*argc < 2)
return exit_error(PARAMETER_PROBLEM, return exit_error(PARAMETER_PROBLEM,
"Missing mandatory argument " "Missing mandatory argument "
"of option `%s'", "of option `%s'",
arg->name[0]); arg->name[0]);
/* Fall through */ /* Fall through */
case IPSET_OPTIONAL_ARG: case IPSET_OPTIONAL_ARG:
if (i + 1 <= *argc) { if (*argc >= 2) {
ret = ipset_call_parser(session, ret = ipset_call_parser(session,
arg->parse, arg, argv[1]);
optstr, arg->opt,
argv[i]);
if (ret < 0) if (ret < 0)
return ret; return ret;
ipset_shift_argv(argc, argv, i); ipset_shift_argv(argc, argv, 1);
break; break;
} }
/* Fall through */ /* Fall through */
default: default:
ret = ipset_call_parser(session, ret = ipset_call_parser(session, arg, optstr);
arg->parse,
optstr, arg->opt,
optstr);
if (ret < 0) if (ret < 0)
return ret; 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; return ret;
err_unknown:
return exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'", argv[1]);
} }
static enum ipset_adt static enum ipset_adt
cmd2cmd(int cmd) cmd2cmd(int cmd)
{ {
switch(cmd) { switch (cmd) {
case IPSET_CMD_ADD: case IPSET_CMD_ADD:
return IPSET_ADD; return IPSET_ADD;
case IPSET_CMD_DEL: case IPSET_CMD_DEL:
@@ -347,15 +343,15 @@ check_allowed(const struct ipset_type *type, enum ipset_cmd command)
? IPSET_CREATE_FLAGS : IPSET_ADT_FLAGS; ? IPSET_CREATE_FLAGS : IPSET_ADT_FLAGS;
const struct ipset_arg *arg = type->args[cmd]; const struct ipset_arg *arg = type->args[cmd];
enum ipset_opt i; enum ipset_opt i;
/* Range can be expressed by ip/cidr or from-to */ /* Range can be expressed by ip/cidr or from-to */
if (allowed & IPSET_FLAG(IPSET_OPT_IP_TO)) if (allowed & IPSET_FLAG(IPSET_OPT_IP_TO))
allowed |= IPSET_FLAG(IPSET_OPT_CIDR); allowed |= IPSET_FLAG(IPSET_OPT_CIDR);
for (i = IPSET_OPT_IP; i < IPSET_OPT_FLAGS; i++) { for (i = IPSET_OPT_IP; i < IPSET_OPT_FLAGS; i++) {
if (!(cmdflags & IPSET_FLAG(i)) if (!(cmdflags & IPSET_FLAG(i)) ||
|| (allowed & IPSET_FLAG(i)) (allowed & IPSET_FLAG(i)) ||
|| !(flags & IPSET_FLAG(i))) !(flags & IPSET_FLAG(i)))
continue; continue;
/* Not allowed element-expressions */ /* Not allowed element-expressions */
switch (i) { switch (i) {
@@ -363,19 +359,22 @@ check_allowed(const struct ipset_type *type, enum ipset_cmd command)
exit_error(OTHER_PROBLEM, exit_error(OTHER_PROBLEM,
"IP/CIDR range is not allowed in command %s " "IP/CIDR range is not allowed in command %s "
"with set type %s and family %s", "with set type %s and family %s",
cmd2name(command), type->name, session_family()); cmd2name(command), type->name,
session_family());
return; return;
case IPSET_OPT_IP_TO: case IPSET_OPT_IP_TO:
exit_error(OTHER_PROBLEM, exit_error(OTHER_PROBLEM,
"FROM-TO IP range is not allowed in command %s " "FROM-TO IP range is not allowed in command %s "
"with set type %s and family %s", "with set type %s and family %s",
cmd2name(command), type->name, session_family()); cmd2name(command), type->name,
session_family());
return; return;
case IPSET_OPT_PORT_TO: case IPSET_OPT_PORT_TO:
exit_error(OTHER_PROBLEM, exit_error(OTHER_PROBLEM,
"FROM-TO port range is not allowed in command %s " "FROM-TO port range is not allowed in command %s "
"with set type %s and family %s", "with set type %s and family %s",
cmd2name(command), type->name, session_family()); cmd2name(command), type->name,
session_family());
return; return;
default: default:
break; break;
@@ -395,7 +394,8 @@ check_allowed(const struct ipset_type *type, enum ipset_cmd command)
"%s parameter is not allowed in command %s " "%s parameter is not allowed in command %s "
"with set type %s and family %s", "with set type %s and family %s",
arg->name[0], arg->name[0],
cmd2name(command), type->name, session_family()); cmd2name(command), type->name,
session_family());
return; return;
} }
exit_error(OTHER_PROBLEM, exit_error(OTHER_PROBLEM,
@@ -410,7 +410,7 @@ static const struct ipset_type *
type_find(const char *name) type_find(const char *name)
{ {
const struct ipset_type *t = ipset_types(); const struct ipset_type *t = ipset_types();
while (t) { while (t) {
if (ipset_match_typename(name, t)) if (ipset_match_typename(name, t))
return t; return t;
@@ -476,61 +476,57 @@ parse_commandline(int argc, char *argv[])
/* Second: parse command */ /* Second: parse command */
for (command = ipset_commands; for (command = ipset_commands;
command->cmd && cmd == IPSET_CMD_NONE; argc > 1 && command->cmd && cmd == IPSET_CMD_NONE;
command++) { command++) {
for (i = 1; i < argc; ) { if (!ipset_match_cmd(argv[1], command->name))
if (!ipset_match_cmd(argv[1], command->name)) { continue;
i++;
continue;
}
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) {
printf("Restore command ignored "
"in interactive mode\n");
return 0;
}
/* Shift off matched command arg */ if (restore_line != 0 &&
ipset_shift_argv(&argc, argv, i); (command->cmd == IPSET_CMD_RESTORE ||
cmd = command->cmd; command->cmd == IPSET_CMD_VERSION ||
switch (command->has_arg) { command->cmd == IPSET_CMD_HELP))
case IPSET_MANDATORY_ARG: return exit_error(PARAMETER_PROBLEM,
case IPSET_MANDATORY_ARG2: "Command `%s' is invalid "
if (i + 1 > argc) "in restore mode.",
return exit_error(PARAMETER_PROBLEM, command->name[0]);
"Missing mandatory argument " if (interactive && command->cmd == IPSET_CMD_RESTORE) {
"to command %s", printf("Restore command ignored "
command->name[0]); "in interactive mode\n");
/* Fall through */ return 0;
case IPSET_OPTIONAL_ARG: }
arg0 = argv[i];
if (i + 1 <= argc) /* Shift off matched command arg */
/* Shift off first arg */ ipset_shift_argv(&argc, argv, 1);
ipset_shift_argv(&argc, argv, i); cmd = command->cmd;
break; switch (command->has_arg) {
default: case IPSET_MANDATORY_ARG:
break; case IPSET_MANDATORY_ARG2:
} if (argc < 2)
if (command->has_arg == IPSET_MANDATORY_ARG2) { return exit_error(PARAMETER_PROBLEM,
if (i + 1 > argc) "Missing mandatory argument "
return exit_error(PARAMETER_PROBLEM, "to command %s",
"Missing second mandatory " command->name[0]);
"argument to command %s", /* Fall through */
command->name[0]); case IPSET_OPTIONAL_ARG:
arg1 = argv[i]; arg0 = argv[1];
/* Shift off second arg */ if (argc >= 2)
ipset_shift_argv(&argc, argv, i); /* Shift off first arg */
} ipset_shift_argv(&argc, argv, 1);
break;
default:
break; break;
} }
if (command->has_arg == IPSET_MANDATORY_ARG2) {
if (argc < 2)
return exit_error(PARAMETER_PROBLEM,
"Missing second mandatory "
"argument to command %s",
command->name[0]);
arg1 = argv[1];
/* Shift off second arg */
ipset_shift_argv(&argc, argv, 1);
}
break;
} }
/* Third: catch interactive mode, handle help, version */ /* Third: catch interactive mode, handle help, version */
@@ -565,16 +561,17 @@ parse_commandline(int argc, char *argv[])
argv[1]); argv[1]);
return exit_error(PARAMETER_PROBLEM, "No command specified."); return exit_error(PARAMETER_PROBLEM, "No command specified.");
case IPSET_CMD_VERSION: 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) if (interactive)
return 0; return 0;
return exit_error(NO_PROBLEM, NULL); return exit_error(NO_PROBLEM, NULL);
case IPSET_CMD_HELP: case IPSET_CMD_HELP:
help(); help();
if (interactive if (interactive ||
|| !ipset_envopt_test(session, IPSET_ENV_QUIET)) { !ipset_envopt_test(session, IPSET_ENV_QUIET)) {
if (arg0) { if (arg0) {
/* Type-specific help, without kernel checking */ /* Type-specific help, without kernel checking */
type = type_find(arg0); type = type_find(arg0);
if (!type) if (!type)
@@ -604,7 +601,7 @@ parse_commandline(int argc, char *argv[])
printf(" %s\n", type->name); printf(" %s\n", type->name);
type = type->next; type = type->next;
} }
} }
} }
if (interactive) if (interactive)
return 0; return 0;
@@ -614,7 +611,7 @@ parse_commandline(int argc, char *argv[])
default: default:
break; break;
} }
/* Forth: parse command args and issue the command */ /* Forth: parse command args and issue the command */
switch (cmd) { switch (cmd) {
case IPSET_CMD_CREATE: case IPSET_CMD_CREATE:
@@ -637,11 +634,11 @@ parse_commandline(int argc, char *argv[])
return handle_error(); return handle_error();
else if (ret) else if (ret)
return ret; return ret;
/* Check mandatory, then allowed options */ /* Check mandatory, then allowed options */
check_mandatory(type, cmd); check_mandatory(type, cmd);
check_allowed(type, cmd); check_allowed(type, cmd);
break; break;
case IPSET_CMD_DESTROY: case IPSET_CMD_DESTROY:
case IPSET_CMD_FLUSH: case IPSET_CMD_FLUSH:
@@ -685,22 +682,22 @@ parse_commandline(int argc, char *argv[])
type = ipset_type_get(session, cmd); type = ipset_type_get(session, cmd);
if (type == NULL) if (type == NULL)
return handle_error(); return handle_error();
ret = ipset_parse_elem(session, type->last_elem_optional, arg1); ret = ipset_parse_elem(session, type->last_elem_optional, arg1);
if (ret < 0) if (ret < 0)
return handle_error(); return handle_error();
/* Parse additional ADT options */ /* Parse additional ADT options */
ret = call_parser(&argc, argv, type->args[cmd2cmd(cmd)]); ret = call_parser(&argc, argv, type->args[cmd2cmd(cmd)]);
if (ret < 0) if (ret < 0)
return handle_error(); return handle_error();
else if (ret) else if (ret)
return ret; return ret;
/* Check mandatory, then allowed options */ /* Check mandatory, then allowed options */
check_mandatory(type, cmd); check_mandatory(type, cmd);
check_allowed(type, cmd); check_allowed(type, cmd);
break; break;
default: default:
break; break;
@@ -726,16 +723,22 @@ parse_commandline(int argc, char *argv[])
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int ret;
/* Register types */ /* Register types */
ipset_type_add(&ipset_bitmap_ip0); ipset_type_add(&ipset_bitmap_ip0);
ipset_type_add(&ipset_bitmap_ipmac0); ipset_type_add(&ipset_bitmap_ipmac0);
ipset_type_add(&ipset_bitmap_port0); ipset_type_add(&ipset_bitmap_port0);
ipset_type_add(&ipset_hash_ip0); ipset_type_add(&ipset_hash_ip0);
ipset_type_add(&ipset_hash_net0); ipset_type_add(&ipset_hash_net0);
ipset_type_add(&ipset_hash_netport0); ipset_type_add(&ipset_hash_net1);
ipset_type_add(&ipset_hash_ipport0); ipset_type_add(&ipset_hash_netport1);
ipset_type_add(&ipset_hash_ipportip0); ipset_type_add(&ipset_hash_netport2);
ipset_type_add(&ipset_hash_ipportnet0); 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); ipset_type_add(&ipset_list_set0);
/* Initialize session */ /* Initialize session */
@@ -744,5 +747,9 @@ main(int argc, char *argv[])
return exit_error(OTHER_PROBLEM, return exit_error(OTHER_PROBLEM,
"Cannot initialize ipset session, aborting."); "Cannot initialize ipset session, aborting.");
return parse_commandline(argc, argv); ret = parse_commandline(argc, argv);
ipset_session_fini(session);
return ret;
} }

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -37,7 +37,7 @@ static const struct ipset_arg bitmap_ip_create_args[] = {
.parse = ipset_parse_net, .parse = ipset_parse_net,
}, },
{ }, { },
}; };
static const struct ipset_arg bitmap_ip_add_args[] = { static const struct ipset_arg bitmap_ip_add_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
@@ -45,7 +45,7 @@ static const struct ipset_arg bitmap_ip_add_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char bitmap_ip_usage[] = static const char bitmap_ip_usage[] =
"create SETNAME bitmap:ip range IP/CIDR|FROM-TO\n" "create SETNAME bitmap:ip range IP/CIDR|FROM-TO\n"
@@ -62,8 +62,8 @@ struct ipset_type ipset_bitmap_ip0 = {
.revision = 0, .revision = 0,
.family = AF_INET, .family = AF_INET,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_ip, .parse = ipset_parse_ip,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP .opt = IPSET_OPT_IP

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -33,7 +33,7 @@ static const struct ipset_arg bitmap_ipmac_create_args[] = {
.parse = ipset_parse_net, .parse = ipset_parse_net,
}, },
{ }, { },
}; };
static const struct ipset_arg bitmap_ipmac_add_args[] = { static const struct ipset_arg bitmap_ipmac_add_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
@@ -41,7 +41,7 @@ static const struct ipset_arg bitmap_ipmac_add_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char bitmap_ipmac_usage[] = static const char bitmap_ipmac_usage[] =
"create SETNAME bitmap:ip,mac range IP/CIDR|FROM-TO\n" "create SETNAME bitmap:ip,mac range IP/CIDR|FROM-TO\n"
@@ -60,13 +60,13 @@ struct ipset_type ipset_bitmap_ipmac0 = {
.family = AF_INET, .family = AF_INET,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.last_elem_optional = true, .last_elem_optional = true,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_single_ip, .parse = ipset_parse_single_ip,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP .opt = IPSET_OPT_IP
}, },
[IPSET_DIM_TWO] = { [IPSET_DIM_TWO] = {
.parse = ipset_parse_ether, .parse = ipset_parse_ether,
.print = ipset_print_ether, .print = ipset_print_ether,
.opt = IPSET_OPT_ETHER .opt = IPSET_OPT_ETHER

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -29,7 +29,7 @@ static const struct ipset_arg bitmap_port_create_args[] = {
.parse = ipset_parse_single_tcp_port, .parse = ipset_parse_single_tcp_port,
}, },
{ }, { },
}; };
static const struct ipset_arg bitmap_port_add_args[] = { static const struct ipset_arg bitmap_port_add_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
@@ -37,7 +37,7 @@ static const struct ipset_arg bitmap_port_add_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char bitmap_port_usage[] = static const char bitmap_port_usage[] =
"create SETNAME bitmap:port range FROM-TO\n" "create SETNAME bitmap:port range FROM-TO\n"
@@ -53,8 +53,8 @@ struct ipset_type ipset_bitmap_port0 = {
.revision = 0, .revision = 0,
.family = AF_UNSPEC, .family = AF_UNSPEC,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_tcp_port, .parse = ipset_parse_tcp_port,
.print = ipset_print_port, .print = ipset_print_port,
.opt = IPSET_OPT_PORT .opt = IPSET_OPT_PORT

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -63,7 +63,7 @@ static const struct ipset_arg hash_ip_add_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char hash_ip_usage[] = static const char hash_ip_usage[] =
"create SETNAME hash:ip\n" "create SETNAME hash:ip\n"
@@ -81,18 +81,17 @@ static const char hash_ip_usage[] =
struct ipset_type ipset_hash_ip0 = { struct ipset_type ipset_hash_ip0 = {
.name = "hash:ip", .name = "hash:ip",
.alias = { "iphash", "iptree", "iptreemap", NULL }, .alias = { "iphash", NULL },
.revision = 0, .revision = 0,
.family = AF_INET46, .family = AF_INET46,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_ip4_single6, .parse = ipset_parse_ip4_single6,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP .opt = IPSET_OPT_IP
}, },
}, },
.compat_parse_elem = ipset_parse_iptimeout,
.args = { .args = {
[IPSET_CREATE] = hash_ip_create_args, [IPSET_CREATE] = hash_ip_create_args,
[IPSET_ADD] = hash_ip_add_args, [IPSET_ADD] = hash_ip_add_args,

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -60,7 +60,7 @@ static const struct ipset_arg hash_ipport_create_args[] = {
.parse = ipset_parse_ignored, .parse = ipset_parse_ignored,
}, },
{ }, { },
}; };
static const struct ipset_arg hash_ipport_add_args[] = { static const struct ipset_arg hash_ipport_add_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
@@ -68,9 +68,9 @@ static const struct ipset_arg hash_ipport_add_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char hash_ipport_usage[] = static const char hash_ipport1_usage[] =
"create SETNAME hash:ip,port\n" "create SETNAME hash:ip,port\n"
" [family inet|inet6]\n" " [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n" " [hashsize VALUE] [maxelem VALUE]\n"
@@ -82,22 +82,22 @@ static const char hash_ipport_usage[] =
" IP is a valid IPv4 or IPv6 address (or hostname).\n" " IP is a valid IPv4 or IPv6 address (or hostname).\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n" " Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" is supported for IPv4.\n" " is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n" " Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" is supported both for IPv4 and IPv6.\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", .name = "hash:ip,port",
.alias = { "ipporthash", NULL }, .alias = { "ipporthash", NULL },
.revision = 0, .revision = 1,
.family = AF_INET46, .family = AF_INET46,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_ip4_single6, .parse = ipset_parse_ip4_single6,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP .opt = IPSET_OPT_IP
}, },
[IPSET_DIM_TWO] = { [IPSET_DIM_TWO] = {
.parse = ipset_parse_proto_port, .parse = ipset_parse_proto_port,
.print = ipset_print_proto_port, .print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT .opt = IPSET_OPT_PORT
@@ -139,6 +139,6 @@ struct ipset_type ipset_hash_ipport0 = {
| IPSET_FLAG(IPSET_OPT_PROTO), | IPSET_FLAG(IPSET_OPT_PROTO),
}, },
.usage = hash_ipport_usage, .usage = hash_ipport1_usage,
.usagefn = ipset_port_usage, .usagefn = ipset_port_usage,
}; };

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -60,7 +60,7 @@ static const struct ipset_arg hash_ipportip_create_args[] = {
.parse = ipset_parse_ignored, .parse = ipset_parse_ignored,
}, },
{ }, { },
}; };
static const struct ipset_arg hash_ipportip_add_args[] = { static const struct ipset_arg hash_ipportip_add_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
@@ -68,9 +68,9 @@ static const struct ipset_arg hash_ipportip_add_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char hash_ipportip_usage[] = static const char hash_ipportip1_usage[] =
"create SETNAME hash:ip,port,ip\n" "create SETNAME hash:ip,port,ip\n"
" [family inet|inet6]\n" " [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n" " [hashsize VALUE] [maxelem VALUE]\n"
@@ -82,27 +82,27 @@ static const char hash_ipportip_usage[] =
" IP is a valid IPv4 or IPv6 address (or hostname).\n" " IP is a valid IPv4 or IPv6 address (or hostname).\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n" " Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" in the first IP component is supported for IPv4.\n" " in the first IP component is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n" " Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" is supported both for IPv4 and IPv6.\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", .name = "hash:ip,port,ip",
.alias = { "ipportiphash", NULL }, .alias = { "ipportiphash", NULL },
.revision = 0, .revision = 1,
.family = AF_INET46, .family = AF_INET46,
.dimension = IPSET_DIM_THREE, .dimension = IPSET_DIM_THREE,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_ip4_single6, .parse = ipset_parse_ip4_single6,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP .opt = IPSET_OPT_IP
}, },
[IPSET_DIM_TWO] = { [IPSET_DIM_TWO] = {
.parse = ipset_parse_proto_port, .parse = ipset_parse_proto_port,
.print = ipset_print_proto_port, .print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT .opt = IPSET_OPT_PORT
}, },
[IPSET_DIM_THREE] = { [IPSET_DIM_THREE] = {
.parse = ipset_parse_single_ip, .parse = ipset_parse_single_ip,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP2 .opt = IPSET_OPT_IP2
@@ -150,6 +150,6 @@ struct ipset_type ipset_hash_ipportip0 = {
| IPSET_FLAG(IPSET_OPT_IP2), | IPSET_FLAG(IPSET_OPT_IP2),
}, },
.usage = hash_ipportip_usage, .usage = hash_ipportip1_usage,
.usagefn = ipset_port_usage, .usagefn = ipset_port_usage,
}; };

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -60,7 +60,7 @@ static const struct ipset_arg hash_ipportnet_create_args[] = {
.parse = ipset_parse_ignored, .parse = ipset_parse_ignored,
}, },
{ }, { },
}; };
static const struct ipset_arg hash_ipportnet_add_args[] = { static const struct ipset_arg hash_ipportnet_add_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
@@ -68,9 +68,9 @@ static const struct ipset_arg hash_ipportnet_add_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char hash_ipportnet_usage[] = static const char hash_ipportnet1_usage[] =
"create SETNAME hash:ip,port,net\n" "create SETNAME hash:ip,port,net\n"
" [family inet|inet6]\n" " [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n" " [hashsize VALUE] [maxelem VALUE]\n"
@@ -83,27 +83,27 @@ static const char hash_ipportnet_usage[] =
" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n" " CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n" " Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" in the first IP component is supported for IPv4.\n" " in the first IP component is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n" " Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" is supported both for IPv4 and IPv6.\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", .name = "hash:ip,port,net",
.alias = { "ipportnethash", NULL }, .alias = { "ipportnethash", NULL },
.revision = 0, .revision = 1,
.family = AF_INET46, .family = AF_INET46,
.dimension = IPSET_DIM_THREE, .dimension = IPSET_DIM_THREE,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_ip4_single6, .parse = ipset_parse_ip4_single6,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP .opt = IPSET_OPT_IP
}, },
[IPSET_DIM_TWO] = { [IPSET_DIM_TWO] = {
.parse = ipset_parse_proto_port, .parse = ipset_parse_proto_port,
.print = ipset_print_proto_port, .print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT .opt = IPSET_OPT_PORT
}, },
[IPSET_DIM_THREE] = { [IPSET_DIM_THREE] = {
.parse = ipset_parse_ipnet, .parse = ipset_parse_ipnet,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP2 .opt = IPSET_OPT_IP2
@@ -133,6 +133,7 @@ struct ipset_type ipset_hash_ipportnet0 = {
| IPSET_FLAG(IPSET_OPT_MAXELEM) | IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT), | IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO) | IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO) | 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_CIDR2)
| IPSET_FLAG(IPSET_OPT_TIMEOUT), | IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO) | IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO) | IPSET_FLAG(IPSET_OPT_PORT_TO)
@@ -154,6 +156,99 @@ struct ipset_type ipset_hash_ipportnet0 = {
| IPSET_FLAG(IPSET_OPT_CIDR2), | IPSET_FLAG(IPSET_OPT_CIDR2),
}, },
.usage = hash_ipportnet_usage, .usage = hash_ipportnet1_usage,
.usagefn = ipset_port_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

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -47,7 +47,7 @@ static const struct ipset_arg hash_net_create_args[] = {
.parse = ipset_parse_ignored, .print = ipset_print_number, .parse = ipset_parse_ignored, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const struct ipset_arg hash_net_add_args[] = { static const struct ipset_arg hash_net_add_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
@@ -55,9 +55,9 @@ static const struct ipset_arg hash_net_add_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char hash_net_usage[] = static const char hash_net0_usage[] =
"create SETNAME hash:net\n" "create SETNAME hash:net\n"
" [family inet|inet6]\n" " [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n" " [hashsize VALUE] [maxelem VALUE]\n"
@@ -75,8 +75,8 @@ struct ipset_type ipset_hash_net0 = {
.revision = 0, .revision = 0,
.family = AF_INET46, .family = AF_INET46,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_ipnet, .parse = ipset_parse_ipnet,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP .opt = IPSET_OPT_IP
@@ -105,5 +105,60 @@ struct ipset_type ipset_hash_net0 = {
| IPSET_FLAG(IPSET_OPT_CIDR), | 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,7 +1,7 @@
/* 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 * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -11,7 +11,7 @@
#include <libipset/types.h> /* prototypes */ #include <libipset/types.h> /* prototypes */
/* Parse commandline arguments */ /* Parse commandline arguments */
static const struct ipset_arg hash_netport_create_args[] = { static const struct ipset_arg hash_netiface_create_args[] = {
{ .name = { "family", NULL }, { .name = { "family", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY,
.parse = ipset_parse_family, .print = ipset_print_family, .parse = ipset_parse_family, .print = ipset_print_family,
@@ -39,84 +39,82 @@ static const struct ipset_arg hash_netport_create_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const struct ipset_arg hash_netport_add_args[] = { static const struct ipset_arg hash_netiface_add_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const char hash_netport_usage[] = static const char hash_netiface_usage[] =
"create SETNAME hash:net,port\n" "create SETNAME hash:net,iface\n"
" [family inet|inet6]\n" " [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n" " [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n" " [timeout VALUE]\n"
"add SETNAME IP[/CIDR],PROTO:PORT [timeout VALUE]\n" "add SETNAME IP[/CIDR]|FROM-TO,[physdev:]IFACE [timeout VALUE]\n"
"del SETNAME IP[/CIDR],PROTO:PORT\n" "del SETNAME IP[/CIDR]|FROM-TO,[physdev:]IFACE\n"
"test SETNAME IP[/CIDR],PROTO:PORT\n\n" "test SETNAME IP[/CIDR],[physdev:]IFACE\n\n"
"where depending on the INET family\n" "where depending on the INET family\n"
" IP is a valid IPv4 or IPv6 address (or hostname),\n" " IP is a valid IPv4 or IPv6 address (or hostname),\n"
" CIDR is a valid IPv4 or IPv6 CIDR prefix.\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 = { struct ipset_type ipset_hash_netiface0 = {
.name = "hash:net,port", .name = "hash:net,iface",
.alias = { "netporthash", NULL }, .alias = { "netifacehash", NULL },
.revision = 0, .revision = 0,
.family = AF_INET46, .family = AF_INET46,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_ipnet, .parse = ipset_parse_ip4_net6,
.print = ipset_print_ip, .print = ipset_print_ip,
.opt = IPSET_OPT_IP .opt = IPSET_OPT_IP
}, },
[IPSET_DIM_TWO] = { [IPSET_DIM_TWO] = {
.parse = ipset_parse_proto_port, .parse = ipset_parse_iface,
.print = ipset_print_proto_port, .print = ipset_print_iface,
.opt = IPSET_OPT_PORT .opt = IPSET_OPT_IFACE
}, },
}, },
.args = { .args = {
[IPSET_CREATE] = hash_netport_create_args, [IPSET_CREATE] = hash_netiface_create_args,
[IPSET_ADD] = hash_netport_add_args, [IPSET_ADD] = hash_netiface_add_args,
}, },
.mandatory = { .mandatory = {
[IPSET_CREATE] = 0, [IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IFACE),
| IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IFACE),
| IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IFACE),
| IPSET_FLAG(IPSET_OPT_PORT),
}, },
.full = { .full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM) | IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT), | IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_PORT_TO) | IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IFACE)
| IPSET_FLAG(IPSET_OPT_TIMEOUT) | IPSET_FLAG(IPSET_OPT_PHYSDEV)
| IPSET_FLAG(IPSET_OPT_CIDR), | IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_PORT_TO) | IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IFACE)
| IPSET_FLAG(IPSET_OPT_CIDR), | IPSET_FLAG(IPSET_OPT_PHYSDEV),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_CIDR), | IPSET_FLAG(IPSET_OPT_IFACE)
| IPSET_FLAG(IPSET_OPT_PHYSDEV),
}, },
.usage = hash_netport_usage, .usage = hash_netiface_usage,
.usagefn = ipset_port_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

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/data.h> /* IPSET_OPT_* */
@@ -20,7 +20,7 @@ static const struct ipset_arg list_set_create_args[] = {
.parse = ipset_parse_uint32, .print = ipset_print_number, .parse = ipset_parse_uint32, .print = ipset_print_number,
}, },
{ }, { },
}; };
static const struct ipset_arg list_set_adt_args[] = { static const struct ipset_arg list_set_adt_args[] = {
{ .name = { "timeout", NULL }, { .name = { "timeout", NULL },
@@ -36,7 +36,7 @@ static const struct ipset_arg list_set_adt_args[] = {
.parse = ipset_parse_after, .parse = ipset_parse_after,
}, },
{ }, { },
}; };
static const char list_set_usage[] = static const char list_set_usage[] =
"create SETNAME list:set\n" "create SETNAME list:set\n"
@@ -52,8 +52,8 @@ struct ipset_type ipset_list_set0 = {
.revision = 0, .revision = 0,
.family = AF_UNSPEC, .family = AF_UNSPEC,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.elem = { .elem = {
[IPSET_DIM_ONE] = { [IPSET_DIM_ONE] = {
.parse = ipset_parse_setname, .parse = ipset_parse_setname,
.print = ipset_print_name, .print = ipset_print_name,
.opt = IPSET_OPT_NAME .opt = IPSET_OPT_NAME

View File

@@ -1,7 +1,7 @@
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <assert.h> /* assert */ #include <assert.h> /* assert */
@@ -23,9 +23,9 @@
const struct ipset_commands ipset_commands[] = { const struct ipset_commands ipset_commands[] = {
/* Order is important */ /* Order is important */
{ /* c[reate], --create, n, -N */ { /* c[reate], --create, n[ew], -N */
.cmd = IPSET_CMD_CREATE, .cmd = IPSET_CMD_CREATE,
.name = { "create", "n" }, .name = { "create", "new" },
.has_arg = IPSET_MANDATORY_ARG2, .has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME TYPENAME [type-specific-options]\n" .help = "SETNAME TYPENAME [type-specific-options]\n"
" Create a new set", " Create a new set",
@@ -35,91 +35,91 @@ const struct ipset_commands ipset_commands[] = {
.name = { "add", NULL }, .name = { "add", NULL },
.has_arg = IPSET_MANDATORY_ARG2, .has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME ENTRY\n" .help = "SETNAME ENTRY\n"
" Add entry to the named set", " Add entry to the named set",
}, },
{ /* d[el], --del, -D */ { /* d[el], --del, -D */
.cmd = IPSET_CMD_DEL, .cmd = IPSET_CMD_DEL,
.name = { "del", NULL }, .name = { "del", NULL },
.has_arg = IPSET_MANDATORY_ARG2, .has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME ENTRY\n" .help = "SETNAME ENTRY\n"
" Delete entry from the named set", " Delete entry from the named set",
}, },
{ /* t[est], --test, -T */ { /* t[est], --test, -T */
.cmd = IPSET_CMD_TEST, .cmd = IPSET_CMD_TEST,
.name = { "test", NULL }, .name = { "test", NULL },
.has_arg = IPSET_MANDATORY_ARG2, .has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME ENTRY\n" .help = "SETNAME ENTRY\n"
" Test entry in the named set", " Test entry in the named set",
}, },
{ /* des[troy], --destroy, x, -X */ { /* des[troy], --destroy, x, -X */
.cmd = IPSET_CMD_DESTROY, .cmd = IPSET_CMD_DESTROY,
.name = { "destroy", "x" }, .name = { "destroy", "x" },
.has_arg = IPSET_OPTIONAL_ARG, .has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n" .help = "[SETNAME]\n"
" Destroy a named set or all sets", " Destroy a named set or all sets",
}, },
{ /* l[ist], --list, -L */ { /* l[ist], --list, -L */
.cmd = IPSET_CMD_LIST, .cmd = IPSET_CMD_LIST,
.name = { "list", NULL }, .name = { "list", NULL },
.has_arg = IPSET_OPTIONAL_ARG, .has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n" .help = "[SETNAME]\n"
" List the entries of a named set or all sets", " List the entries of a named set or all sets",
}, },
{ /* s[save], --save, -S */ { /* s[save], --save, -S */
.cmd = IPSET_CMD_SAVE, .cmd = IPSET_CMD_SAVE,
.name = { "save", NULL }, .name = { "save", NULL },
.has_arg = IPSET_OPTIONAL_ARG, .has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n" .help = "[SETNAME]\n"
" Save the named set or all sets to stdout", " Save the named set or all sets to stdout",
}, },
{ /* r[estore], --restore, -R */ { /* r[estore], --restore, -R */
.cmd = IPSET_CMD_RESTORE, .cmd = IPSET_CMD_RESTORE,
.name = { "restore", NULL }, .name = { "restore", NULL },
.has_arg = IPSET_NO_ARG, .has_arg = IPSET_NO_ARG,
.help = "\n" .help = "\n"
" Restore a saved state", " Restore a saved state",
}, },
{ /* f[lush], --flush, -F */ { /* f[lush], --flush, -F */
.cmd = IPSET_CMD_FLUSH, .cmd = IPSET_CMD_FLUSH,
.name = { "flush", NULL }, .name = { "flush", NULL },
.has_arg = IPSET_OPTIONAL_ARG, .has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n" .help = "[SETNAME]\n"
" Flush a named set or all sets", " Flush a named set or all sets",
}, },
{ /* ren[ame], --rename, e, -E */ { /* ren[ame], --rename, e, -E */
.cmd = IPSET_CMD_RENAME, .cmd = IPSET_CMD_RENAME,
.name = { "rename", "e" }, .name = { "rename", "e" },
.has_arg = IPSET_MANDATORY_ARG2, .has_arg = IPSET_MANDATORY_ARG2,
.help = "FROM-SETNAME TO-SETNAME\n" .help = "FROM-SETNAME TO-SETNAME\n"
" Rename two sets", " Rename two sets",
}, },
{ /* sw[ap], --swap, w, -W */ { /* sw[ap], --swap, w, -W */
.cmd = IPSET_CMD_SWAP, .cmd = IPSET_CMD_SWAP,
.name = { "swap", "w" }, .name = { "swap", "w" },
.has_arg = IPSET_MANDATORY_ARG2, .has_arg = IPSET_MANDATORY_ARG2,
.help = "FROM-SETNAME TO-SETNAME\n" .help = "FROM-SETNAME TO-SETNAME\n"
" Swap the contect of two existing sets", " Swap the contect of two existing sets",
}, },
{ /* h[elp, --help, -H */ { /* h[elp, --help, -H */
.cmd = IPSET_CMD_HELP, .cmd = IPSET_CMD_HELP,
.name = { "help", NULL }, .name = { "help", NULL },
.has_arg = IPSET_OPTIONAL_ARG, .has_arg = IPSET_OPTIONAL_ARG,
.help = "[TYPENAME]\n" .help = "[TYPENAME]\n"
" Print help, and settype specific help", " Print help, and settype specific help",
}, },
{ /* v[ersion], --version, -V */ { /* v[ersion], --version, -V */
.cmd = IPSET_CMD_VERSION, .cmd = IPSET_CMD_VERSION,
.name = { "version", NULL }, .name = { "version", NULL },
.has_arg = IPSET_NO_ARG, .has_arg = IPSET_NO_ARG,
.help = "\n" .help = "\n"
" Print version information", " Print version information",
}, },
{ /* q[uit] */ { /* q[uit] */
.cmd = IPSET_CMD_QUIT, .cmd = IPSET_CMD_QUIT,
.name = { "quit", NULL }, .name = { "quit", NULL },
.has_arg = IPSET_NO_ARG, .has_arg = IPSET_NO_ARG,
.help = "\n" .help = "\n"
" Quit interactive mode", " Quit interactive mode",
}, },
{ }, { },
}; };
@@ -143,14 +143,15 @@ ipset_match_cmd(const char *arg, const char * const name[])
if (len > strlen(name[0]) || !len) if (len > strlen(name[0]) || !len)
return false; 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; return true;
else if (len != 1) else if (len != 1)
return false; return false;
else if (name[1] == NULL)
return tolower(arg[0]) == name[0][0];
else 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[] = { const struct ipset_envopts ipset_envopts[] = {
@@ -158,36 +159,49 @@ const struct ipset_envopts ipset_envopts[] = {
.has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX, .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX,
.parse = ipset_parse_output, .parse = ipset_parse_output,
.help = "plain|save|xml\n" .help = "plain|save|xml\n"
" Specify output mode for listing sets.\n" " Specify output mode for listing sets.\n"
" Default value for \"list\" command is mode \"plain\"\n" " Default value for \"list\" command is mode \"plain\"\n"
" and for \"save\" command is mode \"save\".", " and for \"save\" command is mode \"save\".",
}, },
{ .name = { "-s", "-sorted" }, { .name = { "-s", "-sorted" },
.parse = ipset_envopt_parse, .parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_SORTED, .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_SORTED,
.help = "\n" .help = "\n"
" Print elements sorted (if supported by the set type).", " Print elements sorted (if supported by the set type).",
}, },
{ .name = { "-q", "-quiet" }, { .name = { "-q", "-quiet" },
.parse = ipset_envopt_parse, .parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_QUIET, .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_QUIET,
.help = "\n" .help = "\n"
" Suppress any notice or warning message.", " Suppress any notice or warning message.",
}, },
{ .name = { "-r", "-resolve" }, { .name = { "-r", "-resolve" },
.parse = ipset_envopt_parse, .parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_RESOLVE, .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_RESOLVE,
.help = "\n" .help = "\n"
" Try to resolve IP addresses in the output (slow!)", " Try to resolve IP addresses in the output (slow!)",
}, },
{ .name = { "-!", "-exist" }, { .name = { "-!", "-exist" },
.parse = ipset_envopt_parse, .parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_EXIST, .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_EXIST,
.help = "\n" .help = "\n"
" Ignore errors when creating already created sets,\n" " Ignore errors when creating already created sets,\n"
" when adding already existing elements\n" " when adding already existing elements\n"
" or when deleting non-existing elements.", " 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.",
},
{ }, { },
}; };
@@ -197,13 +211,13 @@ ipset_match_option(const char *arg, const char * const name[])
{ {
assert(arg); assert(arg);
assert(name && name[0]); assert(name && name[0]);
/* Skip two leading dashes */ /* Skip two leading dashes */
if (arg[0] == '-' && arg[1] == '-') if (arg[0] == '-' && arg[1] == '-')
arg++, arg++; arg++, arg++;
return STREQ(arg, name[0]) return STREQ(arg, name[0]) ||
|| (name[1] != NULL && STREQ(arg, name[1])); (name[1] != NULL && STREQ(arg, name[1]));
} }
/* Strict envopt matching */ /* Strict envopt matching */
@@ -212,13 +226,13 @@ ipset_match_envopt(const char *arg, const char * const name[])
{ {
assert(arg); assert(arg);
assert(name && name[0]); assert(name && name[0]);
/* Skip one leading dash */ /* Skip one leading dash */
if (arg[0] == '-' && arg[1] == '-') if (arg[0] == '-' && arg[1] == '-')
arg++; arg++;
return STREQ(arg, name[0]) return STREQ(arg, name[0]) ||
|| (name[1] != NULL && STREQ(arg, name[1])); (name[1] != NULL && STREQ(arg, name[1]));
} }
/** /**
@@ -234,19 +248,18 @@ void
ipset_shift_argv(int *argc, char *argv[], int from) ipset_shift_argv(int *argc, char *argv[], int from)
{ {
int i; int i;
assert(*argc >= from + 1); assert(*argc >= from + 1);
for (i = from + 1; i <= *argc; i++) { for (i = from + 1; i <= *argc; i++)
argv[i-1] = argv[i]; argv[i-1] = argv[i];
}
(*argc)--; (*argc)--;
return; return;
} }
/** /**
* ipset_port_usage - prints the usage for the port parameter * ipset_port_usage - prints the usage for the port parameter
* *
* Print the usage for the port parameter to stdout. * Print the usage for the port parameter to stdout.
*/ */
void void
@@ -256,9 +269,9 @@ ipset_port_usage(void)
const char *name; const char *name;
printf(" [PROTO:]PORT is a valid pattern of the following:\n" printf(" [PROTO:]PORT is a valid pattern of the following:\n"
" PORTNAME port name from /etc/services\n" " PORTNAME TCP port name from /etc/services\n"
" PORTNUMBER port number identifier\n" " PORTNUMBER TCP port number identifier\n"
" tcp|udp:PORTNAME|PORTNUMBER\n" " tcp|sctp|udp|udplite:PORTNAME|PORTNUMBER\n"
" icmp:CODENAME supported ICMP codename\n" " icmp:CODENAME supported ICMP codename\n"
" icmp:TYPE/CODE ICMP type/code value\n" " icmp:TYPE/CODE ICMP type/code value\n"
" icmpv6:CODENAME supported ICMPv6 codename\n" " icmpv6:CODENAME supported ICMPv6 codename\n"

View File

@@ -29,52 +29,33 @@ MODULE_ALIAS("ip6t_SET");
static inline int static inline int
match_set(ip_set_id_t index, const struct sk_buff *skb, 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; inv = !inv;
return 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 */ /* 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 static bool
set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
#endif
{ {
const struct xt_set_info_match_v0 *info = par->matchinfo; 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, return match_set(info->match_set.index, skb, par, &opt,
info->match_set.u.compat.dim,
info->match_set.u.compat.flags,
info->match_set.u.compat.flags & IPSET_INV_MATCH); 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 static int
set_match_v0_checkentry(const struct xt_mtchk_param *par) set_match_v0_checkentry(const struct xt_mtchk_param *par)
#endif
{ {
struct xt_set_info_match_v0 *info = par->matchinfo; struct xt_set_info_match_v0 *info = par->matchinfo;
ip_set_id_t index; ip_set_id_t index;
@@ -110,17 +86,19 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match\n", pr_warning("Cannot find set indentified by id %u to match\n",
info->match_set.index); info->match_set.index);
return CHECK_FAIL; /* error */ return -ENOENT;
} }
if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("That's nasty!\n"); pr_warning("Protocol error: set match dimension "
return CHECK_FAIL; /* error */ "is over the limit!\n");
ip_set_nfnl_put(info->match_set.index);
return -ERANGE;
} }
/* Fill out compatibility data */ /* Fill out compatibility data */
compat_flags(&info->match_set); compat_flags(&info->match_set);
return CHECK_OK; return 0;
} }
static void static void
@@ -131,35 +109,25 @@ set_match_v0_destroy(const struct xt_mtdtor_param *par)
ip_set_nfnl_put(info->match_set.index); 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 static unsigned int
set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
#endif
{ {
const struct xt_set_info_target_v0 *info = par->targinfo; 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) if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par->family, ip_set_add(info->add_set.index, skb, par, &add_opt);
info->add_set.u.compat.dim,
info->add_set.u.compat.flags);
if (info->del_set.index != IPSET_INVALID_ID) if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, skb, par->family, ip_set_del(info->del_set.index, skb, par, &del_opt);
info->del_set.u.compat.dim,
info->del_set.u.compat.flags);
return XT_CONTINUE; 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 static int
set_target_v0_checkentry(const struct xt_tgchk_param *par) set_target_v0_checkentry(const struct xt_tgchk_param *par)
#endif
{ {
struct xt_set_info_target_v0 *info = par->targinfo; struct xt_set_info_target_v0 *info = par->targinfo;
ip_set_id_t index; 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) { if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->add_set.index); index = ip_set_nfnl_get_byindex(info->add_set.index);
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("cannot find add_set index %u as target\n", pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index); info->add_set.index);
return CHECK_FAIL; /* error */ return -ENOENT;
} }
} }
if (info->del_set.index != IPSET_INVALID_ID) { if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->del_set.index); index = ip_set_nfnl_get_byindex(info->del_set.index);
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("cannot find del_set index %u as target\n", pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index); 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 || if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("That's nasty!\n"); pr_warning("Protocol error: SET target dimension "
return CHECK_FAIL; /* error */ "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 */ /* Fill out compatibility data */
compat_flags(&info->add_set); compat_flags(&info->add_set);
compat_flags(&info->del_set); compat_flags(&info->del_set);
return CHECK_OK; return 0;
} }
static void static void
@@ -205,33 +180,23 @@ set_target_v0_destroy(const struct xt_tgdtor_param *par)
ip_set_nfnl_put(info->del_set.index); 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 static bool
set_match(const struct sk_buff *skb, const struct xt_match_param *par) set_match_v1(const struct sk_buff *skb, struct xt_action_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
{ {
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, return match_set(info->match_set.index, skb, par, &opt,
info->match_set.dim,
info->match_set.flags,
info->match_set.flags & IPSET_INV_MATCH); 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 static int
set_match_checkentry(const struct xt_mtchk_param *par) set_match_v1_checkentry(const struct xt_mtchk_param *par)
#endif
{ {
struct xt_set_info_match *info = par->matchinfo; struct xt_set_info_match_v1 *info = par->matchinfo;
ip_set_id_t index; ip_set_id_t index;
index = ip_set_nfnl_get_byindex(info->match_set.index); index = ip_set_nfnl_get_byindex(info->match_set.index);
@@ -239,89 +204,86 @@ set_match_checkentry(const struct xt_mtchk_param *par)
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match\n", pr_warning("Cannot find set indentified by id %u to match\n",
info->match_set.index); info->match_set.index);
return CHECK_FAIL; /* error */ return -ENOENT;
} }
if (info->match_set.dim > IPSET_DIM_MAX) { if (info->match_set.dim > IPSET_DIM_MAX) {
pr_warning("That's nasty!\n"); pr_warning("Protocol error: set match dimension "
return CHECK_FAIL; /* error */ "is over the limit!\n");
ip_set_nfnl_put(info->match_set.index);
return -ERANGE;
} }
return CHECK_OK; return 0;
} }
static void 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); ip_set_nfnl_put(info->match_set.index);
} }
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static unsigned int static unsigned int
set_target(struct sk_buff *skb, const struct xt_target_param *par) set_target_v1(struct sk_buff *skb, const struct xt_action_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
{ {
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) if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, ip_set_add(info->add_set.index, skb, par, &add_opt);
skb, par->family,
info->add_set.dim,
info->add_set.flags);
if (info->del_set.index != IPSET_INVALID_ID) if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, ip_set_del(info->del_set.index, skb, par, &del_opt);
skb, par->family,
info->add_set.dim,
info->del_set.flags);
return XT_CONTINUE; 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 static int
set_target_checkentry(const struct xt_tgchk_param *par) set_target_v1_checkentry(const struct xt_tgchk_param *par)
#endif
{ {
const struct xt_set_info_target *info = par->targinfo; const struct xt_set_info_target_v1 *info = par->targinfo;
ip_set_id_t index; ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) { if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->add_set.index); index = ip_set_nfnl_get_byindex(info->add_set.index);
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("cannot find add_set index %u as target\n", pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index); info->add_set.index);
return CHECK_FAIL; /* error */ return -ENOENT;
} }
} }
if (info->del_set.index != IPSET_INVALID_ID) { if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->del_set.index); index = ip_set_nfnl_get_byindex(info->del_set.index);
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("cannot find del_set index %u as target\n", pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index); 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 || if (info->add_set.dim > IPSET_DIM_MAX ||
info->del_set.flags > IPSET_DIM_MAX) { info->del_set.dim > IPSET_DIM_MAX) {
pr_warning("That's nasty!\n"); pr_warning("Protocol error: SET target dimension "
return CHECK_FAIL; /* error */ "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 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) if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index); 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); 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 = { static struct xt_match set_matches[] __read_mostly = {
{ {
.name = "set", .name = "set",
@@ -344,20 +328,20 @@ static struct xt_match set_matches[] __read_mostly = {
.name = "set", .name = "set",
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.revision = 1, .revision = 1,
.match = set_match, .match = set_match_v1,
.matchsize = sizeof(struct xt_set_info_match), .matchsize = sizeof(struct xt_set_info_match_v1),
.checkentry = set_match_checkentry, .checkentry = set_match_v1_checkentry,
.destroy = set_match_destroy, .destroy = set_match_v1_destroy,
.me = THIS_MODULE .me = THIS_MODULE
}, },
{ {
.name = "set", .name = "set",
.family = NFPROTO_IPV6, .family = NFPROTO_IPV6,
.revision = 1, .revision = 1,
.match = set_match, .match = set_match_v1,
.matchsize = sizeof(struct xt_set_info_match), .matchsize = sizeof(struct xt_set_info_match_v1),
.checkentry = set_match_checkentry, .checkentry = set_match_v1_checkentry,
.destroy = set_match_destroy, .destroy = set_match_v1_destroy,
.me = THIS_MODULE .me = THIS_MODULE
}, },
}; };
@@ -377,20 +361,40 @@ static struct xt_target set_targets[] __read_mostly = {
.name = "SET", .name = "SET",
.revision = 1, .revision = 1,
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.target = set_target, .target = set_target_v1,
.targetsize = sizeof(struct xt_set_info_target), .targetsize = sizeof(struct xt_set_info_target_v1),
.checkentry = set_target_checkentry, .checkentry = set_target_v1_checkentry,
.destroy = set_target_destroy, .destroy = set_target_v1_destroy,
.me = THIS_MODULE .me = THIS_MODULE
}, },
{ {
.name = "SET", .name = "SET",
.revision = 1, .revision = 1,
.family = NFPROTO_IPV6, .family = NFPROTO_IPV6,
.target = set_target, .target = set_target_v1,
.targetsize = sizeof(struct xt_set_info_target), .targetsize = sizeof(struct xt_set_info_target_v1),
.checkentry = set_target_checkentry, .checkentry = set_target_v1_checkentry,
.destroy = set_target_destroy, .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 .me = THIS_MODULE
}, },
}; };

View File

@@ -1,6 +1,7 @@
#ifndef _XT_SET_H #ifndef _XT_SET_H
#define _XT_SET_H #define _XT_SET_H
#include <linux/types.h>
#include "ip_set.h" #include "ip_set.h"
/* Revision 0 interface: backward compatible with netfilter/iptables */ /* 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; struct xt_set_info_v0 del_set;
}; };
/* Revision 1: current interface to netfilter/iptables */ /* Revision 1 match and target */
struct xt_set_info { struct xt_set_info {
ip_set_id_t index; ip_set_id_t index;
@@ -43,13 +44,22 @@ struct xt_set_info {
}; };
/* match and target infos */ /* 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 match_set;
}; };
struct xt_set_info_target { struct xt_set_info_target_v1 {
struct xt_set_info add_set; struct xt_set_info add_set;
struct xt_set_info del_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*/ #endif /*_XT_SET_H*/

View File

@@ -36,6 +36,8 @@ The SYSRQ password can be changed through
.IP .IP
echo \-n "password" >/sys/module/xt_SYSRQ/parameters/password echo \-n "password" >/sys/module/xt_SYSRQ/parameters/password
.PP .PP
The module will not respond to sysrq requests until a password has been set.
.PP
Alternatively, the password may be specified at modprobe time, but this is Alternatively, the password may be specified at modprobe time, but this is
insecure as people can possible see it through ps(1). You can use an option insecure as people can possible see it through ps(1). You can use an option
line in e.g. /etc/modprobe.d/xt_sysrq if it is properly guarded, that is, only line in e.g. /etc/modprobe.d/xt_sysrq if it is properly guarded, that is, only
@@ -52,7 +54,7 @@ The xt_SYSRQ module is normally silent unless a successful request is received,
but the \fIdebug\fP module parameter can be used to find exactly why a but the \fIdebug\fP module parameter can be used to find exactly why a
seemingly correct request is not being processed. seemingly correct request is not being processed.
.PP .PP
To trigger SYSRQ from a remote host, just use netcat or socat: To trigger SYSRQ from a remote host, just use socat:
.PP .PP
.nf .nf
sysrq_key="s" # the SysRq key(s) sysrq_key="s" # the SysRq key(s)
@@ -60,12 +62,11 @@ password="password"
seqno="$(date +%s)" seqno="$(date +%s)"
salt="$(dd bs=12 count=1 if=/dev/urandom 2>/dev/null | salt="$(dd bs=12 count=1 if=/dev/urandom 2>/dev/null |
openssl enc \-base64)" openssl enc \-base64)"
ipaddr=10.10.25.7
req="$sysrq_key,$seqno,$salt" req="$sysrq_key,$seqno,$salt"
req="$req,$(echo \-n "$req,$password" | sha1sum | cut \-c1\-40)" req="$req,$(echo \-n "$req,$ipaddr,$password" | sha1sum | cut \-c1\-40)"
echo "$req" | socat stdin udp\-sendto:10.10.25.7:9 echo "$req" | socat stdin udp\-sendto:$ipaddr:9
# or
echo "$req" | netcat \-uw1 10.10.25.7 9
.fi .fi
.PP .PP
See the Linux docs for possible sysrq keys. Important ones are: re(b)oot, See the Linux docs for possible sysrq keys. Important ones are: re(b)oot,
@@ -73,8 +74,10 @@ power(o)ff, (s)ync filesystems, (u)mount and remount readonly. More than one
sysrq key can be used at once, but bear in mind that, for example, a sync may sysrq key can be used at once, but bear in mind that, for example, a sync may
not complete before a subsequent reboot or poweroff. not complete before a subsequent reboot or poweroff.
.PP .PP
An IPv4 address should have no leading zeros, an IPv6 address should
be in the form recommended by RFC 5952. The debug option will log the
correct form of the address.
.PP
The hashing scheme should be enough to prevent mis-use of SYSRQ in many The hashing scheme should be enough to prevent mis-use of SYSRQ in many
environments, but it is not perfect: take reasonable precautions to environments, but it is not perfect: take reasonable precautions to
protect your machines. Most importantly ensure that each machine has a protect your machines.
different password; there is scant protection for a SYSRQ packet being
applied to a machine that happens to have the same password.

View File

@@ -1,34 +1,120 @@
/* /*
* "TARPIT" target extension to iptables * "TARPIT" target extension to iptables
* this file is in the Public Domain *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License; either
* version 2 of the License, or any later version, as published by the
* Free Software Foundation.
*/ */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <getopt.h> #include <getopt.h>
#include <string.h>
#include <xtables.h> #include <xtables.h>
#include <linux/netfilter/x_tables.h>
#include "xt_TARPIT.h"
#include "compat_user.h" #include "compat_user.h"
enum {
F_TARPIT = 1 << 0,
F_HONEYPOT = 1 << 1,
F_RESET = 1 << 2,
};
static const struct option tarpit_tg_opts[] = {
{.name = "tarpit", .has_arg = false, .val = 't'},
{.name = "honeypot", .has_arg = false, .val = 'h'},
{.name = "reset", .has_arg = false, .val = 'r'},
{NULL},
};
static void tarpit_tg_help(void) static void tarpit_tg_help(void)
{ {
printf("TARPIT takes no options\n\n"); printf(
"TARPIT target options:\n"
" --tarpit Enable classic 0-window tarpit (default)\n"
" --honeypot Enable honeypot option\n"
" --reset Enable inline resets\n");
} }
static int tarpit_tg_parse(int c, char **argv, int invert, unsigned int *flags, static int tarpit_tg_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target) const void *entry, struct xt_entry_target **target)
{ {
return 0; struct xt_tarpit_tginfo *info = (void *)(*target)->data;
switch (c) {
case 't':
info->variant = XTTARPIT_TARPIT;
*flags |= F_TARPIT;
return true;
case 'h':
info->variant = XTTARPIT_HONEYPOT;
*flags |= F_HONEYPOT;
return true;
case 'r':
info->variant = XTTARPIT_RESET;
*flags |= F_RESET;
return true;
}
return false;
} }
static void tarpit_tg_check(unsigned int flags) static void tarpit_tg_check(unsigned int flags)
{ {
if (flags == (F_TARPIT | F_HONEYPOT | F_RESET))
xtables_error(PARAMETER_PROBLEM,
"TARPIT: only one action can be used at a time");
}
static void tarpit_tg_print(const void *ip,
const struct xt_entry_target *target, int numeric)
{
const struct xt_tarpit_tginfo *info = (void *)target->data;
switch (info->variant) {
case XTTARPIT_HONEYPOT:
printf(" honeypot mode ");
break;
case XTTARPIT_RESET:
printf(" reset mode ");
break;
default:
printf(" tarpit mode ");
break;
}
}
static void tarpit_tg_save(const void *ip,
const struct xt_entry_target *target)
{
const struct xt_tarpit_tginfo *info = (const void *)target->data;
switch (info->variant) {
case XTTARPIT_TARPIT:
printf(" --tarpit ");
break;
case XTTARPIT_HONEYPOT:
printf(" --honeypot ");
break;
case XTTARPIT_RESET:
printf(" --reset ");
break;
}
} }
static struct xtables_target tarpit_tg_reg = { static struct xtables_target tarpit_tg_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "TARPIT", .name = "TARPIT",
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_tarpit_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_tarpit_tginfo)),
.help = tarpit_tg_help, .help = tarpit_tg_help,
.parse = tarpit_tg_parse, .parse = tarpit_tg_parse,
.final_check = tarpit_tg_check, .final_check = tarpit_tg_check,
.print = tarpit_tg_print,
.save = tarpit_tg_save,
.extra_opts = tarpit_tg_opts,
}; };
static __attribute__((constructor)) void tarpit_tg_ldr(void) static __attribute__((constructor)) void tarpit_tg_ldr(void)

View File

@@ -1,14 +1,38 @@
Captures and holds incoming TCP connections using no local per-connection Captures and holds incoming TCP connections using no local per-connection
resources. Connections are accepted, but immediately switched to the persist resources.
state (0 byte window), in which the remote side stops sending data and asks to .PP
continue every 60-240 seconds. Attempts to close the connection are ignored, TARPIT only works at the TCP level, and is totally application agnostic. This
forcing the remote side to time out the connection in 12-24 minutes. module will answer a TCP request and play along like a listening server, but
aside from sending an ACK or RST, no data is sent. Incoming packets are ignored
and dropped. The attacker will terminate the session eventually. This module
allows the initial packets of an attack to be captured by other software for
inspection. In most cases this is sufficient to determine the nature of the
attack.
.PP
This offers similar functionality to LaBrea This offers similar functionality to LaBrea
<http://www.hackbusters.net/LaBrea/> but does not require dedicated hardware or <http://www.hackbusters.net/LaBrea/> but does not require dedicated hardware or
IPs. Any TCP port that you would normally DROP or REJECT can instead become a IPs. Any TCP port that you would normally DROP or REJECT can instead become a
tarpit. tarpit.
.TP
\fB\-\-tarpit\fP
This mode completes a connection with the attacker but limits the window size
to 0, thus keeping the attacker waiting long periods of time. While he is
maintaining state of the connection and trying to continue every 60-240
seconds, we keep none, so it is very lightweight. Attempts to close the
connection are ignored, forcing the remote side to time out the connection in
12-24 minutes. This mode is the default.
.TP
\fB\-\-honeypot\fP
This mode completes a connection with the attacker, but signals a normal window
size, so that the remote side will attempt to send data, often with some very
nasty exploit attempts. We can capture these packets for decoding and further
analysis. The module does not send any data, so if the remote expects an
application level response, the game is up.
.TP
\fB\-\-reset\fP
This mode is handy because we can send an inline RST (reset). It has no other
function.
.PP
To tarpit connections to TCP port 80 destined for the current machine: To tarpit connections to TCP port 80 destined for the current machine:
.IP .IP
\-A INPUT \-p tcp \-m tcp \-\-dport 80 \-j TARPIT \-A INPUT \-p tcp \-m tcp \-\-dport 80 \-j TARPIT
@@ -24,10 +48,12 @@ the Linux box, and add:
.PP .PP
NOTE: NOTE:
If you use the conntrack module while you are using TARPIT, you should also use If you use the conntrack module while you are using TARPIT, you should also use
the NOTRACK target, or the kernel will unnecessarily allocate resources for unset tracking on the packet, or the kernel will unnecessarily allocate
each TARPITted connection. To TARPIT incoming connections to the standard IRC resources for each TARPITted connection. To TARPIT incoming connections to the
port while using conntrack, you could: standard IRC port while using conntrack, you could:
.IP .IP
\-t raw \-A PREROUTING \-p tcp \-\-dport 6667 \-j NOTRACK \-t raw \-A PREROUTING \-p tcp \-\-dport 6667 \-j CT \-\-notrack
.IP
\-A INPUT \-p tcp \-\-dport 6667 \-j NFLOG
.IP .IP
\-A INPUT \-p tcp \-\-dport 6667 \-j TARPIT \-A INPUT \-p tcp \-\-dport 6667 \-j TARPIT

View File

@@ -21,7 +21,7 @@ Known symbol names (and their number):
.PP .PP
4 \(em \fBtimestamp\fP \(em RFC 781, 791 4 \(em \fBtimestamp\fP \(em RFC 781, 791
.PP .PP
7 \(em \fBrecord\-route\fP \em RFC 791 7 \(em \fBrecord\-route\fP \(em RFC 791
.PP .PP
9 \(em \fBssrr\fP \(em Strict Source Routing, RFC 791 9 \(em \fBssrr\fP \(em Strict Source Routing, RFC 791
.PP .PP

View File

@@ -398,19 +398,15 @@ peer_gc(unsigned long r)
/** /**
* Compares length and name equality for the rules. * Compares length and name equality for the rules.
*
* @info
* @rule
* @return: 0 equals, 1 otherwise
*/ */
static inline bool static inline bool
rulecmp(const struct xt_pknock_mtinfo *info, const struct xt_pknock_rule *rule) rulecmp(const struct xt_pknock_mtinfo *info, const struct xt_pknock_rule *rule)
{ {
if (info->rule_name_len != rule->rule_name_len) if (info->rule_name_len != rule->rule_name_len)
return true; return false;
if (strncmp(info->rule_name, rule->rule_name, info->rule_name_len) != 0) if (strncmp(info->rule_name, rule->rule_name, info->rule_name_len) != 0)
return true; return false;
return false; return true;
} }
/** /**
@@ -427,9 +423,9 @@ static struct xt_pknock_rule *search_rule(const struct xt_pknock_mtinfo *info)
ipt_pknock_hash_rnd, rule_hashsize); ipt_pknock_hash_rnd, rule_hashsize);
list_for_each_safe(pos, n, &rule_hashtable[hash]) { list_for_each_safe(pos, n, &rule_hashtable[hash]) {
rule = list_entry(pos, struct xt_pknock_rule, head); rule = list_entry(pos, struct xt_pknock_rule, head);
if (rulecmp(info, rule)) if (rulecmp(info, rule))
return rule; return rule;
} }
return NULL; return NULL;
} }
@@ -451,23 +447,20 @@ add_rule(struct xt_pknock_mtinfo *info)
list_for_each_safe(pos, n, &rule_hashtable[hash]) { list_for_each_safe(pos, n, &rule_hashtable[hash]) {
rule = list_entry(pos, struct xt_pknock_rule, head); rule = list_entry(pos, struct xt_pknock_rule, head);
if (rulecmp(info, rule)) { if (!rulecmp(info, rule))
++rule->ref_count; continue;
++rule->ref_count;
if (info->option & XT_PKNOCK_OPENSECRET) { if (info->option & XT_PKNOCK_OPENSECRET) {
rule->max_time = info->max_time; rule->max_time = info->max_time;
rule->autoclose_time = info->autoclose_time; rule->autoclose_time = info->autoclose_time;
}
if (info->option & XT_PKNOCK_CHECKIP) {
pr_debug("add_rule() (AC)"
" rule found: %s - "
"ref_count: %d\n",
rule->rule_name,
rule->ref_count);
}
return true;
} }
if (info->option & XT_PKNOCK_CHECKIP)
pr_debug("add_rule() (AC) rule found: %s - "
"ref_count: %d\n",
rule->rule_name, rule->ref_count);
return true;
} }
rule = kmalloc(sizeof(*rule), GFP_KERNEL); rule = kmalloc(sizeof(*rule), GFP_KERNEL);
@@ -523,7 +516,8 @@ remove_rule(struct xt_pknock_mtinfo *info)
unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len,
ipt_pknock_hash_rnd, rule_hashsize); ipt_pknock_hash_rnd, rule_hashsize);
if (list_empty(&rule_hashtable[hash])) return; if (list_empty(&rule_hashtable[hash]))
return;
list_for_each_safe(pos, n, &rule_hashtable[hash]) { list_for_each_safe(pos, n, &rule_hashtable[hash]) {
rule = list_entry(pos, struct xt_pknock_rule, head); rule = list_entry(pos, struct xt_pknock_rule, head);
@@ -576,7 +570,8 @@ static struct peer *get_peer(struct xt_pknock_rule *rule, __be32 ip)
list_for_each_safe(pos, n, &rule->peer_head[hash]) { list_for_each_safe(pos, n, &rule->peer_head[hash]) {
peer = list_entry(pos, struct peer, head); peer = list_entry(pos, struct peer, head);
if (peer->ip == ip) return peer; if (peer->ip == ip)
return peer;
} }
return NULL; return NULL;
} }
@@ -886,7 +881,7 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info,
/* If security is needed. */ /* If security is needed. */
if (info->option & XT_PKNOCK_OPENSECRET ) { if (info->option & XT_PKNOCK_OPENSECRET ) {
if (hdr->proto != IPPROTO_UDP) if (hdr->proto != IPPROTO_UDP && hdr->proto != IPPROTO_UDPLITE)
return false; return false;
if (!pass_security(peer, info, hdr->payload, hdr->payload_len)) if (!pass_security(peer, info, hdr->payload, hdr->payload_len))
@@ -987,6 +982,7 @@ static bool pknock_mt(const struct sk_buff *skb,
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
case IPPROTO_UDPLITE:
#ifdef PK_CRYPTO #ifdef PK_CRYPTO
hdr_len = (iph->ihl * 4) + sizeof(struct udphdr); hdr_len = (iph->ihl * 4) + sizeof(struct udphdr);
break; break;
@@ -1018,7 +1014,7 @@ static bool pknock_mt(const struct sk_buff *skb,
goto out; goto out;
} }
if (iph->protocol == IPPROTO_UDP) { if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_UDPLITE) {
hdr.payload = (void *)iph + hdr_len; hdr.payload = (void *)iph + hdr_len;
hdr.payload_len = skb->len - hdr_len; hdr.payload_len = skb->len - hdr_len;
} }
@@ -1027,7 +1023,8 @@ static bool pknock_mt(const struct sk_buff *skb,
if (info->option & XT_PKNOCK_KNOCKPORT) { if (info->option & XT_PKNOCK_KNOCKPORT) {
if ((ret = is_allowed(peer))) { if ((ret = is_allowed(peer))) {
if (info->option & XT_PKNOCK_CLOSESECRET && if (info->option & XT_PKNOCK_CLOSESECRET &&
iph->protocol == IPPROTO_UDP) (iph->protocol == IPPROTO_UDP ||
iph->protocol == IPPROTO_UDPLITE))
{ {
if (is_close_knock(peer, info, hdr.payload, hdr.payload_len)) if (is_close_knock(peer, info, hdr.payload, hdr.payload_len))
{ {
@@ -1043,7 +1040,8 @@ static bool pknock_mt(const struct sk_buff *skb,
add_peer(peer, rule); add_peer(peer, rule);
} }
if (peer == NULL) goto out; if (peer == NULL)
goto out;
update_peer(peer, info, rule, &hdr); update_peer(peer, info, rule, &hdr);
} }
@@ -1087,15 +1085,15 @@ static int pknock_mt_check(const struct xt_mtchk_param *par)
RETURN_ERR("No crypto support available; " RETURN_ERR("No crypto support available; "
"cannot use opensecret/closescret\n"); "cannot use opensecret/closescret\n");
#endif #endif
if ((info->option & XT_PKNOCK_OPENSECRET) && (info->ports_count != 1)) if (info->option & XT_PKNOCK_OPENSECRET && info->ports_count != 1)
RETURN_ERR("--opensecret must have just one knock port\n"); RETURN_ERR("--opensecret must have just one knock port\n");
if (info->option & XT_PKNOCK_KNOCKPORT) { if (info->option & XT_PKNOCK_KNOCKPORT) {
if (info->option & XT_PKNOCK_CHECKIP) if (info->option & XT_PKNOCK_CHECKIP)
RETURN_ERR("Can't specify --knockports with --checkip.\n"); RETURN_ERR("Can't specify --knockports with --checkip.\n");
if ((info->option & XT_PKNOCK_OPENSECRET) && if (info->option & XT_PKNOCK_OPENSECRET &&
!(info->option & XT_PKNOCK_CLOSESECRET)) !(info->option & XT_PKNOCK_CLOSESECRET))
RETURN_ERR("--opensecret must go with --closesecret.\n"); RETURN_ERR("--opensecret must go with --closesecret.\n");
if ((info->option & XT_PKNOCK_CLOSESECRET) && if (info->option & XT_PKNOCK_CLOSESECRET &&
!(info->option & XT_PKNOCK_OPENSECRET)) !(info->option & XT_PKNOCK_OPENSECRET))
RETURN_ERR("--closesecret must go with --opensecret.\n"); RETURN_ERR("--closesecret must go with --opensecret.\n");
} }
@@ -1115,13 +1113,11 @@ static int pknock_mt_check(const struct xt_mtchk_param *par)
RETURN_ERR("you must specify --time.\n"); RETURN_ERR("you must specify --time.\n");
} }
if (info->option & XT_PKNOCK_OPENSECRET) { if (info->option & XT_PKNOCK_OPENSECRET &&
if (info->open_secret_len == info->close_secret_len) { info->open_secret_len == info->close_secret_len &&
if (memcmp(info->open_secret, info->close_secret, memcmp(info->open_secret, info->close_secret,
info->open_secret_len) == 0) info->open_secret_len) == 0)
RETURN_ERR("opensecret & closesecret cannot be equal.\n"); RETURN_ERR("opensecret & closesecret cannot be equal.\n");
}
}
if (!add_rule(info)) if (!add_rule(info))
/* should ENOMEM here */ /* should ENOMEM here */
@@ -1166,11 +1162,10 @@ static int __init xt_pknock_mt_init(void)
} }
crypto.tfm = crypto_alloc_hash(crypto.algo, 0, CRYPTO_ALG_ASYNC); crypto.tfm = crypto_alloc_hash(crypto.algo, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(crypto.tfm)) {
if (crypto.tfm == NULL) {
printk(KERN_ERR PKNOCK "failed to load transform for %s\n", printk(KERN_ERR PKNOCK "failed to load transform for %s\n",
crypto.algo); crypto.algo);
return -ENXIO; return PTR_ERR(crypto.tfm);
} }
crypto.size = crypto_hash_digestsize(crypto.tfm); crypto.size = crypto_hash_digestsize(crypto.tfm);
@@ -1195,7 +1190,8 @@ static void __exit xt_pknock_mt_exit(void)
kfree(rule_hashtable); kfree(rule_hashtable);
#ifdef PK_CRYPTO #ifdef PK_CRYPTO
if (crypto.tfm != NULL) crypto_free_hash(crypto.tfm); if (crypto.tfm != NULL)
crypto_free_hash(crypto.tfm);
#endif #endif
} }

View File

@@ -12,6 +12,13 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
# error ----------------------------------------------------------
# error This module has been merged into, and is available in the
# error mainline since Linux kernel v2.6.36. Please use that.
# error ----------------------------------------------------------
#endif
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include "xt_CHECKSUM.h" #include "xt_CHECKSUM.h"
#include "compat_xtables.h" #include "compat_xtables.h"

View File

@@ -3,10 +3,12 @@
* or destination (PREROUTING), * or destination (PREROUTING),
*/ */
/* (C) 2010 Marek Kierdelewicz <marek@koba.pl> /* (C) 2011 Marek Kierdelewicz <marek@koba.pl>
* *
* module is dedicated to my wife Eliza and my daughters Jula and Ola :* :* :* * module is dedicated to my wife Eliza and my daughters Jula and Ola :* :* :*
* *
* module audited and cleaned-up by Jan Engelhardt
*
* module uses some code and ideas from following modules: * module uses some code and ideas from following modules:
* - "NETMAP" module by Svenning Soerensen <svenning@post5.tele.dk> * - "NETMAP" module by Svenning Soerensen <svenning@post5.tele.dk>
* - "recent" module by Stephen Frost <sfrost@snowman.net> * - "recent" module by Stephen Frost <sfrost@snowman.net>
@@ -23,9 +25,12 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <linux/version.h>
#include <net/netfilter/nf_nat_rule.h> #include <net/netfilter/nf_nat_rule.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
#endif
#include "xt_DNETMAP.h" #include "xt_DNETMAP.h"
#include "compat_xtables.h" #include "compat_xtables.h"
@@ -33,6 +38,7 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Kierdelewicz <marek@koba.pl>"); MODULE_AUTHOR("Marek Kierdelewicz <marek@koba.pl>");
MODULE_DESCRIPTION( MODULE_DESCRIPTION(
"Xtables: dynamic two-way 1:1 NAT mapping of IPv4 addresses"); "Xtables: dynamic two-way 1:1 NAT mapping of IPv4 addresses");
MODULE_ALIAS("ipt_DNETMAP");
static unsigned int default_ttl = 600; static unsigned int default_ttl = 600;
static unsigned int proc_perms = S_IRUGO | S_IWUSR; static unsigned int proc_perms = S_IRUGO | S_IWUSR;
@@ -90,11 +96,16 @@ struct dnetmap_net {
struct list_head *dnetmap_iphash; struct list_head *dnetmap_iphash;
}; };
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
static int dnetmap_net_id; static int dnetmap_net_id;
static inline struct dnetmap_net *dnetmap_pernet(struct net *net) static inline struct dnetmap_net *dnetmap_pernet(struct net *net)
{ {
return net_generic(net, dnetmap_net_id); return net_generic(net, dnetmap_net_id);
} }
#else
struct dnetmap_net *dnetmap;
#define dnetmap_pernet(x) dnetmap
#endif
static DEFINE_SPINLOCK(dnetmap_lock); static DEFINE_SPINLOCK(dnetmap_lock);
static DEFINE_MUTEX(dnetmap_mutex); static DEFINE_MUTEX(dnetmap_mutex);
@@ -236,12 +247,12 @@ static int dnetmap_tg_check(const struct xt_tgchk_param *par)
ip_min = ntohl(mr->range[0].min_ip) + (whole_prefix == 0); ip_min = ntohl(mr->range[0].min_ip) + (whole_prefix == 0);
ip_max = ntohl(mr->range[0].max_ip) - (whole_prefix == 0); ip_max = ntohl(mr->range[0].max_ip) - (whole_prefix == 0);
sprintf(p->prefix_str, "%pI4/%u", &mr->range[0].min_ip, sprintf(p->prefix_str, NIPQUAD_FMT "/%u", NIPQUAD(mr->range[0].min_ip),
33 - ffs(~(ip_min ^ ip_max))); 33 - ffs(~(ip_min ^ ip_max)));
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
sprintf(proc_str_data, "%pI4_%u", &mr->range[0].min_ip, sprintf(proc_str_data, NIPQUAD_FMT "_%u", NIPQUAD(mr->range[0].min_ip),
33 - ffs(~(ip_min ^ ip_max))); 33 - ffs(~(ip_min ^ ip_max)));
sprintf(proc_str_stat, "%pI4_%u_stat", &mr->range[0].min_ip, sprintf(proc_str_stat, NIPQUAD_FMT "_%u_stat", NIPQUAD(mr->range[0].min_ip),
33 - ffs(~(ip_min ^ ip_max))); 33 - ffs(~(ip_min ^ ip_max)));
#endif #endif
printk(KERN_INFO KBUILD_MODNAME ": new prefix %s\n", p->prefix_str); printk(KERN_INFO KBUILD_MODNAME ": new prefix %s\n", p->prefix_str);
@@ -368,8 +379,8 @@ bind_new_prefix:
if (e->prenat_addr != 0 && time_before(jiffies, e->stamp)) { if (e->prenat_addr != 0 && time_before(jiffies, e->stamp)) {
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": ip %pI4 - no free adresses in prefix %s\n", ": ip " NIPQUAD_FMT " - no free adresses in prefix %s\n",
&prenat_ip, p->prefix_str); NIPQUAD(prenat_ip), p->prefix_str);
goto no_free_ip; goto no_free_ip;
} }
@@ -379,8 +390,8 @@ bind_new_prefix:
prenat_ip_prev = e->prenat_addr; prenat_ip_prev = e->prenat_addr;
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": timeout binding %pI4 -> %pI4\n", ": timeout binding " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
&prenat_ip_prev, &postnat_ip); NIPQUAD(prenat_ip_prev), NIPQUAD(postnat_ip) );
list_del(&e->list); list_del(&e->list);
list_del(&e->glist); list_del(&e->glist);
list_del(&e->grlist); list_del(&e->grlist);
@@ -400,8 +411,8 @@ bind_new_prefix:
(postnat_ip)]); (postnat_ip)]);
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": add binding %pI4 -> %pI4\n", &prenat_ip, ": add binding " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
&postnat_ip); NIPQUAD(prenat_ip),NIPQUAD(postnat_ip));
} else { } else {
@@ -409,9 +420,9 @@ bind_new_prefix:
if (time_before(e->stamp, jiffies) && p != e->prefix) { if (time_before(e->stamp, jiffies) && p != e->prefix) {
if (!disable_log) if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME printk(KERN_INFO KBUILD_MODNAME
": timeout binding %pI4 -> %pI4\n", ": timeout binding " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
&e->prenat_addr, NIPQUAD(e->prenat_addr),
&e->postnat_addr); NIPQUAD(e->postnat_addr));
list_del(&e->list); list_del(&e->list);
list_del(&e->glist); list_del(&e->glist);
list_del(&e->grlist); list_del(&e->grlist);
@@ -464,11 +475,11 @@ static void dnetmap_tg_destroy(const struct xt_tgdtor_param *par)
list_del(&p->list); list_del(&p->list);
spin_unlock_bh(&dnetmap_lock); spin_unlock_bh(&dnetmap_lock);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
sprintf(str, "%pI4_%u", &mr->range[0].min_ip, sprintf(str, NIPQUAD_FMT "_%u", NIPQUAD(mr->range[0].min_ip),
33 - ffs(~(ntohl(mr->range[0].min_ip ^ 33 - ffs(~(ntohl(mr->range[0].min_ip ^
mr->range[0].max_ip)))); mr->range[0].max_ip))));
remove_proc_entry(str, dnetmap_net->xt_dnetmap); remove_proc_entry(str, dnetmap_net->xt_dnetmap);
sprintf(str, "%pI4_%u_stat", &mr->range[0].min_ip, sprintf(str, NIPQUAD_FMT "_%u_stat", NIPQUAD(mr->range[0].min_ip),
33 - ffs(~(ntohl(mr->range[0].min_ip ^ 33 - ffs(~(ntohl(mr->range[0].min_ip ^
mr->range[0].max_ip)))); mr->range[0].max_ip))));
remove_proc_entry(str, dnetmap_net->xt_dnetmap); remove_proc_entry(str, dnetmap_net->xt_dnetmap);
@@ -525,8 +536,8 @@ static int dnetmap_seq_show(struct seq_file *seq, void *v)
{ {
const struct dnetmap_entry *e = v; const struct dnetmap_entry *e = v;
seq_printf(seq, "%pI4 -> %pI4 --- ttl: %d lasthit: %lu\n", seq_printf(seq, NIPQUAD_FMT " -> " NIPQUAD_FMT " --- ttl: %d lasthit: %lu\n",
&e->prenat_addr, &e->postnat_addr, NIPQUAD(e->prenat_addr), NIPQUAD(e->postnat_addr),
(int)(e->stamp - jiffies) / HZ, (e->stamp - jtimeout) / HZ); (int)(e->stamp - jiffies) / HZ, (e->stamp - jtimeout) / HZ);
return 0; return 0;
} }
@@ -609,6 +620,7 @@ static void __net_exit dnetmap_proc_net_exit(struct net *net)
{ {
proc_net_remove(net, "xt_DNETMAP"); proc_net_remove(net, "xt_DNETMAP");
} }
#else #else
static inline int dnetmap_proc_net_init(struct net *net) static inline int dnetmap_proc_net_init(struct net *net)
{ {
@@ -618,7 +630,6 @@ static inline int dnetmap_proc_net_init(struct net *net)
static inline void dnetmap_proc_net_exit(struct net *net) static inline void dnetmap_proc_net_exit(struct net *net)
{ {
} }
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
static int __net_init dnetmap_net_init(struct net *net) static int __net_init dnetmap_net_init(struct net *net)
@@ -626,6 +637,13 @@ static int __net_init dnetmap_net_init(struct net *net)
struct dnetmap_net *dnetmap_net = dnetmap_pernet(net); struct dnetmap_net *dnetmap_net = dnetmap_pernet(net);
int i; int i;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
dnetmap = kmalloc(sizeof(struct dnetmap_net),GFP_ATOMIC);
if (dnetmap == NULL)
return -ENOMEM;
dnetmap_net = dnetmap;
#endif
dnetmap_net->dnetmap_iphash = kmalloc(sizeof(struct list_head) * dnetmap_net->dnetmap_iphash = kmalloc(sizeof(struct list_head) *
hash_size * 2, GFP_ATOMIC); hash_size * 2, GFP_ATOMIC);
if (dnetmap_net->dnetmap_iphash == NULL) if (dnetmap_net->dnetmap_iphash == NULL)
@@ -643,14 +661,19 @@ static void __net_exit dnetmap_net_exit(struct net *net)
BUG_ON(!list_empty(&dnetmap_net->prefixes)); BUG_ON(!list_empty(&dnetmap_net->prefixes));
kfree(dnetmap_net->dnetmap_iphash); kfree(dnetmap_net->dnetmap_iphash);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
kfree(dnetmap_net);
#endif
dnetmap_proc_net_exit(net); dnetmap_proc_net_exit(net);
} }
static struct pernet_operations dnetmap_net_ops = { static struct pernet_operations dnetmap_net_ops = {
.init = dnetmap_net_init, .init = dnetmap_net_init,
.exit = dnetmap_net_exit, .exit = dnetmap_net_exit,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
.id = &dnetmap_net_id, .id = &dnetmap_net_id,
.size = sizeof(struct dnetmap_net), .size = sizeof(struct dnetmap_net),
#endif
}; };
static struct xt_target dnetmap_tg_reg __read_mostly = { static struct xt_target dnetmap_tg_reg __read_mostly = {

View File

@@ -29,6 +29,38 @@ static const char *const dir_names[] = {
"ORIGINAL", "REPLY", "ORIGINAL", "REPLY",
}; };
static void logmark_ct(const struct nf_conn *ct, enum ip_conntrack_info ctinfo)
{
bool prev = false;
printk(" ct=0x%p ctmark=0x%x ctstate=", ct, ct->mark);
ctinfo %= IP_CT_IS_REPLY;
if (ctinfo == IP_CT_NEW)
printk("NEW");
else if (ctinfo == IP_CT_ESTABLISHED)
printk("ESTABLISHED");
else if (ctinfo == IP_CT_RELATED)
printk("RELATED");
if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
printk(",SNAT");
if (test_bit(IPS_DST_NAT_BIT, &ct->status))
printk(",DNAT");
printk(" ctstatus=");
if (ct->status & IPS_EXPECTED) {
printk("EXPECTED");
prev = true;
}
if (ct->status & IPS_SEEN_REPLY)
printk("%s""SEEN_REPLY", prev++ ? "," : "");
if (ct->status & IPS_ASSURED)
printk("%s""ASSURED", prev++ ? "," : "");
if (ct->status & IPS_CONFIRMED)
printk("%s""CONFIRMED", prev++ ? "," : "");
printk(" lifetime=%lus",
(jiffies - ct->timeout.expires) / HZ);
}
static unsigned int static unsigned int
logmark_tg(struct sk_buff **pskb, const struct xt_action_param *par) logmark_tg(struct sk_buff **pskb, const struct xt_action_param *par)
{ {
@@ -36,7 +68,6 @@ logmark_tg(struct sk_buff **pskb, const struct xt_action_param *par)
const struct xt_logmark_tginfo *info = par->targinfo; const struct xt_logmark_tginfo *info = par->targinfo;
const struct nf_conn *ct; const struct nf_conn *ct;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
bool prev = false;
printk("<%u>%.*s""iif=%d hook=%s nfmark=0x%x " printk("<%u>%.*s""iif=%d hook=%s nfmark=0x%x "
"secmark=0x%x classify=0x%x", "secmark=0x%x classify=0x%x",
@@ -46,43 +77,17 @@ logmark_tg(struct sk_buff **pskb, const struct xt_action_param *par)
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
printk(" ctdir=%s", dir_names[ctinfo >= IP_CT_IS_REPLY]); printk(" ctdir=%s", dir_names[ctinfo >= IP_CT_IS_REPLY]);
if (ct == NULL) { if (ct == NULL)
printk(" ct=NULL ctmark=NULL ctstate=INVALID ctstatus=NONE"); printk(" ct=NULL ctmark=NULL ctstate=INVALID ctstatus=NONE");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
} else if (nf_ct_is_untracked(ct)) { else if (nf_ct_is_untracked(ct))
printk(" ct=UNTRACKED ctmark=NULL ctstate=UNTRACKED ctstatus=NONE"); printk(" ct=UNTRACKED ctmark=NULL ctstate=UNTRACKED ctstatus=NONE");
#else #else
} else if (ct == &nf_conntrack_untracked) { else if (ct == &nf_conntrack_untracked)
printk(" ct=UNTRACKED ctmark=NULL ctstate=UNTRACKED ctstatus=NONE"); printk(" ct=UNTRACKED ctmark=NULL ctstate=UNTRACKED ctstatus=NONE");
#endif #endif
} else { else
printk(" ct=0x%p ctmark=0x%x ctstate=", ct, ct->mark); logmark_ct(ct, ctinfo);
ctinfo %= IP_CT_IS_REPLY;
if (ctinfo == IP_CT_NEW)
printk("NEW");
else if (ctinfo == IP_CT_ESTABLISHED)
printk("ESTABLISHED");
else if (ctinfo == IP_CT_RELATED)
printk("RELATED");
if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
printk(",SNAT");
if (test_bit(IPS_DST_NAT_BIT, &ct->status))
printk(",DNAT");
printk(" ctstatus=");
if (ct->status & IPS_EXPECTED) {
printk("EXPECTED");
prev = true;
}
if (ct->status & IPS_SEEN_REPLY)
printk("%s""SEEN_REPLY", prev++ ? "," : "");
if (ct->status & IPS_ASSURED)
printk("%s""ASSURED", prev++ ? "," : "");
if (ct->status & IPS_CONFIRMED)
printk("%s""CONFIRMED", prev++ ? "," : "");
printk(" lifetime=%lus",
(jiffies - ct->timeout.expires) / HZ);
}
printk("\n"); printk("\n");
return XT_CONTINUE; return XT_CONTINUE;

View File

@@ -1,9 +1,11 @@
/* /*
* "SYSRQ" target extension for Netfilter * "SYSRQ" target extension for Xtables
* Copyright © Jan Engelhardt <jengelh [at] medozas de>, 2008 - 2010 * Copyright © Jan Engelhardt <jengelh [at] medozas de>, 2008 - 2010
* *
* Based upon the ipt_SYSRQ idea by Marek Zalem <marek [at] terminus sk> * Based upon the ipt_SYSRQ idea by Marek Zalem <marek [at] terminus sk>
* *
* Security additions John Haxby <john.haxby [at] oracle com>
*
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* version 2 or 3 as published by the Free Software Foundation. * version 2 or 3 as published by the Free Software Foundation.
@@ -58,13 +60,13 @@ static char *sysrq_hexdigest;
* is a series of sysrq requests; <seqno> is a sequence number that must be * is a series of sysrq requests; <seqno> is a sequence number that must be
* greater than the last sequence number; <salt> is some random bytes; and * greater than the last sequence number; <salt> is some random bytes; and
* <hash> is the hash of everything up to and including the preceding "," * <hash> is the hash of everything up to and including the preceding ","
* together with the password. * together with "<dstaddr>,<password>".
* *
* For example * For example
* *
* salt=$RANDOM * salt=$RANDOM
* req="s,$(date +%s),$salt" * req="s,$(date +%s),$salt"
* echo "$req,$(echo -n $req,secret | sha1sum | cut -c1-40)" * echo "$req,$(echo -n $req,10.10.25.1,secret | sha1sum | cut -c1-40)"
* *
* You will want a better salt and password than that though :-) * You will want a better salt and password than that though :-)
*/ */
@@ -121,7 +123,6 @@ static unsigned int sysrq_tg(const void *pdata, uint16_t len)
sg_init_table(sg, 2); sg_init_table(sg, 2);
#endif #endif
sg_set_buf(&sg[0], data, n); sg_set_buf(&sg[0], data, n);
strcpy(sysrq_digest_password, sysrq_password);
i = strlen(sysrq_digest_password); i = strlen(sysrq_digest_password);
sg_set_buf(&sg[1], sysrq_digest_password, i); sg_set_buf(&sg[1], sysrq_digest_password, i);
ret = crypto_hash_digest(&desc, sg, n + i, sysrq_digest); ret = crypto_hash_digest(&desc, sg, n + i, sysrq_digest);
@@ -223,6 +224,8 @@ sysrq_tg4(struct sk_buff **pskb, const struct xt_action_param *par)
": " NIPQUAD_FMT ":%u -> :%u len=%u\n", ": " NIPQUAD_FMT ":%u -> :%u len=%u\n",
NIPQUAD(iph->saddr), htons(udph->source), NIPQUAD(iph->saddr), htons(udph->source),
htons(udph->dest), len); htons(udph->dest), len);
sprintf(sysrq_digest_password, NIPQUAD_FMT ",%s",
NIPQUAD(iph->daddr), sysrq_password);
return sysrq_tg((void *)udph + sizeof(struct udphdr), len); return sysrq_tg((void *)udph + sizeof(struct udphdr), len);
} }
@@ -241,7 +244,8 @@ sysrq_tg6(struct sk_buff **pskb, const struct xt_action_param *par)
return NF_DROP; return NF_DROP;
iph = ipv6_hdr(skb); iph = ipv6_hdr(skb);
if (ipv6_find_hdr(skb, &th_off, IPPROTO_UDP, &frag_off) < 0 || if ((ipv6_find_hdr(skb, &th_off, IPPROTO_UDP, &frag_off) < 0 &&
ipv6_find_hdr(skb, &th_off, IPPROTO_UDPLITE, &frag_off) < 0) ||
frag_off > 0) frag_off > 0)
return NF_DROP; return NF_DROP;
@@ -253,7 +257,9 @@ sysrq_tg6(struct sk_buff **pskb, const struct xt_action_param *par)
": " NIP6_FMT ":%hu -> :%hu len=%u\n", ": " NIP6_FMT ":%hu -> :%hu len=%u\n",
NIP6(iph->saddr), ntohs(udph->source), NIP6(iph->saddr), ntohs(udph->source),
ntohs(udph->dest), len); ntohs(udph->dest), len);
return sysrq_tg(udph + sizeof(struct udphdr), len); sprintf(sysrq_digest_password, NIP6_FMT ",%s",
NIP6(iph->daddr), sysrq_password);
return sysrq_tg((void *)udph + sizeof(struct udphdr), len);
} }
#endif #endif
@@ -340,7 +346,9 @@ static int __init sysrq_crypto_init(void)
sysrq_hexdigest = kmalloc(2 * sysrq_digest_size + 1, GFP_KERNEL); sysrq_hexdigest = kmalloc(2 * sysrq_digest_size + 1, GFP_KERNEL);
if (sysrq_hexdigest == NULL) if (sysrq_hexdigest == NULL)
goto fail; goto fail;
sysrq_digest_password = kmalloc(sizeof(sysrq_password), GFP_KERNEL); sysrq_digest_password =
kmalloc(sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255") +
sizeof(sysrq_password), GFP_KERNEL);
if (sysrq_digest_password == NULL) if (sysrq_digest_password == NULL)
goto fail; goto fail;
do_gettimeofday(&now); do_gettimeofday(&now);
@@ -376,6 +384,7 @@ module_init(sysrq_tg_init);
module_exit(sysrq_tg_exit); module_exit(sysrq_tg_exit);
MODULE_DESCRIPTION("Xtables: triggering SYSRQ remotely"); MODULE_DESCRIPTION("Xtables: triggering SYSRQ remotely");
MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
MODULE_AUTHOR("John Haxby <john.haxby@oracle.com");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_SYSRQ"); MODULE_ALIAS("ipt_SYSRQ");
MODULE_ALIAS("ip6t_SYSRQ"); MODULE_ALIAS("ip6t_SYSRQ");

View File

@@ -13,4 +13,10 @@ config NETFILTER_XT_TARGET_TARPIT
This offers similar functionality to LaBrea This offers similar functionality to LaBrea
<http://www.hackbusters.net/LaBrea/>, but does not require dedicated <http://www.hackbusters.net/LaBrea/>, but does not require dedicated
hardware or IPs. Any TCP port that you would normally DROP or REJECT hardware or IPs. Any TCP port that you would normally DROP or REJECT
can instead become a tar pit. can instead become a tar pit or honeypot. All 3 modes may be used
in iptables rules interchangably and simultaneously.
A honeypot option is available which will answer connections normally
and allow the remote to send data packets that may be captured in a
pcap for later analysis. A reset mode is also available that will only
send an inline reset (RST).

View File

@@ -48,14 +48,17 @@
#include <net/route.h> #include <net/route.h>
#include <net/tcp.h> #include <net/tcp.h>
#include "compat_xtables.h" #include "compat_xtables.h"
#include "xt_TARPIT.h"
static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook) static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook,
unsigned int mode)
{ {
struct tcphdr _otcph, *oth, *tcph; struct tcphdr _otcph, *oth, *tcph;
unsigned int addr_type = RTN_UNSPEC; unsigned int addr_type = RTN_UNSPEC;
struct sk_buff *nskb; struct sk_buff *nskb;
const struct iphdr *oldhdr;
struct iphdr *niph; struct iphdr *niph;
u_int16_t tmp; uint16_t tmp, payload;
/* A truncated TCP header is not going to be useful */ /* A truncated TCP header is not going to be useful */
if (oldskb->len < ip_hdrlen(oldskb) + sizeof(struct tcphdr)) if (oldskb->len < ip_hdrlen(oldskb) + sizeof(struct tcphdr))
@@ -66,17 +69,6 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
if (oth == NULL) if (oth == NULL)
return; return;
/* No replies for RST, FIN or !SYN,!ACK */
if (oth->rst || oth->fin || (!oth->syn && !oth->ack))
return;
/* Rate-limit replies to !SYN,ACKs */
#if 0
if (!oth->syn && oth->ack)
if (!xrlim_allow(rt_dst(ort), HZ))
return;
#endif
/* Check checksum. */ /* Check checksum. */
if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
return; return;
@@ -102,6 +94,7 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
skb_shinfo(nskb)->gso_type = 0; skb_shinfo(nskb)->gso_type = 0;
#endif #endif
oldhdr = ip_hdr(oldskb);
tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb));
/* Swap source and dest */ /* Swap source and dest */
@@ -111,28 +104,94 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
tcph->source = tcph->dest; tcph->source = tcph->dest;
tcph->dest = tmp; tcph->dest = tmp;
/* Calculate payload size?? */
payload = nskb->len - ip_hdrlen(nskb) - sizeof(struct tcphdr);
/* Truncate to length (no data) */ /* Truncate to length (no data) */
tcph->doff = sizeof(struct tcphdr) / 4; tcph->doff = sizeof(struct tcphdr) / 4;
skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr));
niph->tot_len = htons(nskb->len); niph->tot_len = htons(nskb->len);
/* Use supplied sequence number or make a new one */
tcph->seq = oth->ack ? oth->ack_seq : 0;
/* Our SYN-ACKs must have a >0 window */
tcph->window = (oth->syn && !oth->ack) ? htons(5) : 0;
tcph->urg_ptr = 0; tcph->urg_ptr = 0;
/* Reset flags */ /* Reset flags */
((u_int8_t *)tcph)[13] = 0; ((u_int8_t *)tcph)[13] = 0;
if (oth->syn && oth->ack) { if (mode == XTTARPIT_TARPIT) {
/* No replies for RST, FIN or !SYN,!ACK */
if (oth->rst || oth->fin || (!oth->syn && !oth->ack))
return;
tcph->seq = oth->ack ? oth->ack_seq : 0;
/* Our SYN-ACKs must have a >0 window */
tcph->window = (oth->syn && !oth->ack) ? htons(5) : 0;
if (oth->syn && oth->ack) {
tcph->rst = true;
tcph->ack_seq = false;
} else {
tcph->syn = oth->syn;
tcph->ack = true;
tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn);
}
#if 0
/* Rate-limit replies to !SYN,ACKs */
if (!oth->syn && oth->ack)
if (!xrlim_allow(rt_dst(ort), HZ))
return;
#endif
} else if (mode == XTTARPIT_HONEYPOT) {
/* Do not answer any resets regardless of combination */
if (oth->rst || oth->seq == 0xDEADBEEF)
return;
/* Send a reset to scanners. They like that. */
if (oth->syn && oth->ack) {
tcph->window = 0;
tcph->ack = false;
tcph->psh = true;
tcph->ack_seq = 0xdeadbeef; /* see if they ack it */
tcph->seq = oth->ack_seq;
tcph->rst = true;
}
/* SYN > SYN-ACK */
if (oth->syn && !oth->ack) {
tcph->syn = true;
tcph->ack = true;
tcph->window = oth->window &
((net_random() & 0x1f) - 0xf);
tcph->seq = htonl(net_random() & ~oth->seq);
tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn);
}
/* ACK > ACK */
if (oth->ack && (!(oth->fin || oth->syn))) {
tcph->syn = false;
tcph->ack = true;
tcph->window = oth->window &
((net_random() & 0x1f) - 0xf);
tcph->ack_seq = payload > 100 ?
htonl(ntohl(oth->seq) + payload) :
oth->seq;
tcph->seq = oth->ack_seq;
}
/*
* FIN > RST.
* We cannot terminate gracefully so just be abrupt.
*/
if (oth->fin) {
tcph->window = 0;
tcph->seq = oth->ack_seq;
tcph->ack_seq = oth->ack_seq;
tcph->fin = false;
tcph->ack = false;
tcph->rst = true;
}
} else if (mode == XTTARPIT_RESET) {
tcph->window = 0;
tcph->ack = false;
tcph->syn = false;
tcph->rst = true; tcph->rst = true;
tcph->ack_seq = false; tcph->seq = oth->ack_seq;
} else { tcph->ack_seq = oth->seq;
tcph->syn = oth->syn;
tcph->ack = 1;
tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn);
} }
/* Adjust TCP checksum */ /* Adjust TCP checksum */
@@ -149,7 +208,10 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
/* Set DF, id = 0 */ /* Set DF, id = 0 */
niph->frag_off = htons(IP_DF); niph->frag_off = htons(IP_DF);
niph->id = 0; if (mode == XTTARPIT_TARPIT || mode == XTTARPIT_RESET)
niph->id = 0;
else if (mode == XTTARPIT_HONEYPOT)
niph->id = ~oldhdr->id + 1;
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL && if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL &&
@@ -167,7 +229,14 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
nskb->ip_summed = CHECKSUM_NONE; nskb->ip_summed = CHECKSUM_NONE;
/* Adjust IP TTL */ /* Adjust IP TTL */
niph->ttl = dst_metric(skb_dst(nskb), RTAX_HOPLIMIT); if (mode == XTTARPIT_HONEYPOT)
niph->ttl = 128;
else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
niph->ttl = ip4_dst_hoplimit(skb_dst(nskb));
#else
niph->ttl = dst_metric(skb_dst(nskb), RTAX_HOPLIMIT);
#endif
/* Adjust IP checksum */ /* Adjust IP checksum */
niph->check = 0; niph->check = 0;
@@ -193,6 +262,7 @@ tarpit_tg(struct sk_buff **pskb, const struct xt_action_param *par)
const struct sk_buff *skb = *pskb; const struct sk_buff *skb = *pskb;
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
const struct rtable *rt = skb_rtable(skb); const struct rtable *rt = skb_rtable(skb);
const struct xt_tarpit_tginfo *info = par->targinfo;
/* Do we have an input route cache entry? (Not in PREROUTING.) */ /* Do we have an input route cache entry? (Not in PREROUTING.) */
if (rt == NULL) if (rt == NULL)
@@ -218,19 +288,19 @@ tarpit_tg(struct sk_buff **pskb, const struct xt_action_param *par)
if (iph->frag_off & htons(IP_OFFSET)) if (iph->frag_off & htons(IP_OFFSET))
return NF_DROP; return NF_DROP;
tarpit_tcp(*pskb, par->hooknum); tarpit_tcp(*pskb, par->hooknum, info->variant);
return NF_DROP; return NF_DROP;
} }
static struct xt_target tarpit_tg_reg __read_mostly = { static struct xt_target tarpit_tg_reg __read_mostly = {
.name = "TARPIT", .name = "TARPIT",
.revision = 0, .revision = 0,
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.table = "filter", .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD),
.hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD), .proto = IPPROTO_TCP,
.proto = IPPROTO_TCP, .target = tarpit_tg,
.target = tarpit_tg, .targetsize = sizeof(struct xt_tarpit_tginfo),
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
static int __init tarpit_tg_init(void) static int __init tarpit_tg_init(void)

14
extensions/xt_TARPIT.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef _LINUX_NETFILTER_XT_TARPIT_H
#define _LINUX_NETFILTER_XT_TARPIT_H 1
enum xt_tarpit_target_variant {
XTTARPIT_TARPIT,
XTTARPIT_HONEYPOT,
XTTARPIT_RESET,
};
struct xt_tarpit_tginfo {
uint8_t variant;
};
#endif /* _LINUX_NETFILTER_XT_TARPIT_H */

View File

@@ -14,6 +14,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/route.h> #include <linux/route.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/version.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/ip.h> #include <net/ip.h>
@@ -21,6 +22,13 @@
#include <net/route.h> #include <net/route.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
# error ----------------------------------------------------------
# error This module has been merged into, and is available in the
# error mainline since Linux kernel v2.6.35. Please use that.
# error ----------------------------------------------------------
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
# define WITH_CONNTRACK 1 # define WITH_CONNTRACK 1
# include <net/netfilter/nf_conntrack.h> # include <net/netfilter/nf_conntrack.h>

View File

@@ -167,16 +167,20 @@ static bool geoip_bsearch6(const struct geoip_subnet6 *range,
{ {
int mid; int mid;
if (hi <= lo) while (true) {
return false; if (hi <= lo)
mid = (lo + hi) / 2; return false;
if (ipv6_cmp(&range[mid].begin, addr) <= 0 && mid = (lo + hi) / 2;
ipv6_cmp(addr, &range[mid].end) <= 0) if (ipv6_cmp(&range[mid].begin, addr) <= 0 &&
return true; ipv6_cmp(addr, &range[mid].end) <= 0)
if (ipv6_cmp(&range[mid].begin, addr) > 0) return true;
return geoip_bsearch6(range, addr, lo, mid); if (ipv6_cmp(&range[mid].begin, addr) > 0)
else if (ipv6_cmp(&range[mid].end, addr) < 0) hi = mid;
return geoip_bsearch6(range, addr, mid + 1, hi); else if (ipv6_cmp(&range[mid].end, addr) < 0)
lo = mid + 1;
else
break;
}
WARN_ON(true); WARN_ON(true);
return false; return false;
@@ -218,15 +222,19 @@ static bool geoip_bsearch4(const struct geoip_subnet4 *range,
{ {
int mid; int mid;
if (hi <= lo) while (true) {
return false; if (hi <= lo)
mid = (lo + hi) / 2; return false;
if (range[mid].begin <= addr && addr <= range[mid].end) mid = (lo + hi) / 2;
return true; if (range[mid].begin <= addr && addr <= range[mid].end)
if (range[mid].begin > addr) return true;
return geoip_bsearch4(range, addr, lo, mid); if (range[mid].begin > addr)
else if (range[mid].end < addr) hi = mid;
return geoip_bsearch4(range, addr, mid + 1, hi); else if (range[mid].end < addr)
lo = mid + 1;
else
break;
}
WARN_ON(true); WARN_ON(true);
return false; return false;

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