ipset: import ipset 5.2+GENL

This requires Linux 2.6.35 or newer to build, so it is deactivated by
default in the "mconfig" file.
This commit is contained in:
Jan Engelhardt
2011-01-04 03:57:21 +01:00
parent 75212f3972
commit 7d8ffffd85
75 changed files with 21736 additions and 1 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
*.loT *.loT
*.o *.o
.deps .deps
.dirstamp
.libs .libs
Makefile Makefile
Makefile.in Makefile.in

View File

@@ -25,6 +25,7 @@ AC_CHECK_HEADERS([linux/netfilter/x_tables.h], [],
[AC_MSG_ERROR([You need to have linux/netfilter/x_tables.h, see INSTALL file for details])]) [AC_MSG_ERROR([You need to have linux/netfilter/x_tables.h, see INSTALL file for details])])
PKG_CHECK_MODULES([libxtables], [xtables >= 1.4.3]) PKG_CHECK_MODULES([libxtables], [xtables >= 1.4.3])
xtlibdir="$(pkg-config --variable=xtlibdir xtables)" xtlibdir="$(pkg-config --variable=xtlibdir xtables)"
PKG_CHECK_MODULES([libmnl], [libmnl >= 1])
AC_ARG_WITH([xtlibdir], AC_ARG_WITH([xtlibdir],
AS_HELP_STRING([--with-xtlibdir=PATH], AS_HELP_STRING([--with-xtlibdir=PATH],
@@ -86,5 +87,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/pknock/Makefile]) extensions/ipset-4/Makefile extensions/ipset-5/Makefile
extensions/pknock/Makefile])
AC_OUTPUT AC_OUTPUT

View File

@@ -4,6 +4,9 @@ HEAD
Fixes: Fixes:
- Update to ipset 4.5 - Update to ipset 4.5
* the iptreemap type used wrong gfp flags when deleting entries * the iptreemap type used wrong gfp flags when deleting entries
- Include ipset 5.2 with genetlink patch (beta)
* no kernel patch needed, but requires Linux >= 2.6.35
and thus needs to be manually enabled in mconfig
v1.31 (2010-11-05) v1.31 (2010-11-05)

View File

@@ -27,6 +27,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_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

@@ -19,6 +19,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_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

1
extensions/ipset-5/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/ipset

View File

@@ -0,0 +1,7 @@
# -*- Makefile -*-
obj-m += xt_set.o
obj-m += ip_set.o ip_set_bitmap_ip.o ip_set_bitmap_ipmac.o
obj-m += ip_set_bitmap_port.o ip_set_hash_ip.o ip_set_hash_ipport.o
obj-m += ip_set_hash_ipportip.o ip_set_hash_ipportnet.o ip_set_hash_net.o
obj-m += ip_set_hash_netport.o ip_set_list_set.o

View File

@@ -0,0 +1,23 @@
# -*- Makefile -*-
AM_CFLAGS = ${regular_CFLAGS} ${libmnl_CFLAGS} -Iinclude
include ../../Makefile.extra
lib_LTLIBRARIES = libipset.la
libipset_la_SOURCES = libipset/data.c libipset/icmp.c libipset/icmpv6.c \
libipset/mnl.c libipset/parse.c libipset/print.c \
libipset/session.c libipset/types.c
libipset_la_LIBADD = ${libmnl_LIBS}
libipset_la_LDFLAGS = -version-info 1:0:0
sbin_PROGRAMS = ipset
ipset_SOURCES = src/ipset.c src/errcode.c src/ui.c src/ipset_bitmap_ip.c \
src/ipset_bitmap_ipmac.c src/ipset_bitmap_port.c \
src/ipset_hash_ip.c src/ipset_hash_ipport.c \
src/ipset_hash_ipportip.c src/ipset_hash_ipportnet.c \
src/ipset_hash_net.c src/ipset_hash_netport.c \
src/ipset_list_set.c
ipset_LDADD = libipset.la
man_MANS = ipset.8

View File

@@ -0,0 +1,2 @@
# -*- Makefile -*-

View File

@@ -0,0 +1,133 @@
/* 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.
*/
#ifndef LIBIPSET_DATA_H
#define LIBIPSET_DATA_H
#include <stdbool.h> /* bool */
#include <libipset/nf_inet_addr.h> /* union nf_inet_addr */
/* Data options */
enum ipset_opt {
IPSET_OPT_NONE = 0,
/* Common ones */
IPSET_SETNAME,
IPSET_OPT_TYPENAME,
IPSET_OPT_FAMILY,
/* CADT options */
IPSET_OPT_IP,
IPSET_OPT_IP_FROM = IPSET_OPT_IP,
IPSET_OPT_IP_TO,
IPSET_OPT_CIDR,
IPSET_OPT_PORT,
IPSET_OPT_PORT_FROM = IPSET_OPT_PORT,
IPSET_OPT_PORT_TO,
IPSET_OPT_TIMEOUT,
/* Create-specific options */
IPSET_OPT_GC,
IPSET_OPT_HASHSIZE,
IPSET_OPT_MAXELEM,
IPSET_OPT_NETMASK,
IPSET_OPT_PROBES,
IPSET_OPT_RESIZE,
IPSET_OPT_SIZE,
/* Create-specific options, filled out by the kernel */
IPSET_OPT_ELEMENTS,
IPSET_OPT_REFERENCES,
IPSET_OPT_MEMSIZE,
/* ADT-specific options */
IPSET_OPT_ETHER,
IPSET_OPT_NAME,
IPSET_OPT_NAMEREF,
IPSET_OPT_IP2,
IPSET_OPT_CIDR2,
IPSET_OPT_PROTO,
/* Swap/rename to */
IPSET_OPT_SETNAME2,
/* Flags */
IPSET_OPT_EXIST,
IPSET_OPT_BEFORE,
/* Internal options */
IPSET_OPT_FLAGS = 48, /* IPSET_FLAG_EXIST| */
IPSET_OPT_CADT_FLAGS, /* IPSET_FLAG_BEFORE| */
IPSET_OPT_ELEM,
IPSET_OPT_TYPE,
IPSET_OPT_LINENO,
IPSET_OPT_REVISION,
IPSET_OPT_REVISION_MIN,
IPSET_OPT_MAX,
};
#define IPSET_FLAG(opt) (1LL << (opt))
#define IPSET_FLAGS_ALL (~0LL)
#define IPSET_CREATE_FLAGS \
( IPSET_FLAG(IPSET_OPT_FAMILY) \
| IPSET_FLAG(IPSET_OPT_TYPENAME)\
| IPSET_FLAG(IPSET_OPT_TYPE) \
| IPSET_FLAG(IPSET_OPT_IP) \
| IPSET_FLAG(IPSET_OPT_IP_TO) \
| IPSET_FLAG(IPSET_OPT_CIDR) \
| IPSET_FLAG(IPSET_OPT_PORT) \
| IPSET_FLAG(IPSET_OPT_PORT_TO) \
| IPSET_FLAG(IPSET_OPT_TIMEOUT) \
| IPSET_FLAG(IPSET_OPT_GC) \
| IPSET_FLAG(IPSET_OPT_HASHSIZE)\
| IPSET_FLAG(IPSET_OPT_MAXELEM) \
| IPSET_FLAG(IPSET_OPT_NETMASK) \
| IPSET_FLAG(IPSET_OPT_PROBES) \
| IPSET_FLAG(IPSET_OPT_RESIZE) \
| IPSET_FLAG(IPSET_OPT_SIZE))
#define IPSET_ADT_FLAGS \
( IPSET_FLAG(IPSET_OPT_IP) \
| IPSET_FLAG(IPSET_OPT_IP_TO) \
| IPSET_FLAG(IPSET_OPT_CIDR) \
| IPSET_FLAG(IPSET_OPT_PORT) \
| IPSET_FLAG(IPSET_OPT_PORT_TO) \
| IPSET_FLAG(IPSET_OPT_TIMEOUT) \
| IPSET_FLAG(IPSET_OPT_ETHER) \
| IPSET_FLAG(IPSET_OPT_NAME) \
| IPSET_FLAG(IPSET_OPT_NAMEREF) \
| IPSET_FLAG(IPSET_OPT_IP2) \
| IPSET_FLAG(IPSET_OPT_CIDR2) \
| IPSET_FLAG(IPSET_OPT_PROTO) \
| IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\
| IPSET_FLAG(IPSET_OPT_BEFORE))
struct ipset_data;
extern void ipset_strlcpy(char *dst, const char *src, size_t len);
extern bool ipset_data_flags_test(const struct ipset_data *data,
uint64_t flags);
extern void ipset_data_flags_set(struct ipset_data *data, uint64_t flags);
extern void ipset_data_flags_unset(struct ipset_data *data, uint64_t flags);
extern bool ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt);
extern int ipset_data_set(struct ipset_data *data, enum ipset_opt opt,
const void *value);
extern const void * ipset_data_get(const struct ipset_data *data,
enum ipset_opt opt);
static inline bool
ipset_data_test(const struct ipset_data *data, enum ipset_opt opt)
{
return ipset_data_flags_test(data, IPSET_FLAG(opt));
}
/* Shortcuts */
extern const char * ipset_data_setname(const struct ipset_data *data);
extern uint8_t ipset_data_family(const struct ipset_data *data);
extern uint8_t ipset_data_cidr(const struct ipset_data *data);
extern uint64_t ipset_data_flags(const struct ipset_data *data);
extern void ipset_data_reset(struct ipset_data *data);
extern struct ipset_data * ipset_data_init(void);
extern void ipset_data_fini(struct ipset_data *data);
extern size_t ipset_data_sizeof(enum ipset_opt opt, uint8_t family);
#endif /* LIBIPSET_DATA_H */

View File

@@ -0,0 +1,33 @@
/* 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.
*/
#ifndef LIBIPSET_DEBUG_H
#define LIBIPSET_DEBUG_H
#ifdef IPSET_DEBUG
#include <stdio.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#define D(fmt, args...) \
fprintf(stderr, "%s: %s: " fmt "\n", __FILE__, __FUNCTION__ , ## args)
#define IF_D(test, fmt, args...) \
if (test) \
D(fmt , ## args)
static inline void
dump_nla(struct nlattr *nla[], int maxlen)
{
int i;
for (i = 0; i < maxlen; i++)
D("nla[%u] does%s exist", i, nla[i] ? "" : " NOT");
}
#else
#define D(fmt, args...)
#define IF_D(test, fmt, args...)
#define dump_nla(nla, maxlen)
#endif
#endif /* LIBIPSET_DEBUG_H */

View File

@@ -0,0 +1,24 @@
/* Copyright 2007-2008 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.
*/
#ifndef LIBIPSET_ERRCODE_H
#define LIBIPSET_ERRCODE_H
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
struct ipset_session;
/* Kernel error code to message table */
struct ipset_errcode_table {
int errcode; /* error code returned by the kernel */
enum ipset_cmd cmd; /* issued command */
const char *message; /* error message the code translated to */
};
extern int ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd,
int errcode);
#endif /* LIBIPSET_ERRCODE_H */

View File

@@ -0,0 +1,16 @@
/* 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.
*/
#ifndef LIBIPSET_ICMP_H
#define LIBIPSET_ICMP_H
#include <stdint.h> /* uintxx_t */
extern const char * id_to_icmp(uint8_t id);
extern const char * icmp_to_name(uint8_t type, uint8_t code);
extern int name_to_icmp(const char *str, uint16_t *typecode);
#endif /* LIBIPSET_ICMP_H */

View File

@@ -0,0 +1,16 @@
/* 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.
*/
#ifndef LIBIPSET_ICMPV6_H
#define LIBIPSET_ICMPV6_H
#include <stdint.h> /* uintxx_t */
extern const char * id_to_icmpv6(uint8_t id);
extern const char * icmpv6_to_name(uint8_t type, uint8_t code);
extern int name_to_icmpv6(const char *str, uint16_t *typecode);
#endif /* LIBIPSET_ICMPV6_H */

View File

@@ -0,0 +1,163 @@
#ifndef _IP_SET_H
#define _IP_SET_H
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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.
*/
/* The protocol version */
#define IPSET_PROTOCOL 5
/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
/* Message types and commands */
enum ipset_cmd {
IPSET_CMD_NONE,
IPSET_CMD_PROTOCOL, /* 1: Return protocol version */
IPSET_CMD_CREATE, /* 2: Create a new (empty) set */
IPSET_CMD_DESTROY, /* 3: Destroy a (empty) set */
IPSET_CMD_FLUSH, /* 4: Remove all elements from a set */
IPSET_CMD_RENAME, /* 5: Rename a set */
IPSET_CMD_SWAP, /* 6: Swap two sets */
IPSET_CMD_LIST, /* 7: List sets */
IPSET_CMD_SAVE, /* 8: Save sets */
IPSET_CMD_ADD, /* 9: Add an element to a set */
IPSET_CMD_DEL, /* 10: Delete an element from a set */
IPSET_CMD_TEST, /* 11: Test an element in a set */
IPSET_CMD_HEADER, /* 12: Get set header data only */
IPSET_CMD_TYPE, /* 13: Get set type */
IPSET_MSG_MAX, /* Netlink message commands */
/* Commands in userspace: */
IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */
IPSET_CMD_HELP, /* 15: Get help */
IPSET_CMD_VERSION, /* 16: Get program version */
IPSET_CMD_QUIT, /* 17: Quit from interactive mode */
IPSET_CMD_MAX,
IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */
};
/* Attributes at command level */
enum {
IPSET_ATTR_UNSPEC,
IPSET_ATTR_PROTOCOL, /* 1: Protocol version */
IPSET_ATTR_SETNAME, /* 2: Name of the set */
IPSET_ATTR_TYPENAME, /* 3: Typename */
IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* Setname at rename/swap */
IPSET_ATTR_REVISION, /* 4: Settype revision */
IPSET_ATTR_FAMILY, /* 5: Settype family */
IPSET_ATTR_FLAGS, /* 6: Flags at command level */
IPSET_ATTR_DATA, /* 7: Nested attributes */
IPSET_ATTR_ADT, /* 8: Multiple data containers */
IPSET_ATTR_LINENO, /* 9: Restore lineno */
IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */
IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
__IPSET_ATTR_CMD_MAX,
};
#define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1)
/* CADT specific attributes */
enum {
IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1,
IPSET_ATTR_IP_FROM = IPSET_ATTR_IP,
IPSET_ATTR_IP_TO, /* 2 */
IPSET_ATTR_CIDR, /* 3 */
IPSET_ATTR_PORT, /* 4 */
IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
IPSET_ATTR_PORT_TO, /* 5 */
IPSET_ATTR_TIMEOUT, /* 6 */
IPSET_ATTR_PROTO, /* 7 */
IPSET_ATTR_CADT_FLAGS, /* 8 */
IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
IPSET_ATTR_GC,
IPSET_ATTR_HASHSIZE,
IPSET_ATTR_MAXELEM,
IPSET_ATTR_NETMASK,
IPSET_ATTR_PROBES,
IPSET_ATTR_RESIZE,
IPSET_ATTR_SIZE,
/* Kernel-only */
IPSET_ATTR_ELEMENTS,
IPSET_ATTR_REFERENCES,
IPSET_ATTR_MEMSIZE,
__IPSET_ATTR_CREATE_MAX,
};
#define IPSET_ATTR_CREATE_MAX (__IPSET_ATTR_CREATE_MAX - 1)
/* ADT specific attributes */
enum {
IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + 1,
IPSET_ATTR_NAME,
IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
/* IP specific attributes */
enum {
IPSET_ATTR_IPADDR_IPV4 = IPSET_ATTR_UNSPEC + 1,
IPSET_ATTR_IPADDR_IPV6,
__IPSET_ATTR_IPADDR_MAX,
};
#define IPSET_ATTR_IPADDR_MAX (__IPSET_ATTR_IPADDR_MAX - 1)
/* Error codes */
enum ipset_errno {
IPSET_ERR_PRIVATE = 128,
IPSET_ERR_PROTOCOL,
IPSET_ERR_FIND_TYPE,
IPSET_ERR_MAX_SETS,
IPSET_ERR_BUSY,
IPSET_ERR_EXIST_SETNAME2,
IPSET_ERR_TYPE_MISMATCH,
IPSET_ERR_EXIST,
IPSET_ERR_INVALID_CIDR,
IPSET_ERR_INVALID_NETMASK,
IPSET_ERR_INVALID_FAMILY,
IPSET_ERR_TIMEOUT,
IPSET_ERR_REFERENCED,
IPSET_ERR_IPADDR_IPV4,
IPSET_ERR_IPADDR_IPV6,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 160,
};
/* Flags at command level */
enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
};
/* Flags at CADT attribute level */
enum ipset_cadt_flags {
IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE),
};
/* Commands with settype-specific attributes */
enum ipset_adt {
IPSET_ADD,
IPSET_DEL,
IPSET_TEST,
IPSET_ADT_MAX,
IPSET_CREATE = IPSET_ADT_MAX,
IPSET_CADT_MAX,
};
#endif /* __IP_SET_H */

View File

@@ -0,0 +1,12 @@
#ifndef __IP_SET_BITMAP_H
#define __IP_SET_BITMAP_H
/* Bitmap type specific error codes */
enum {
/* The element is out of the range of the set */
IPSET_ERR_BITMAP_RANGE = IPSET_ERR_TYPE_SPECIFIC,
/* The range exceeds the size limit of the set type */
IPSET_ERR_BITMAP_RANGE_SIZE,
};
#endif /* __IP_SET_BITMAP_H */

View File

@@ -0,0 +1,16 @@
#ifndef __IP_SET_HASH_H
#define __IP_SET_HASH_H
/* Hash type specific error codes */
enum {
/* Hash is full */
IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC,
/* Null-valued element */
IPSET_ERR_HASH_ELEM,
/* Invalid protocol */
IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO,
};
#endif /* __IP_SET_HASH_H */

View File

@@ -0,0 +1,20 @@
#ifndef __IP_SET_LIST_H
#define __IP_SET_LIST_H
/* List type specific error codes */
enum {
/* Set name to be added/deleted/tested does not exist. */
IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC,
/* list:set type is not permitted to add */
IPSET_ERR_LOOP,
/* Missing reference set */
IPSET_ERR_BEFORE,
/* Reference set does not exist */
IPSET_ERR_NAMEREF,
/* Set is full */
IPSET_ERR_LIST_FULL,
/* Reference set is not added to the set */
IPSET_ERR_REF_EXIST,
};
#endif /* __IP_SET_LIST_H */

View File

@@ -0,0 +1,29 @@
/* 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.
*/
#ifndef LIBIPSET_MNL_H
#define LIBIPSET_MNL_H
#include <stdint.h> /* uintxx_t */
#include <libmnl/libmnl.h> /* libmnl backend */
#include <libipset/transport.h> /* struct ipset_transport */
#ifndef NFNETLINK_V0
#define NFNETLINK_V0 0
struct nfgenmsg {
uint8_t nfgen_family;
uint8_t version;
uint16_t res_id;
};
#endif
extern int ipset_get_nlmsg_type(const struct nlmsghdr *nlh);
extern const struct ipset_transport ipset_mnl_transport;
#endif /* LIBIPSET_MNL_H */

View File

@@ -0,0 +1,22 @@
/* 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.
*/
#ifndef LIBIPSET_NF_INET_ADDR_H
#define LIBIPSET_NF_INET_ADDR_H
#include <stdint.h> /* uint32_t */
#include <netinet/in.h> /* struct in[6]_addr */
/* The structure to hold IP addresses, same as in linux/netfilter.h */
union nf_inet_addr {
uint32_t all[4];
uint32_t ip;
uint32_t ip6[4];
struct in_addr in;
struct in6_addr in6;
};
#endif /* LIBIPSET_NF_INET_ADDR_H */

View File

@@ -0,0 +1,96 @@
/* 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.
*/
#ifndef LIBIPSET_PARSE_H
#define LIBIPSET_PARSE_H
#include <libipset/data.h> /* enum ipset_opt */
/* For parsing/printing data */
#define IPSET_CIDR_SEPARATOR "/"
#define IPSET_RANGE_SEPARATOR "-"
#define IPSET_ELEM_SEPARATOR ","
#define IPSET_NAME_SEPARATOR ","
#define IPSET_PROTO_SEPARATOR ":"
struct ipset_session;
typedef int (*ipset_parsefn)(struct ipset_session *s,
enum ipset_opt opt, const char *str);
extern int ipset_parse_ether(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_port(struct ipset_session *session,
enum ipset_opt opt, const char *str,
const char *proto);
extern int ipset_parse_tcpudp_port(struct ipset_session *session,
enum ipset_opt opt, const char *str,
const char *proto);
extern int ipset_parse_tcp_port(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_single_tcp_port(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_proto(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_icmp(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_icmpv6(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_proto_port(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_family(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_single_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_net(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_range(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_netrange(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_iprange(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_ipnet(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_ip4_single6(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_name(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_before(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_after(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_setname(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_uint32(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_uint8(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_netmask(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_flag(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_typename(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_output(struct ipset_session *session,
int opt, const char *str);
extern int ipset_parse_ignored(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_elem(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_call_parser(struct ipset_session *session,
ipset_parsefn parse, const char *optstr,
enum ipset_opt optional, const char *str);
/* Compatibility parser functions */
extern int ipset_parse_iptimeout(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_name_compat(struct ipset_session *session,
enum ipset_opt opt, const char *str);
#endif /* LIBIPSET_PARSE_H */

View File

@@ -0,0 +1,157 @@
#ifndef _NET_PFXLEN_H
#define _NET_PFXLEN_H 1
#include <asm/byteorder.h>
#ifdef HAVE_PFXLEN_H
#include <linux/netfilter/pfxlen.h>
#else
#include <libipset/nf_inet_addr.h> /* union nf_inet_addr */
#define E(a, b, c, d) \
{.ip6 = { \
__constant_htonl(a), __constant_htonl(b), \
__constant_htonl(c), __constant_htonl(d), \
}}
/*
* This table works for both IPv4 and IPv6;
* just use prefixlen_netmask_map[prefixlength].ip.
*/
const union nf_inet_addr prefixlen_netmask_map[] = {
E(0x00000000, 0x00000000, 0x00000000, 0x00000000),
E(0x80000000, 0x00000000, 0x00000000, 0x00000000),
E(0xC0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xE0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xF0000000, 0x00000000, 0x00000000, 0x00000000),
E(0xF8000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFC000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFE000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFF000000, 0x00000000, 0x00000000, 0x00000000),
E(0xFF800000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE),
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF),
};
#endif /* !HAVE_PFXLEN_H */
#define PFXLEN(n) prefixlen_netmask_map[n].ip
#define PFXLEN6(n) prefixlen_netmask_map[n].ip6
#endif

View File

@@ -0,0 +1,65 @@
/* 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.
*/
#ifndef LIBIPSET_PRINT_H
#define LIBIPSET_PRINT_H
#include <libipset/data.h> /* enum ipset_opt */
typedef int (*ipset_printfn)(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_ether(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_family(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_type(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_ip(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_ipaddr(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_number(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_name(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_port(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_proto(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_icmp(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_icmpv6(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_proto_port(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_flag(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
extern int ipset_print_elem(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
#define ipset_print_portnum ipset_print_number
extern int ipset_print_data(char *buf, unsigned int len,
const struct ipset_data *data,
enum ipset_opt opt, uint8_t env);
#endif /* LIBIPSET_PRINT_H */

View File

@@ -0,0 +1,94 @@
/* 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.
*/
#ifndef LIBIPSET_SESSION_H
#define LIBIPSET_SESSION_H
#include <stdbool.h> /* bool */
#include <stdint.h> /* uintxx_t */
#include <stdio.h> /* printf */
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
/* Report and output buffer sizes */
#define IPSET_ERRORBUFLEN 1024
#define IPSET_OUTBUFLEN 8192
struct ipset_session;
struct ipset_data;
struct ipset_handle;
extern struct ipset_data * ipset_session_data(const struct ipset_session *session);
extern struct ipset_handle * ipset_session_handle(const struct ipset_session *session);
extern const struct ipset_type * ipset_saved_type(const struct ipset_session *session);
enum ipset_err_type {
IPSET_ERROR,
IPSET_WARNING,
};
extern int ipset_session_report(struct ipset_session *session,
enum ipset_err_type type,
const char *fmt, ...);
#define ipset_err(session, fmt, args...) \
ipset_session_report(session, IPSET_ERROR, fmt , ## args)
#define ipset_warn(session, fmt, args...) \
ipset_session_report(session, IPSET_WARNING, fmt , ## args)
#define ipset_errptr(session, fmt, args...) ({ \
ipset_session_report(session, IPSET_ERROR, fmt , ## args); \
NULL; \
})
extern void ipset_session_report_reset(struct ipset_session *session);
extern const char * ipset_session_error(const struct ipset_session *session);
extern const char * ipset_session_warning(const struct ipset_session *session);
#define ipset_session_data_set(session, opt, value) \
ipset_data_set(ipset_session_data(session), opt, value)
#define ipset_session_data_get(session, opt) \
ipset_data_get(ipset_session_data(session), opt)
/* Environment option flags */
enum ipset_envopt {
IPSET_ENV_BIT_SORTED = 0,
IPSET_ENV_SORTED = (1 << IPSET_ENV_BIT_SORTED),
IPSET_ENV_BIT_QUIET = 1,
IPSET_ENV_QUIET = (1 << IPSET_ENV_BIT_QUIET),
IPSET_ENV_BIT_RESOLVE = 2,
IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE),
IPSET_ENV_BIT_EXIST = 3,
IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST),
};
extern int ipset_envopt_parse(struct ipset_session *session,
int env, const char *str);
extern bool ipset_envopt_test(struct ipset_session *session,
enum ipset_envopt env);
enum ipset_output_mode {
IPSET_LIST_NONE,
IPSET_LIST_PLAIN,
IPSET_LIST_SAVE,
IPSET_LIST_XML,
};
extern int ipset_session_output(struct ipset_session *session,
enum ipset_output_mode mode);
extern int ipset_commit(struct ipset_session *session);
extern int ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd,
uint32_t lineno);
typedef int (*ipset_outfn)(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
extern struct ipset_session * ipset_session_init(ipset_outfn outfn);
extern int ipset_session_fini(struct ipset_session *session);
#endif /* LIBIPSET_SESSION_H */

View File

@@ -0,0 +1,27 @@
/* 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.
*/
#ifndef LIBIPSET_TRANSPORT_H
#define LIBIPSET_TRANSPORT_H
#include <stdint.h> /* uintxx_t */
#include <linux/netlink.h> /* struct nlmsghdr */
#include <libmnl/libmnl.h> /* mnl_cb_t */
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
struct ipset_handle;
struct ipset_transport {
struct ipset_handle * (*init)(mnl_cb_t *cb_ctl, void *data);
int (*fini)(struct ipset_handle *handle);
void (*fill_hdr)(struct ipset_handle *handle, enum ipset_cmd cmd,
void *buffer, size_t len, uint8_t envflags);
int (*query)(struct ipset_handle *handle, void *buffer, size_t len);
};
#endif /* LIBIPSET_TRANSPORT_H */

View File

@@ -0,0 +1,110 @@
/* 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.
*/
#ifndef LIBIPSET_TYPES_H
#define LIBIPSET_TYPES_H
#include <stddef.h> /* NULL */
#include <stdint.h> /* uintxx_t */
#include <libipset/data.h> /* enum ipset_opt */
#include <libipset/parse.h> /* ipset_parsefn */
#include <libipset/print.h> /* ipset_printfn */
#include <libipset/linux_ip_set.h> /* IPSET_MAXNAMELEN */
#define AF_INET46 255
/* Family rules:
* - AF_UNSPEC: type is family-neutral
* - AF_INET: type supports IPv4 only
* - AF_INET6: type supports IPv6 only
* - AF_INET46: type supports both IPv4 and IPv6
*/
/* Set dimensions */
enum {
IPSET_DIM_ONE, /* foo */
IPSET_DIM_TWO, /* foo,bar */
IPSET_DIM_THREE, /* foo,bar,fie */
IPSET_DIM_MAX,
};
/* Parser options */
enum {
IPSET_NO_ARG = -1,
IPSET_OPTIONAL_ARG,
IPSET_MANDATORY_ARG,
IPSET_MANDATORY_ARG2,
};
struct ipset_session;
/* Parse and print type-specific arguments */
struct ipset_arg {
const char *name[2]; /* option names */
int has_arg; /* mandatory/optional/no arg */
enum ipset_opt opt; /* argumentum type */
ipset_parsefn parse; /* parser function */
ipset_printfn print; /* printing function */
};
/* Type check against the kernel */
enum {
IPSET_KERNEL_MISMATCH = -1,
IPSET_KERNEL_CHECK_NEEDED,
IPSET_KERNEL_OK,
};
/* How element parts are parsed */
struct ipset_elem {
ipset_parsefn parse; /* elem parser function */
ipset_printfn print; /* elem print function */
enum ipset_opt opt; /* elem option */
};
/* The set types in userspace
* we could collapse 'args' and 'mandatory' to two-element lists
* but for the readability the full list is supported.
*/
struct ipset_type {
char name[IPSET_MAXNAMELEN]; /* type name */
uint8_t revision; /* revision number */
uint8_t family; /* supported family */
uint8_t dimension; /* elem dimension */
int8_t kernel_check; /* kernel check */
bool last_elem_optional; /* last element optional */
struct ipset_elem elem[IPSET_DIM_MAX]; /* parse elem */
ipset_parsefn compat_parse_elem; /* compatibility parser */
const struct ipset_arg *args[IPSET_CADT_MAX]; /* create/ADT args besides elem */
uint64_t mandatory[IPSET_CADT_MAX]; /* create/ADT mandatory flags */
uint64_t full[IPSET_CADT_MAX]; /* full args flags */
const char *usage; /* terse usage */
void (*usagefn)(void); /* additional usage */
struct ipset_type *next;
const char *alias[]; /* name alias(es) */
};
extern int ipset_cache_add(const char *name, const struct ipset_type *type,
uint8_t family);
extern int ipset_cache_del(const char *name);
extern int ipset_cache_rename(const char *from, const char *to);
extern int ipset_cache_swap(const char *from, const char *to);
extern int ipset_cache_init(void);
extern void ipset_cache_fini(void);
extern const struct ipset_type * ipset_type_get(struct ipset_session *session,
enum ipset_cmd cmd);
extern const struct ipset_type * ipset_type_check(struct ipset_session *session);
extern int ipset_type_add(struct ipset_type *type);
extern const struct ipset_type * ipset_types(void);
extern const char * ipset_typename_resolve(const char *str);
extern bool ipset_match_typename(const char *str,
const struct ipset_type *t);
#endif /* LIBIPSET_TYPES_H */

View File

@@ -0,0 +1,44 @@
/* 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.
*/
#ifndef LIBIPSET_UI_H
#define LIBIPSET_UI_H
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
/* Commands in userspace */
struct ipset_commands {
enum ipset_cmd cmd;
int has_arg;
const char *name[2];
const char *help;
};
extern const struct ipset_commands ipset_commands[];
struct ipset_session;
struct ipset_data;
/* Environment options */
struct ipset_envopts {
int flag;
int has_arg;
const char *name[2];
const char *help;
int (*parse)(struct ipset_session *s, int flag, const char *str);
int (*print)(char *buf, unsigned int len,
const struct ipset_data *data, int flag, uint8_t env);
};
extern const struct ipset_envopts ipset_envopts[];
extern bool ipset_match_cmd(const char *arg, const char * const name[]);
extern bool ipset_match_option(const char *arg, const char * const name[]);
extern bool ipset_match_envopt(const char *arg, const char * const name[]);
extern void ipset_shift_argv(int *argc, char *argv[], int from);
extern void ipset_port_usage(void);
#endif /* LIBIPSET_UI_H */

View File

@@ -0,0 +1,45 @@
/* 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.
*/
#ifndef LIBIPSET_UTILS_H
#define LIBIPSET_UTILS_H
#include <string.h> /* strcmp */
#include <netinet/in.h> /* struct in[6]_addr */
/* String equality tests */
#define STREQ(a,b) (strcmp(a,b) == 0)
#define STRNEQ(a,b,n) (strncmp(a,b,n) == 0)
#define STRCASEQ(a,b) (strcasecmp(a,b) == 0)
#define STRNCASEQ(a,b,n) (strncasecmp(a,b,n) == 0)
/* Stringify tokens */
#define _STR(c) #c
#define STR(c) _STR(c)
/* Min/max */
#define MIN(a, b) (a < b ? a : b)
#define MAX(a, b) (a > b ? a : b)
#define UNUSED __attribute__ ((unused))
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#endif
static inline void
in4cpy(struct in_addr *dest, const struct in_addr *src)
{
dest->s_addr = src->s_addr;
}
static inline void
in6cpy(struct in6_addr *dest, const struct in6_addr *src)
{
memcpy(dest, src, sizeof(struct in6_addr));
}
#endif /* LIBIPSET_UTILS_H */

1863
extensions/ipset-5/ip_set.c Normal file

File diff suppressed because it is too large Load Diff

530
extensions/ipset-5/ip_set.h Normal file
View File

@@ -0,0 +1,530 @@
#ifndef _IP_SET_H
#define _IP_SET_H
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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.
*/
/* The protocol version */
#define IPSET_PROTOCOL 5
/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
/* Message types and commands */
enum ipset_cmd {
IPSET_CMD_NONE,
IPSET_CMD_PROTOCOL, /* 1: Return protocol version */
IPSET_CMD_CREATE, /* 2: Create a new (empty) set */
IPSET_CMD_DESTROY, /* 3: Destroy a (empty) set */
IPSET_CMD_FLUSH, /* 4: Remove all elements from a set */
IPSET_CMD_RENAME, /* 5: Rename a set */
IPSET_CMD_SWAP, /* 6: Swap two sets */
IPSET_CMD_LIST, /* 7: List sets */
IPSET_CMD_SAVE, /* 8: Save sets */
IPSET_CMD_ADD, /* 9: Add an element to a set */
IPSET_CMD_DEL, /* 10: Delete an element from a set */
IPSET_CMD_TEST, /* 11: Test an element in a set */
IPSET_CMD_HEADER, /* 12: Get set header data only */
IPSET_CMD_TYPE, /* 13: Get set type */
IPSET_MSG_MAX, /* Netlink message commands */
/* Commands in userspace: */
IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */
IPSET_CMD_HELP, /* 15: Get help */
IPSET_CMD_VERSION, /* 16: Get program version */
IPSET_CMD_QUIT, /* 17: Quit from interactive mode */
IPSET_CMD_MAX,
IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */
};
/* Attributes at command level */
enum {
IPSET_ATTR_UNSPEC,
IPSET_ATTR_PROTOCOL, /* 1: Protocol version */
IPSET_ATTR_SETNAME, /* 2: Name of the set */
IPSET_ATTR_TYPENAME, /* 3: Typename */
IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* Setname at rename/swap */
IPSET_ATTR_REVISION, /* 4: Settype revision */
IPSET_ATTR_FAMILY, /* 5: Settype family */
IPSET_ATTR_FLAGS, /* 6: Flags at command level */
IPSET_ATTR_DATA, /* 7: Nested attributes */
IPSET_ATTR_ADT, /* 8: Multiple data containers */
IPSET_ATTR_LINENO, /* 9: Restore lineno */
IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */
IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
__IPSET_ATTR_CMD_MAX,
};
#define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1)
/* CADT specific attributes */
enum {
IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1,
IPSET_ATTR_IP_FROM = IPSET_ATTR_IP,
IPSET_ATTR_IP_TO, /* 2 */
IPSET_ATTR_CIDR, /* 3 */
IPSET_ATTR_PORT, /* 4 */
IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
IPSET_ATTR_PORT_TO, /* 5 */
IPSET_ATTR_TIMEOUT, /* 6 */
IPSET_ATTR_PROTO, /* 7 */
IPSET_ATTR_CADT_FLAGS, /* 8 */
IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
IPSET_ATTR_GC,
IPSET_ATTR_HASHSIZE,
IPSET_ATTR_MAXELEM,
IPSET_ATTR_NETMASK,
IPSET_ATTR_PROBES,
IPSET_ATTR_RESIZE,
IPSET_ATTR_SIZE,
/* Kernel-only */
IPSET_ATTR_ELEMENTS,
IPSET_ATTR_REFERENCES,
IPSET_ATTR_MEMSIZE,
__IPSET_ATTR_CREATE_MAX,
};
#define IPSET_ATTR_CREATE_MAX (__IPSET_ATTR_CREATE_MAX - 1)
/* ADT specific attributes */
enum {
IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + 1,
IPSET_ATTR_NAME,
IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
/* IP specific attributes */
enum {
IPSET_ATTR_IPADDR_IPV4 = IPSET_ATTR_UNSPEC + 1,
IPSET_ATTR_IPADDR_IPV6,
__IPSET_ATTR_IPADDR_MAX,
};
#define IPSET_ATTR_IPADDR_MAX (__IPSET_ATTR_IPADDR_MAX - 1)
/* Error codes */
enum ipset_errno {
IPSET_ERR_PRIVATE = 128,
IPSET_ERR_PROTOCOL,
IPSET_ERR_FIND_TYPE,
IPSET_ERR_MAX_SETS,
IPSET_ERR_BUSY,
IPSET_ERR_EXIST_SETNAME2,
IPSET_ERR_TYPE_MISMATCH,
IPSET_ERR_EXIST,
IPSET_ERR_INVALID_CIDR,
IPSET_ERR_INVALID_NETMASK,
IPSET_ERR_INVALID_FAMILY,
IPSET_ERR_TIMEOUT,
IPSET_ERR_REFERENCED,
IPSET_ERR_IPADDR_IPV4,
IPSET_ERR_IPADDR_IPV6,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 160,
};
/* Flags at command level */
enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
};
/* Flags at CADT attribute level */
enum ipset_cadt_flags {
IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE),
};
/* Commands with settype-specific attributes */
enum ipset_adt {
IPSET_ADD,
IPSET_DEL,
IPSET_TEST,
IPSET_ADT_MAX,
IPSET_CREATE = IPSET_ADT_MAX,
IPSET_CADT_MAX,
};
#ifdef __KERNEL__
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/vmalloc.h>
#include <net/netlink.h>
/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t
* and IPSET_INVALID_ID if you want to increase the max number of sets.
*/
typedef u16 ip_set_id_t;
#define IPSET_INVALID_ID 65535
enum ip_set_dim {
IPSET_DIM_ZERO = 0,
IPSET_DIM_ONE,
IPSET_DIM_TWO,
IPSET_DIM_THREE,
/* Max dimension in elements.
* If changed, new revision of iptables match/target is required.
*/
IPSET_DIM_MAX = 6,
};
/* Option flags for kernel operations */
enum ip_set_kopt {
IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO),
IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE),
IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO),
IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE),
};
/* Set features */
enum ip_set_feature {
IPSET_TYPE_IP_FLAG = 0,
IPSET_TYPE_IP = (1 << IPSET_TYPE_IP_FLAG),
IPSET_TYPE_PORT_FLAG = 1,
IPSET_TYPE_PORT = (1 << IPSET_TYPE_PORT_FLAG),
IPSET_TYPE_MAC_FLAG = 2,
IPSET_TYPE_MAC = (1 << IPSET_TYPE_MAC_FLAG),
IPSET_TYPE_IP2_FLAG = 3,
IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG),
IPSET_TYPE_NAME_FLAG = 4,
IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG),
/* Strictly speaking not a feature, but a flag for dumping:
* this settype must be dumped last */
IPSET_DUMP_LAST_FLAG = 7,
IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG),
};
struct ip_set;
typedef int (*ipset_adtfn)(struct ip_set *set, void *value, u32 timeout);
/* Set type, variant-specific part */
struct ip_set_type_variant {
/* Kernelspace: test/add/del entries
* returns negative error code,
* zero for no match/success to add/delete
* positive for matching element */
int (*kadt)(struct ip_set *set, const struct sk_buff * skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
/* Userspace: test/add/del entries
* returns negative error code,
* zero for no match/success to add/delete
* positive for matching element */
int (*uadt)(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags);
/* Low level add/del/test functions */
ipset_adtfn adt[IPSET_ADT_MAX];
/* When adding entries and set is full, try to resize the set */
int (*resize)(struct ip_set *set, bool retried);
/* Destroy the set */
void (*destroy)(struct ip_set *set);
/* Flush the elements */
void (*flush)(struct ip_set *set);
/* Expire entries before listing */
void (*expire)(struct ip_set *set);
/* List set header data */
int (*head)(struct ip_set *set, struct sk_buff *skb);
/* List elements */
int (*list)(const struct ip_set *set, struct sk_buff *skb,
struct netlink_callback *cb);
/* Return true if "b" set is the same as "a"
* according to the create set parameters */
bool (*same_set)(const struct ip_set *a, const struct ip_set *b);
};
/* The core set type structure */
struct ip_set_type {
struct list_head list;
/* Typename */
char name[IPSET_MAXNAMELEN];
/* Protocol version */
u8 protocol;
/* Set features to control swapping */
u8 features;
/* Set type dimension */
u8 dimension;
/* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */
u8 family;
/* Type revision */
u8 revision;
/* Create set */
int (*create)(struct ip_set *set,
struct nlattr *head, int len, u32 flags);
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me;
};
extern int ip_set_type_register(struct ip_set_type *set_type);
extern void ip_set_type_unregister(struct ip_set_type *set_type);
/* A generic IP set */
struct ip_set {
/* The name of the set */
char name[IPSET_MAXNAMELEN];
/* Lock protecting the set data */
rwlock_t lock;
/* References to the set */
atomic_t ref;
/* The core set type */
const struct ip_set_type *type;
/* The type variant doing the real job */
const struct ip_set_type_variant *variant;
/* The actual INET family of the set */
u8 family;
/* The type specific data */
void *data;
};
/* register and unregister set references */
extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set);
extern void ip_set_put_byindex(ip_set_id_t index);
extern const char * ip_set_name_byindex(ip_set_id_t index);
extern ip_set_id_t ip_set_nfnl_get(const char *name);
extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index);
extern void ip_set_nfnl_put(ip_set_id_t index);
/* API for iptables set match, and SET target */
extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb,
u8 family, u8 dim, u8 flags);
extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb,
u8 family, u8 dim, u8 flags);
extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb,
u8 family, u8 dim, u8 flags);
/* Allocate members */
static inline void *
ip_set_alloc(size_t size, gfp_t gfp_mask)
{
void *members = NULL;
if (size < KMALLOC_MAX_SIZE)
members = kzalloc(size, gfp_mask | __GFP_NOWARN);
if (members) {
pr_debug("%p: allocated with kmalloc", members);
return members;
}
members = __vmalloc(size, gfp_mask | __GFP_ZERO, PAGE_KERNEL);
if (!members)
return NULL;
pr_debug("%p: allocated with vmalloc", members);
return members;
}
static inline void
ip_set_free(void *members)
{
pr_debug("%p: free with %s", members,
is_vmalloc_addr(members) ? "vfree" : "kfree");
if (is_vmalloc_addr(members))
vfree(members);
else
kfree(members);
}
/* Ignore IPSET_ERR_EXIST errors if asked to do so? */
static inline bool
ip_set_eexist(int ret, u32 flags)
{
return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST);
}
/* Useful converters */
static inline u32
ip_set_get_h32(const struct nlattr *attr)
{
u32 value = nla_get_u32(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohl(value) : value;
}
static inline u16
ip_set_get_h16(const struct nlattr *attr)
{
u16 value = nla_get_u16(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohs(value) : value;
}
static inline u32
ip_set_get_n32(const struct nlattr *attr)
{
u32 value = nla_get_u32(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htonl(value);
}
static inline u16
ip_set_get_n16(const struct nlattr *attr)
{
u16 value = nla_get_u16(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htons(value);
}
static const struct nla_policy ipaddr_policy[IPSET_ATTR_IPADDR_MAX + 1] = {
[IPSET_ATTR_IPADDR_IPV4] = { .type = NLA_U32 },
[IPSET_ATTR_IPADDR_IPV6] = { .type = NLA_BINARY,
.len = sizeof(struct in6_addr) },
};
static inline int
ip_set_get_ipaddr4(struct nlattr *attr[], int type, u32 *ipaddr)
{
struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1] = {};
if (!attr[type])
return -IPSET_ERR_PROTOCOL;
if (nla_parse(tb, IPSET_ATTR_IPADDR_MAX,
nla_data(attr[type]), nla_len(attr[type]),
ipaddr_policy))
return -IPSET_ERR_PROTOCOL;
if (!tb[IPSET_ATTR_IPADDR_IPV4])
return -IPSET_ERR_IPADDR_IPV4;
*ipaddr = ip_set_get_n32(tb[IPSET_ATTR_IPADDR_IPV4]);
return 0;
}
static inline int
ip_set_get_ipaddr6(struct nlattr *attr[], int type, union nf_inet_addr *ipaddr)
{
struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1] = {};
if (!attr[type])
return -IPSET_ERR_PROTOCOL;
if (nla_parse(tb, IPSET_ATTR_IPADDR_MAX,
nla_data(attr[type]), nla_len(attr[type]),
ipaddr_policy))
return -IPSET_ERR_PROTOCOL;
if (!tb[IPSET_ATTR_IPADDR_IPV6])
return -IPSET_ERR_IPADDR_IPV6;
memcpy(ipaddr, nla_data(tb[IPSET_ATTR_IPADDR_IPV6]),
sizeof(struct in6_addr));
return 0;
}
#define ipset_nest_start(skb, attr) nla_nest_start(skb, attr | NLA_F_NESTED)
#define ipset_nest_end(skb, start) nla_nest_end(skb, start)
#define NLA_PUT_NET32(skb, type, value) \
NLA_PUT_BE32(skb, type | NLA_F_NET_BYTEORDER, value)
#define NLA_PUT_NET16(skb, type, value) \
NLA_PUT_BE16(skb, type | NLA_F_NET_BYTEORDER, value)
#define NLA_PUT_IPADDR4(skb, type, ipaddr) \
do { \
struct nlattr *__nested = ipset_nest_start(skb, type); \
\
if (!__nested) \
goto nla_put_failure; \
NLA_PUT_NET32(skb, IPSET_ATTR_IPADDR_IPV4, ipaddr); \
ipset_nest_end(skb, __nested); \
} while (0)
#define NLA_PUT_IPADDR6(skb, type, ipaddrptr) \
do { \
struct nlattr *__nested = ipset_nest_start(skb, type); \
\
if (!__nested) \
goto nla_put_failure; \
NLA_PUT(skb, IPSET_ATTR_IPADDR_IPV6, \
sizeof(struct in6_addr), ipaddrptr); \
ipset_nest_end(skb, __nested); \
} while (0)
/* Get address from skbuff */
static inline u32
ip4addr(const struct sk_buff *skb, bool src)
{
return src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
}
static inline void
ip4addrptr(const struct sk_buff *skb, bool src, u32 *addr)
{
*addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
}
static inline void
ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr)
{
memcpy(addr, src ? &ipv6_hdr(skb)->saddr : &ipv6_hdr(skb)->daddr,
sizeof(*addr));
}
/* Calculate the bytes required to store the inclusive range of a-b */
static inline int
bitmap_bytes(u32 a, u32 b)
{
return 4 * ((((b - a + 8) / 8) + 3) / 4);
}
/* Prefixlen maps */
extern const union nf_inet_addr prefixlen_netmask_map[];
extern const union nf_inet_addr prefixlen_hostmask_map[];
#define NETMASK(n) prefixlen_netmask_map[n].ip
#define NETMASK6(n) prefixlen_netmask_map[n].ip6
#define HOSTMASK(n) prefixlen_hostmask_map[n].ip
#define HOSTMASK6(n) prefixlen_hostmask_map[n].ip6
/* Interface to iptables/ip6tables */
#define SO_IP_SET 83
union ip_set_name_index {
char name[IPSET_MAXNAMELEN];
ip_set_id_t index;
};
#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */
struct ip_set_req_get_set {
unsigned op;
unsigned version;
union ip_set_name_index set;
};
#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */
/* Uses ip_set_req_get_set */
#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
struct ip_set_req_version {
unsigned op;
unsigned version;
};
#endif /* __KERNEL__ */
#endif /*_IP_SET_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
#ifndef __IP_SET_BITMAP_H
#define __IP_SET_BITMAP_H
/* Bitmap type specific error codes */
enum {
/* The element is out of the range of the set */
IPSET_ERR_BITMAP_RANGE = IPSET_ERR_TYPE_SPECIFIC,
/* The range exceeds the size limit of the set type */
IPSET_ERR_BITMAP_RANGE_SIZE,
};
#ifdef __KERNEL__
#define IPSET_BITMAP_MAX_RANGE 0x0000FFFF
/* Common functions */
static inline u32
range_to_mask(u32 from, u32 to, u8 *bits)
{
u32 mask = 0xFFFFFFFE;
*bits = 32;
while (--(*bits) > 0 && mask && (to & mask) != from)
mask <<= 1;
return mask;
}
#endif /* __KERNEL__ */
#endif /* __IP_SET_BITMAP_H */

View File

@@ -0,0 +1,727 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the bitmap:ip type */
#include "ip_set_kernel.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/netlink.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <net/netlink.h>
#include <net/tcp.h>
#include "ip_set.h"
#include "ip_set_bitmap.h"
#define IP_SET_BITMAP_TIMEOUT
#include "ip_set_timeout.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("bitmap:ip type of IP sets");
MODULE_ALIAS("ip_set_bitmap:ip");
/* Base variant */
struct bitmap_ip {
void *members; /* the set members */
u32 first_ip; /* host byte order, included in range */
u32 last_ip; /* host byte order, included in range */
u32 elements; /* number of max elements in the set */
u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */
u8 netmask; /* subnet netmask */
};
static inline u32
ip_to_id(const struct bitmap_ip *map, u32 ip)
{
return ((ip & HOSTMASK(map->netmask)) - map->first_ip)/map->hosts;
}
static inline int
bitmap_ip_test(const struct bitmap_ip *map, u32 id)
{
return !!test_bit(id, map->members);
}
static inline int
bitmap_ip_add(struct bitmap_ip *map, u32 id)
{
if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
}
static inline int
bitmap_ip_del(struct bitmap_ip *map, u32 id)
{
if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
}
static int
bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_ip *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(map, ip);
switch (adt) {
case IPSET_TEST:
return bitmap_ip_test(map, ip);
case IPSET_ADD:
return bitmap_ip_add(map, ip);
case IPSET_DEL:
return bitmap_ip_del(map, ip);
default:
return -EINVAL;
}
}
static const struct nla_policy bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct bitmap_ip *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
u32 ip, ip_to, id;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_ip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip);
if (ret)
return ret;
ip = ntohl(ip);
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
/* Set was defined without timeout support:
* don't ignore the attribute silently */
if (tb[IPSET_ATTR_TIMEOUT])
return -IPSET_ERR_TIMEOUT;
if (adt == IPSET_TEST)
return bitmap_ip_test(map, ip_to_id(map, ip));
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to) {
swap(ip, ip_to);
if (ip < map->first_ip)
return -IPSET_ERR_BITMAP_RANGE;
}
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
if (ip_to > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
for (; !before(ip_to, ip); ip += map->hosts) {
id = ip_to_id(map, ip);
ret = adt == IPSET_ADD ? bitmap_ip_add(map, id)
: bitmap_ip_del(map, id);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static void
bitmap_ip_destroy(struct ip_set *set)
{
struct bitmap_ip *map = set->data;
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_ip_flush(struct ip_set *set)
{
struct bitmap_ip *map = set->data;
memset(map->members, 0, map->memsize);
}
static int
bitmap_ip_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ip *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_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_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
bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct bitmap_ip *x = a->data;
const struct bitmap_ip *y = b->data;
return x->first_ip == y->first_ip
&& x->last_ip == y->last_ip
&& x->netmask == y->netmask;
}
static const struct ip_set_type_variant bitmap_ip = {
.kadt = bitmap_ip_kadt,
.uadt = bitmap_ip_uadt,
.destroy = bitmap_ip_destroy,
.flush = bitmap_ip_flush,
.head = bitmap_ip_head,
.list = bitmap_ip_list,
.same_set = bitmap_ip_same_set,
};
/* Timeout variant */
struct bitmap_ip_timeout {
unsigned long *members; /* the set members */
u32 first_ip; /* host byte order, included in range */
u32 last_ip; /* host byte order, included in range */
u32 elements; /* number of max elements in the set */
u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */
u8 netmask; /* subnet netmask */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
static inline bool
bitmap_ip_timeout_test(const struct bitmap_ip_timeout *map, u32 id)
{
return ip_set_timeout_test(map->members[id]);
}
static inline int
bitmap_ip_timeout_add(struct bitmap_ip_timeout *map,
u32 id, u32 timeout)
{
if (bitmap_ip_timeout_test(map, id))
return -IPSET_ERR_EXIST;
map->members[id] = ip_set_timeout_set(timeout);
return 0;
}
static inline int
bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, u32 id)
{
int ret = -IPSET_ERR_EXIST;
if (bitmap_ip_timeout_test(map, id))
ret = 0;
map->members[id] = IPSET_ELEM_UNSET;
return ret;
}
static int
bitmap_ip_timeout_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_ip_timeout *map = set->data;
u32 ip;
ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
ip = ip_to_id((const struct bitmap_ip *)map, ip);
switch (adt) {
case IPSET_TEST:
return bitmap_ip_timeout_test(map, ip);
case IPSET_ADD:
return bitmap_ip_timeout_add(map, ip, map->timeout);
case IPSET_DEL:
return bitmap_ip_timeout_del(map, ip);
default:
return -EINVAL;
}
}
static int
bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct bitmap_ip_timeout *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
u32 ip, ip_to, id, timeout = map->timeout;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_ip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip);
if (ret)
return ret;
ip = ntohl(ip);
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
if (adt == IPSET_TEST)
return bitmap_ip_timeout_test(map,
ip_to_id((const struct bitmap_ip *)map, ip));
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to) {
swap(ip, ip_to);
if (ip < map->first_ip)
return -IPSET_ERR_BITMAP_RANGE;
}
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
if (ip_to > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT])
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
for (; !before(ip_to, ip); ip += map->hosts) {
id = ip_to_id((const struct bitmap_ip *)map, ip);
ret = adt == IPSET_ADD
? bitmap_ip_timeout_add(map, id, timeout)
: bitmap_ip_timeout_del(map, id);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static void
bitmap_ip_timeout_destroy(struct ip_set *set)
{
struct bitmap_ip_timeout *map = set->data;
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_ip_timeout_flush(struct ip_set *set)
{
struct bitmap_ip_timeout *map = set->data;
memset(map->members, IPSET_ELEM_UNSET, map->memsize);
}
static int
bitmap_ip_timeout_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ip_timeout *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip));
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
if (map->netmask != 32)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->memsize));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EFAULT;
}
static int
bitmap_ip_timeout_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ip_timeout *map = set->data;
struct nlattr *adt, *nested;
u32 id, first = cb->args[2];
const unsigned long *table = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!adt)
return -EFAULT;
for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2];
if (!bitmap_ip_timeout_test(map, id))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, adt);
return -EFAULT;
} else
goto nla_put_failure;
}
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(table[id])));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, adt);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
return 0;
}
static bool
bitmap_ip_timeout_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct bitmap_ip_timeout *x = a->data;
const struct bitmap_ip_timeout *y = b->data;
return x->first_ip == y->first_ip
&& x->last_ip == y->last_ip
&& x->netmask == y->netmask
&& x->timeout == y->timeout;
}
static const struct ip_set_type_variant bitmap_ip_timeout = {
.kadt = bitmap_ip_timeout_kadt,
.uadt = bitmap_ip_timeout_uadt,
.destroy = bitmap_ip_timeout_destroy,
.flush = bitmap_ip_timeout_flush,
.head = bitmap_ip_timeout_head,
.list = bitmap_ip_timeout_list,
.same_set = bitmap_ip_timeout_same_set,
};
static void
bitmap_ip_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_ip_timeout *map = set->data;
unsigned long *table = map->members;
u32 id;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (id = 0; id < map->elements; id++)
if (ip_set_timeout_expired(table[id]))
table[id] = IPSET_ELEM_UNSET;
read_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static inline void
bitmap_ip_gc_init(struct ip_set *set)
{
struct bitmap_ip_timeout *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = bitmap_ip_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
/* Create bitmap:ip type of sets */
static const struct nla_policy
bitmap_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static bool
init_map_ip(struct ip_set *set, struct bitmap_ip *map,
u32 first_ip, u32 last_ip,
u32 elements, u32 hosts, u8 netmask)
{
map->members = ip_set_alloc(map->memsize, GFP_KERNEL);
if (!map->members)
return false;
map->first_ip = first_ip;
map->last_ip = last_ip;
map->elements = elements;
map->hosts = hosts;
map->netmask = netmask;
set->data = map;
set->family = AF_INET;
return true;
}
static int
bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 first_ip, last_ip, hosts, elements;
u8 netmask = 32;
int ret;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_ip_create_policy))
return -IPSET_ERR_PROTOCOL;
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &first_ip);
if (ret)
return ret;
first_ip = ntohl(first_ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &last_ip);
if (ret)
return ret;
last_ip = htonl(last_ip);
if (first_ip > last_ip) {
u32 tmp = first_ip;
first_ip = last_ip;
last_ip = tmp;
}
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR;
last_ip = first_ip | ~HOSTMASK(cidr);
} else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_NETMASK]) {
netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
if (netmask > 32)
return -IPSET_ERR_INVALID_NETMASK;
first_ip &= HOSTMASK(netmask);
last_ip |= ~HOSTMASK(netmask);
}
if (netmask == 32) {
hosts = 1;
elements = last_ip - first_ip + 1;
} else {
u8 mask_bits;
u32 mask;
mask = range_to_mask(first_ip, last_ip, &mask_bits);
if ((!mask && (first_ip || last_ip != 0xFFFFFFFF))
|| netmask <= mask_bits)
return -IPSET_ERR_BITMAP_RANGE;
pr_debug("mask_bits %u, netmask %u", mask_bits, netmask);
hosts = 2 << (32 - netmask - 1);
elements = 2 << (netmask - mask_bits - 1);
}
if (elements > IPSET_BITMAP_MAX_RANGE + 1)
return -IPSET_ERR_BITMAP_RANGE_SIZE;
pr_debug("hosts %u, elements %u", hosts, elements);
if (tb[IPSET_ATTR_TIMEOUT]) {
struct bitmap_ip_timeout *map;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map->memsize = elements * sizeof(unsigned long);
if (!init_map_ip(set, (struct bitmap_ip *)map,
first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_ip_timeout;
bitmap_ip_gc_init(set);
} else {
struct bitmap_ip *map;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map->memsize = bitmap_bytes(0, elements - 1);
if (!init_map_ip(set, map,
first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
set->variant = &bitmap_ip;
}
return 0;
}
static struct ip_set_type bitmap_ip_type __read_mostly = {
.name = "bitmap:ip",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_INET,
.revision = 0,
.create = bitmap_ip_create,
.me = THIS_MODULE,
};
static int __init
bitmap_ip_init(void)
{
return ip_set_type_register(&bitmap_ip_type);
}
static void __exit
bitmap_ip_fini(void)
{
ip_set_type_unregister(&bitmap_ip_type);
}
module_init(bitmap_ip_init);
module_exit(bitmap_ip_fini);

View File

@@ -0,0 +1,660 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the bitmap:ip,mac type */
#include "ip_set_kernel.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <net/netlink.h>
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_bitmap.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("bitmap:ip,mac type of IP sets");
MODULE_ALIAS("ip_set_bitmap:ip,mac");
enum {
MAC_EMPTY, /* element is not set */
MAC_FILLED, /* element is set with MAC */
MAC_UNSET, /* element is set, without MAC */
};
/* Type structure */
struct bitmap_ipmac {
void *members; /* the set members */
u32 first_ip; /* host byte order, included in range */
u32 last_ip; /* host byte order, included in range */
u32 timeout; /* timeout value */
struct timer_list gc; /* garbage collector */
size_t dsize; /* size of element */
};
/* ADT structure for generic function args */
struct ipmac {
u32 id; /* id in array */
unsigned char *ether; /* ethernet address */
};
/* Member element without and with timeout */
struct ipmac_elem {
unsigned char ether[ETH_ALEN];
unsigned char match;
} __attribute__ ((aligned));
struct ipmac_telem {
unsigned char ether[ETH_ALEN];
unsigned char match;
unsigned long timeout;
} __attribute__ ((aligned));
static inline void *
bitmap_ipmac_elem(const struct bitmap_ipmac *map, u32 id)
{
return (void *)((char *)map->members + id * map->dsize);
}
static inline bool
bitmap_timeout(const struct bitmap_ipmac *map, u32 id)
{
const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id);
return ip_set_timeout_test(elem->timeout);
}
static inline bool
bitmap_expired(const struct bitmap_ipmac *map, u32 id)
{
const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id);
return ip_set_timeout_expired(elem->timeout);
}
static inline int
bitmap_ipmac_exist(const struct ipmac_telem *elem)
{
return elem->match == MAC_UNSET
|| (elem->match == MAC_FILLED
&& !ip_set_timeout_expired(elem->timeout));
}
/* Base variant */
static int
bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
switch (elem->match) {
case MAC_UNSET:
/* Trigger kernel to fill out the ethernet address */
return -EAGAIN;
case MAC_FILLED:
return data->ether == NULL
|| compare_ether_addr(data->ether, elem->ether) == 0;
}
return 0;
}
static int
bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
switch (elem->match) {
case MAC_UNSET:
if (!data->ether)
/* Already added without ethernet address */
return -IPSET_ERR_EXIST;
/* Fill the MAC address */
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
break;
case MAC_FILLED:
return -IPSET_ERR_EXIST;
case MAC_EMPTY:
if (data->ether) {
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
} else
elem->match = MAC_UNSET;
}
return 0;
}
static int
bitmap_ipmac_del(struct ip_set *set, void *value, u32 timeout)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
if (elem->match == MAC_EMPTY)
return -IPSET_ERR_EXIST;
elem->match = MAC_EMPTY;
return 0;
}
static int
bitmap_ipmac_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac_elem *elem;
struct nlattr *atd, *nested;
u32 id, first = cb->args[2];
u32 last = map->last_ip - map->first_ip;
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
elem = bitmap_ipmac_elem(map, id);
if (elem->match == MAC_EMPTY)
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));
if (elem->match == MAC_FILLED)
NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN,
elem->ether);
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;
}
/* Timeout variant */
static int
bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
switch (elem->match) {
case MAC_UNSET:
/* Trigger kernel to fill out the ethernet address */
return -EAGAIN;
case MAC_FILLED:
return (data->ether == NULL
|| compare_ether_addr(data->ether, elem->ether) == 0)
&& !bitmap_expired(map, data->id);
}
return 0;
}
static int
bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
switch (elem->match) {
case MAC_UNSET:
if (!data->ether)
/* Already added without ethernet address */
return -IPSET_ERR_EXIST;
/* Fill the MAC address and activate the timer */
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
if (timeout == map->timeout)
/* Timeout was not specified, get stored one */
timeout = elem->timeout;
elem->timeout = ip_set_timeout_set(timeout);
break;
case MAC_FILLED:
if (!bitmap_expired(map, data->id))
return -IPSET_ERR_EXIST;
/* Fall through */
case MAC_EMPTY:
if (data->ether) {
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
} else
elem->match = MAC_UNSET;
/* If MAC is unset yet, we store plain timeout value
* because the timer is not activated yet
* and we can reuse it later when MAC is filled out,
* possibly by the kernel */
elem->timeout = data->ether ? ip_set_timeout_set(timeout)
: timeout;
break;
}
return 0;
}
static int
bitmap_ipmac_tdel(struct ip_set *set, void *value, u32 timeout)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
if (elem->match == MAC_EMPTY || bitmap_expired(map, data->id))
return -IPSET_ERR_EXIST;
elem->match = MAC_EMPTY;
return 0;
}
static int
bitmap_ipmac_tlist(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac_telem *elem;
struct nlattr *atd, *nested;
u32 id, first = cb->args[2];
u32 timeout, last = map->last_ip - map->first_ip;
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
elem = bitmap_ipmac_elem(map, id);
if (!bitmap_ipmac_exist(elem))
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));
if (elem->match == MAC_FILLED)
NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN,
elem->ether);
timeout = elem->match == MAC_UNSET ? elem->timeout
: ip_set_timeout_get(elem->timeout);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(timeout));
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 int
bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data;
data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
/* Backward compatibility: we don't check the second flag */
if (skb_mac_header(skb) < skb->head
|| (skb_mac_header(skb) + ETH_HLEN) > skb->data)
return -EINVAL;
data.id -= map->first_ip;
data.ether = eth_hdr(skb)->h_source;
return adtfn(set, &data, map->timeout);
}
static const struct nla_policy
bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_ETHER] = { .type = NLA_BINARY, .len = ETH_ALEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct bitmap_ipmac *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data;
u32 timeout = map->timeout;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
bitmap_ipmac_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.id);
if (ret)
return ret;
data.id = ntohl(data.id);
if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_ETHER])
data.ether = nla_data(tb[IPSET_ATTR_ETHER]);
else
data.ether = NULL;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(map->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
data.id -= map->first_ip;
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
static void
bitmap_ipmac_destroy(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_ipmac_flush(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
memset(map->members, 0,
(map->last_ip - map->first_ip + 1) * map->dsize);
}
static int
bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ipmac *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));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map)
+ (map->last_ip - map->first_ip + 1) * map->dsize));
if (with_timeout(map->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EFAULT;
}
static bool
bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct bitmap_ipmac *x = a->data;
const struct bitmap_ipmac *y = b->data;
return x->first_ip == y->first_ip
&& x->last_ip == y->last_ip
&& x->timeout == y->timeout;
}
const struct ip_set_type_variant bitmap_ipmac = {
.kadt = bitmap_ipmac_kadt,
.uadt = bitmap_ipmac_uadt,
.adt = {
[IPSET_ADD] = bitmap_ipmac_add,
[IPSET_DEL] = bitmap_ipmac_del,
[IPSET_TEST] = bitmap_ipmac_test,
},
.destroy = bitmap_ipmac_destroy,
.flush = bitmap_ipmac_flush,
.head = bitmap_ipmac_head,
.list = bitmap_ipmac_list,
.same_set = bitmap_ipmac_same_set,
};
const struct ip_set_type_variant bitmap_tipmac = {
.kadt = bitmap_ipmac_kadt,
.uadt = bitmap_ipmac_uadt,
.adt = {
[IPSET_ADD] = bitmap_ipmac_tadd,
[IPSET_DEL] = bitmap_ipmac_tdel,
[IPSET_TEST] = bitmap_ipmac_ttest,
},
.destroy = bitmap_ipmac_destroy,
.flush = bitmap_ipmac_flush,
.head = bitmap_ipmac_head,
.list = bitmap_ipmac_tlist,
.same_set = bitmap_ipmac_same_set,
};
static void
bitmap_ipmac_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_ipmac *map = set->data;
struct ipmac_telem *elem;
u32 id, last = map->last_ip - map->first_ip;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (id = 0; id <= last; id++) {
elem = bitmap_ipmac_elem(map, id);
if (elem->match == MAC_FILLED
&& ip_set_timeout_expired(elem->timeout))
elem->match = MAC_EMPTY;
}
read_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static inline void
bitmap_ipmac_gc_init(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = bitmap_ipmac_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
/* Create bitmap:ip,mac type of sets */
static const struct nla_policy
bitmap_ipmac_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static bool
init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
u32 first_ip, u32 last_ip)
{
map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize,
GFP_KERNEL);
if (!map->members)
return false;
map->first_ip = first_ip;
map->last_ip = last_ip;
map->timeout = IPSET_NO_TIMEOUT;
set->data = map;
set->family = AF_INET;
return true;
}
static int
bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 first_ip, last_ip, elements;
struct bitmap_ipmac *map;
int ret;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_ipmac_create_policy))
return -IPSET_ERR_PROTOCOL;
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &first_ip);
if (ret)
return ret;
first_ip = ntohl(first_ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &last_ip);
if (ret)
return ret;
last_ip = ntohl(last_ip);
if (first_ip > last_ip) {
u32 tmp = first_ip;
first_ip = last_ip;
last_ip = tmp;
}
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR;
last_ip = first_ip | ~HOSTMASK(cidr);
} else
return -IPSET_ERR_PROTOCOL;
elements = last_ip - first_ip + 1;
if (elements > IPSET_BITMAP_MAX_RANGE + 1)
return -IPSET_ERR_BITMAP_RANGE_SIZE;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->dsize = sizeof(struct ipmac_telem);
if (!init_map_ipmac(set, map, first_ip, last_ip)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_tipmac;
bitmap_ipmac_gc_init(set);
} else {
map->dsize = sizeof(struct ipmac_elem);
if (!init_map_ipmac(set, map, first_ip, last_ip)) {
kfree(map);
return -ENOMEM;
}
set->variant = &bitmap_ipmac;
}
return 0;
}
struct ip_set_type bitmap_ipmac_type = {
.name = "bitmap:ip,mac",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_MAC,
.dimension = IPSET_DIM_TWO,
.family = AF_INET,
.revision = 0,
.create = bitmap_ipmac_create,
.me = THIS_MODULE,
};
static int __init
bitmap_ipmac_init(void)
{
return ip_set_type_register(&bitmap_ipmac_type);
}
static void __exit
bitmap_ipmac_fini(void)
{
ip_set_type_unregister(&bitmap_ipmac_type);
}
module_init(bitmap_ipmac_init);
module_exit(bitmap_ipmac_fini);

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,26 @@
#ifndef __IP_SET_HASH_H
#define __IP_SET_HASH_H
/* Hash type specific error codes */
enum {
/* Hash is full */
IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC,
/* Null-valued element */
IPSET_ERR_HASH_ELEM,
/* Invalid protocol */
IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO,
};
#ifdef __KERNEL__
#define IPSET_DEFAULT_HASHSIZE 1024
#define IPSET_MIMINAL_HASHSIZE 64
#define IPSET_DEFAULT_MAXELEM 65536
#define IPSET_DEFAULT_PROBES 4
#define IPSET_DEFAULT_RESIZE 100
#endif /* __KERNEL__ */
#endif /* __IP_SET_HASH_H */

View File

@@ -0,0 +1,484 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the hash:ip type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <net/tcp.h>
#include <linux/netfilter.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:ip type of IP sets");
MODULE_ALIAS("ip_set_hash:ip");
/* Type specific function prefix */
#define TYPE hash_ip
static bool
hash_ip_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_ip4_same_set hash_ip_same_set
#define hash_ip6_same_set hash_ip_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
struct hash_ip4_elem {
u32 ip;
};
/* Member elements with timeout support */
struct hash_ip4_telem {
u32 ip;
unsigned long timeout;
};
static inline bool
hash_ip4_data_equal(const struct hash_ip4_elem *ip1,
const struct hash_ip4_elem *ip2)
{
return ip1->ip == ip2->ip;
}
static inline bool
hash_ip4_data_isnull(const struct hash_ip4_elem *elem)
{
return elem->ip == 0;
}
static inline void
hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src)
{
dst->ip = src->ip;
}
static inline void
hash_ip4_data_swap(struct hash_ip4_elem *dst, struct hash_ip4_elem *src)
{
swap(dst->ip, src->ip);
}
/* Zero valued IP addresses cannot be stored */
static inline void
hash_ip4_data_zero_out(struct hash_ip4_elem *elem)
{
elem->ip = 0;
}
static inline bool
hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data)
{
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data)
{
const struct hash_ip4_telem *tdata =
(const struct hash_ip4_telem *)data;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
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_NETMASK
#define PF 4
#define HOST_MASK 32
#include "ip_set_ahash.h"
static int
hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip);
ip &= NETMASK(h->netmask);
if (ip == 0)
return -EINVAL;
return adtfn(set, &ip, h->timeout);
}
static const struct nla_policy hash_ip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip, nip, ip_to, hosts, timeout = h->timeout;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip4_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip);
if (ret)
return ret;
ip &= NETMASK(h->netmask);
if (ip == 0)
return -IPSET_ERR_HASH_ELEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST)
return adtfn(set, &ip, timeout);
ip = ntohl(ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to)
swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
for (; !before(ip_to, ip); ip += hosts) {
nip = htonl(ip);
ret = adtfn(set, &nip, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static bool
hash_ip_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
&& x->netmask == y->netmask;
}
/* The type variant functions: IPv6 */
struct hash_ip6_elem {
union nf_inet_addr ip;
};
struct hash_ip6_telem {
union nf_inet_addr ip;
unsigned long timeout;
};
static inline bool
hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
const struct hash_ip6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0;
}
static inline bool
hash_ip6_data_isnull(const struct hash_ip6_elem *elem)
{
return ipv6_addr_any(&elem->ip.in6);
}
static inline void
hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src)
{
ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
}
static inline void
hash_ip6_data_swap(struct hash_ip6_elem *dst, struct hash_ip6_elem *src)
{
struct in6_addr tmp;
ipv6_addr_copy(&tmp, &dst->ip.in6);
ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
ipv6_addr_copy(&src->ip.in6, &tmp);
}
static inline void
hash_ip6_data_zero_out(struct hash_ip6_elem *elem)
{
ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0);
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= NETMASK6(prefix)[0];
ip->ip6[1] &= NETMASK6(prefix)[1];
ip->ip6[2] &= NETMASK6(prefix)[2];
ip->ip6[3] &= NETMASK6(prefix)[3];
}
static inline bool
hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data)
{
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data)
{
const struct hash_ip6_telem *e =
(const struct hash_ip6_telem *)data;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
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 int
hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6);
ip6_netmask(&ip, h->netmask);
if (ipv6_addr_any(&ip.in6))
return -EINVAL;
return adtfn(set, &ip, h->timeout);
}
static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip6_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &ip);
if (ret)
return ret;
ip6_netmask(&ip, h->netmask);
if (ipv6_addr_any(&ip.in6))
return -IPSET_ERR_HASH_ELEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
ret = adtfn(set, &ip, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
static const struct nla_policy
hash_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
};
static int
hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 netmask, hbits;
struct ip_set_hash *h;
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
netmask = set->family == AF_INET ? 32 : 128;
pr_debug("Create set %s with family %s",
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 (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]);
if (tb[IPSET_ATTR_NETMASK]) {
netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
if ((set->family == AF_INET && netmask > 32)
|| (set->family == AF_INET6 && netmask > 128)
|| netmask == 0)
return -IPSET_ERR_INVALID_NETMASK;
}
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
h->netmask = netmask;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
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_ip4_tvariant : &hash_ip6_tvariant;
if (set->family == AF_INET)
hash_ip4_gc_init(set);
else
hash_ip6_gc_init(set);
} else {
set->variant = set->family == AF_INET
? &hash_ip4_variant : &hash_ip6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
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_ip_type __read_mostly = {
.name = "hash:ip",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.create = hash_ip_create,
.me = THIS_MODULE,
};
static int __init
hash_ip_init(void)
{
return ip_set_type_register(&hash_ip_type);
}
static void __exit
hash_ip_fini(void)
{
ip_set_type_unregister(&hash_ip_type);
}
module_init(hash_ip_init);
module_exit(hash_ip_fini);

View File

@@ -0,0 +1,569 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the hash:ip,port type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <net/tcp.h>
#include <linux/netfilter.h>
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_getport.h"
#include "ip_set_hash.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("hash:ip,port type of IP sets");
MODULE_ALIAS("ip_set_hash:ip,port");
/* Type specific function prefix */
#define TYPE hash_ipport
static bool
hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_ipport4_same_set hash_ipport_same_set
#define hash_ipport6_same_set hash_ipport_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
struct hash_ipport4_elem {
u32 ip;
u16 port;
u8 proto;
u8 padding;
};
/* Member elements with timeout support */
struct hash_ipport4_telem {
u32 ip;
u16 port;
u8 proto;
u8 padding;
unsigned long timeout;
};
static inline bool
hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
const struct hash_ipport4_elem *ip2)
{
return ip1->ip == ip2->ip
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
}
static inline bool
hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipport4_data_copy(struct hash_ipport4_elem *dst,
const struct hash_ipport4_elem *src)
{
dst->ip = src->ip;
dst->port = src->port;
dst->proto = src->proto;
}
static inline void
hash_ipport4_data_swap(struct hash_ipport4_elem *dst,
struct hash_ipport4_elem *src)
{
swap(dst->ip, src->ip);
swap(dst->port, src->port);
swap(dst->proto, src->proto);
}
static inline void
hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem)
{
elem->proto = 0;
}
static inline bool
hash_ipport4_data_list(struct sk_buff *skb,
const struct hash_ipport4_elem *data)
{
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_ipport4_data_tlist(struct sk_buff *skb,
const struct hash_ipport4_elem *data)
{
const struct hash_ipport4_telem *tdata =
(const struct hash_ipport4_telem *)data;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
return 0;
nla_put_failure:
return 1;
}
#define PF 4
#define HOST_MASK 32
#include "ip_set_ahash.h"
static int
hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
return adtfn(set, &data, h->timeout);
}
static const struct nla_policy
hash_ipport_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
u32 ip, ip_to, p, port, port_to;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR]
|| tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to)
swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
} else
port_to = port;
for (; !before(ip_to, ip); ip++)
for (p = port; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static bool
hash_ipport_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_ipport6_elem {
union nf_inet_addr ip;
u16 port;
u8 proto;
u8 padding;
};
struct hash_ipport6_telem {
union nf_inet_addr ip;
u16 port;
u8 proto;
u8 padding;
unsigned long timeout;
};
static inline bool
hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
const struct hash_ipport6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
}
static inline bool
hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipport6_data_copy(struct hash_ipport6_elem *dst,
const struct hash_ipport6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipport6_data_swap(struct hash_ipport6_elem *dst,
struct hash_ipport6_elem *src)
{
struct hash_ipport6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem)
{
elem->proto = 0;
}
static inline bool
hash_ipport6_data_list(struct sk_buff *skb,
const struct hash_ipport6_elem *data)
{
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_ipport6_data_tlist(struct sk_buff *skb,
const struct hash_ipport6_elem *data)
{
const struct hash_ipport6_telem *e =
(const struct hash_ipport6_telem *)data;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
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 int
hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { };
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
return adtfn(set, &data, h->timeout);
}
static int
hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { };
u32 port, port_to;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
/* Create hash:ip type of sets */
static const struct nla_policy
hash_ipport_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
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 (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipport_create_policy))
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), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
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_ipport4_tvariant : &hash_ipport6_tvariant;
if (set->family == AF_INET)
hash_ipport4_gc_init(set);
else
hash_ipport6_gc_init(set);
} else {
set->variant = set->family == AF_INET
? &hash_ipport4_variant : &hash_ipport6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
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_ipport_type __read_mostly = {
.name = "hash:ip,port",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC,
.revision = 0,
.create = hash_ipport_create,
.me = THIS_MODULE,
};
static int __init
hash_ipport_init(void)
{
return ip_set_type_register(&hash_ipport_type);
}
static void __exit
hash_ipport_fini(void)
{
ip_set_type_unregister(&hash_ipport_type);
}
module_init(hash_ipport_init);
module_exit(hash_ipport_fini);

View File

@@ -0,0 +1,590 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the hash:ip,port,ip type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <net/tcp.h>
#include <linux/netfilter.h>
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_getport.h"
#include "ip_set_hash.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("hash:ip,port,ip type of IP sets");
MODULE_ALIAS("ip_set_hash:ip,port,ip");
/* Type specific function prefix */
#define TYPE hash_ipportip
static bool
hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_ipportip4_same_set hash_ipportip_same_set
#define hash_ipportip6_same_set hash_ipportip_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
struct hash_ipportip4_elem {
u32 ip;
u32 ip2;
u16 port;
u8 proto;
u8 padding;
};
/* Member elements with timeout support */
struct hash_ipportip4_telem {
u32 ip;
u32 ip2;
u16 port;
u8 proto;
u8 padding;
unsigned long timeout;
};
static inline bool
hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
const struct hash_ipportip4_elem *ip2)
{
return ip1->ip == ip2->ip
&& ip1->ip2 == ip2->ip2
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
}
static inline bool
hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst,
const struct hash_ipportip4_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportip4_data_swap(struct hash_ipportip4_elem *dst,
struct hash_ipportip4_elem *src)
{
struct hash_ipportip4_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem)
{
elem->proto = 0;
}
static inline bool
hash_ipportip4_data_list(struct sk_buff *skb,
const struct hash_ipportip4_elem *data)
{
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_ipportip4_data_tlist(struct sk_buff *skb,
const struct hash_ipportip4_elem *data)
{
const struct hash_ipportip4_telem *tdata =
(const struct hash_ipportip4_telem *)data;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
return 0;
nla_put_failure:
return 1;
}
#define PF 4
#define HOST_MASK 32
#include "ip_set_ahash.h"
static int
hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
return adtfn(set, &data, h->timeout);
}
static const struct nla_policy
hash_ipportip_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
u32 ip, ip_to, p, port, port_to;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
if (ret)
return ret;
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP2, &data.ip2);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR]
|| tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to)
swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
} else
port_to = port;
for (; !before(ip_to, ip); ip++)
for (p = port; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static bool
hash_ipportip_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_ipportip6_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
u8 proto;
u8 padding;
};
struct hash_ipportip6_telem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
u8 proto;
u8 padding;
unsigned long timeout;
};
static inline bool
hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
const struct hash_ipportip6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
}
static inline bool
hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst,
const struct hash_ipportip6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportip6_data_swap(struct hash_ipportip6_elem *dst,
struct hash_ipportip6_elem *src)
{
struct hash_ipportip6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem)
{
elem->proto = 0;
}
static inline bool
hash_ipportip6_data_list(struct sk_buff *skb,
const struct hash_ipportip6_elem *data)
{
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_ipportip6_data_tlist(struct sk_buff *skb,
const struct hash_ipportip6_elem *data)
{
const struct hash_ipportip6_telem *e =
(const struct hash_ipportip6_telem *)data;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
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 int
hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { };
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
return adtfn(set, &data, h->timeout);
}
static int
hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { };
u32 port, port_to;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
if (ret)
return ret;
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP2, &data.ip2);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
/* Create hash:ip type of sets */
static const struct nla_policy
hash_ipportip_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_ipportip_create(struct ip_set *set, struct nlattr *head,
int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
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 (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipportip_create_policy))
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), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
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_ipportip4_tvariant : &hash_ipportip6_tvariant;
if (set->family == AF_INET)
hash_ipportip4_gc_init(set);
else
hash_ipportip6_gc_init(set);
} else {
set->variant = set->family == AF_INET
? &hash_ipportip4_variant : &hash_ipportip6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
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_ipportip_type __read_mostly = {
.name = "hash:ip,port,ip",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC,
.revision = 0,
.create = hash_ipportip_create,
.me = THIS_MODULE,
};
static int __init
hash_ipportip_init(void)
{
return ip_set_type_register(&hash_ipportip_type);
}
static void __exit
hash_ipportip_fini(void)
{
ip_set_type_unregister(&hash_ipportip_type);
}
module_init(hash_ipportip_init);
module_exit(hash_ipportip_fini);

View File

@@ -0,0 +1,656 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the hash:ip,port,net type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <net/tcp.h>
#include <linux/netfilter.h>
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_getport.h"
#include "ip_set_hash.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("hash:ip,port,net type of IP sets");
MODULE_ALIAS("ip_set_hash:ip,port,net");
/* Type specific function prefix */
#define TYPE hash_ipportnet
static bool
hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_ipportnet4_same_set hash_ipportnet_same_set
#define hash_ipportnet6_same_set hash_ipportnet_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
struct hash_ipportnet4_elem {
u32 ip;
u32 ip2;
u16 port;
u8 cidr;
u8 proto;
};
/* Member elements with timeout support */
struct hash_ipportnet4_telem {
u32 ip;
u32 ip2;
u16 port;
u8 cidr;
u8 proto;
unsigned long timeout;
};
static inline bool
hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
const struct hash_ipportnet4_elem *ip2)
{
return ip1->ip == ip2->ip
&& ip1->ip2 == ip2->ip2
&& ip1->cidr == ip2->cidr
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
}
static inline bool
hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
const struct hash_ipportnet4_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportnet4_data_swap(struct hash_ipportnet4_elem *dst,
struct hash_ipportnet4_elem *src)
{
struct hash_ipportnet4_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
{
elem->ip2 &= NETMASK(cidr);
elem->cidr = cidr;
}
static inline void
hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem)
{
elem->proto = 0;
}
static inline bool
hash_ipportnet4_data_list(struct sk_buff *skb,
const struct hash_ipportnet4_elem *data)
{
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_ipportnet4_data_tlist(struct sk_buff *skb,
const struct hash_ipportnet4_elem *data)
{
const struct hash_ipportnet4_telem *tdata =
(const struct hash_ipportnet4_telem *)data;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
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_PROTO
#define IP_SET_HASH_WITH_NETS
#define PF 4
#define HOST_MASK 32
#include "ip_set_ahash.h"
static int
hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data =
{ .cidr = h->nets[0].cidr || HOST_MASK };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
data.ip2 &= NETMASK(data.cidr);
return adtfn(set, &data, h->timeout);
}
static const struct nla_policy
hash_ipportnet_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
u32 ip, ip_to, p, port, port_to;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportnet_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
if (ret)
return ret;
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP2, &data.ip2);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR2])
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!data.cidr)
return -IPSET_ERR_INVALID_CIDR;
data.ip2 &= NETMASK(data.cidr);
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR]
|| tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to);
if (ret)
return ret;
ip_to = ntohl(ip_to);
if (ip > ip_to)
swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
port = ntohs(data.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
} else
port_to = port;
for (; !before(ip_to, ip); ip++)
for (p = port; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static bool
hash_ipportnet_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_ipportnet6_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
u8 cidr;
u8 proto;
};
struct hash_ipportnet6_telem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
u8 cidr;
u8 proto;
unsigned long timeout;
};
static inline bool
hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
const struct hash_ipportnet6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0
&& ip1->cidr == ip2->cidr
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto;
}
static inline bool
hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
const struct hash_ipportnet6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportnet6_data_swap(struct hash_ipportnet6_elem *dst,
struct hash_ipportnet6_elem *src)
{
struct hash_ipportnet6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
{
elem->proto = 0;
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= NETMASK6(prefix)[0];
ip->ip6[1] &= NETMASK6(prefix)[1];
ip->ip6[2] &= NETMASK6(prefix)[2];
ip->ip6[3] &= NETMASK6(prefix)[3];
}
static inline void
hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
{
ip6_netmask(&elem->ip2, cidr);
elem->cidr = cidr;
}
static inline bool
hash_ipportnet6_data_list(struct sk_buff *skb,
const struct hash_ipportnet6_elem *data)
{
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_ipportnet6_data_tlist(struct sk_buff *skb,
const struct hash_ipportnet6_elem *data)
{
const struct hash_ipportnet6_telem *e =
(const struct hash_ipportnet6_telem *)data;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
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 int
hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data =
{ .cidr = h->nets[0].cidr || HOST_MASK };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
ip6_netmask(&data.ip2, data.cidr);
return adtfn(set, &data, h->timeout);
}
static int
hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
u32 port, port_to;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportnet_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
if (ret)
return ret;
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP2, &data.ip2);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR2])
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!data.cidr)
return -IPSET_ERR_INVALID_CIDR;
ip6_netmask(&data.ip2, data.cidr);
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
/* Create hash:ip type of sets */
static const struct nla_policy
hash_ipportnet_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
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 (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ipportnet_create_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
* (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
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_ipportnet4_tvariant
: &hash_ipportnet6_tvariant;
if (set->family == AF_INET)
hash_ipportnet4_gc_init(set);
else
hash_ipportnet6_gc_init(set);
} else {
set->variant = set->family == AF_INET
? &hash_ipportnet4_variant : &hash_ipportnet6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
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_ipportnet_type __read_mostly = {
.name = "hash:ip,port,net",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC,
.revision = 0,
.create = hash_ipportnet_create,
.me = THIS_MODULE,
};
static int __init
hash_ipportnet_init(void)
{
return ip_set_type_register(&hash_ipportnet_type);
}
static void __exit
hash_ipportnet_fini(void)
{
ip_set_type_unregister(&hash_ipportnet_type);
}
module_init(hash_ipportnet_init);
module_exit(hash_ipportnet_fini);

View File

@@ -0,0 +1,485 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the hash:net type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <linux/netfilter.h>
#include "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 type of IP sets");
MODULE_ALIAS("ip_set_hash:net");
/* Type specific function prefix */
#define TYPE hash_net
static bool
hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_net4_same_set hash_net_same_set
#define hash_net6_same_set hash_net_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
struct hash_net4_elem {
u32 ip;
u16 padding0;
u8 padding1;
u8 cidr;
};
/* Member elements with timeout support */
struct hash_net4_telem {
u32 ip;
u16 padding0;
u8 padding1;
u8 cidr;
unsigned long timeout;
};
static inline bool
hash_net4_data_equal(const struct hash_net4_elem *ip1,
const struct hash_net4_elem *ip2)
{
return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr;
}
static inline bool
hash_net4_data_isnull(const struct hash_net4_elem *elem)
{
return elem->cidr == 0;
}
static inline void
hash_net4_data_copy(struct hash_net4_elem *dst,
const struct hash_net4_elem *src)
{
dst->ip = src->ip;
dst->cidr = src->cidr;
}
static inline void
hash_net4_data_swap(struct hash_net4_elem *dst,
struct hash_net4_elem *src)
{
swap(dst->ip, src->ip);
swap(dst->cidr, src->cidr);
}
static inline void
hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr)
{
elem->ip &= NETMASK(cidr);
elem->cidr = cidr;
}
/* Zero CIDR values cannot be stored */
static inline void
hash_net4_data_zero_out(struct hash_net4_elem *elem)
{
elem->cidr = 0;
}
static inline bool
hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
{
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
{
const struct hash_net4_telem *tdata =
(const struct hash_net4_telem *)data;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr);
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 PF 4
#define HOST_MASK 32
#include "ip_set_ahash.h"
static int
hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= NETMASK(data.cidr);
return adtfn(set, &data, h->timeout);
}
static const struct nla_policy hash_net_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_net_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &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;
data.ip &= NETMASK(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]);
}
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
static bool
hash_net_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_net6_elem {
union nf_inet_addr ip;
u16 padding0;
u8 padding1;
u8 cidr;
};
struct hash_net6_telem {
union nf_inet_addr ip;
u16 padding0;
u8 padding1;
u8 cidr;
unsigned long timeout;
};
static inline bool
hash_net6_data_equal(const struct hash_net6_elem *ip1,
const struct hash_net6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ip1->cidr == ip2->cidr;
}
static inline bool
hash_net6_data_isnull(const struct hash_net6_elem *elem)
{
return elem->cidr == 0;
}
static inline void
hash_net6_data_copy(struct hash_net6_elem *dst,
const struct hash_net6_elem *src)
{
ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
dst->cidr = src->cidr;
}
static inline void
hash_net6_data_swap(struct hash_net6_elem *dst, struct hash_net6_elem *src)
{
struct hash_net6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_net6_data_zero_out(struct hash_net6_elem *elem)
{
elem->cidr = 0;
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= NETMASK6(prefix)[0];
ip->ip6[1] &= NETMASK6(prefix)[1];
ip->ip6[2] &= NETMASK6(prefix)[2];
ip->ip6[3] &= NETMASK6(prefix)[3];
}
static inline void
hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
{
ip6_netmask(&elem->ip, cidr);
elem->cidr = cidr;
}
static inline bool
hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data)
{
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
{
const struct hash_net6_telem *e =
(const struct hash_net6_telem *)data;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr);
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 int
hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
return adtfn(set, &data, h->timeout);
}
static int
hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_net_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
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]);
}
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
static const struct nla_policy
hash_net_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
struct ip_set_hash *h;
u8 hbits;
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_net_create_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
* (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
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_net4_tvariant : &hash_net6_tvariant;
if (set->family == AF_INET)
hash_net4_gc_init(set);
else
hash_net6_gc_init(set);
} else {
set->variant = set->family == AF_INET
? &hash_net4_variant : &hash_net6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
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_net_type __read_mostly = {
.name = "hash:net",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.create = hash_net_create,
.me = THIS_MODULE,
};
static int __init
hash_net_init(void)
{
return ip_set_type_register(&hash_net_type);
}
static void __exit
hash_net_fini(void)
{
ip_set_type_unregister(&hash_net_type);
}
module_init(hash_net_init);
module_exit(hash_net_fini);

View File

@@ -0,0 +1,605 @@
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the hash:net,port type */
#include "ip_set_kernel.h"
#include "jhash.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <linux/netfilter.h>
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_getport.h"
#include "ip_set_hash.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("hash:net,port type of IP sets");
MODULE_ALIAS("ip_set_hash:net,port");
/* Type specific function prefix */
#define TYPE hash_netport
static bool
hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_netport4_same_set hash_netport_same_set
#define hash_netport6_same_set hash_netport_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
struct hash_netport4_elem {
u32 ip;
u16 port;
u8 proto;
u8 cidr;
};
/* Member elements with timeout support */
struct hash_netport4_telem {
u32 ip;
u16 port;
u8 proto;
u8 cidr;
unsigned long timeout;
};
static inline bool
hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
const struct hash_netport4_elem *ip2)
{
return ip1->ip == ip2->ip
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto
&& ip1->cidr == ip2->cidr;
}
static inline bool
hash_netport4_data_isnull(const struct hash_netport4_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_netport4_data_copy(struct hash_netport4_elem *dst,
const struct hash_netport4_elem *src)
{
dst->ip = src->ip;
dst->port = src->port;
dst->proto = src->proto;
dst->cidr = src->cidr;
}
static inline void
hash_netport4_data_swap(struct hash_netport4_elem *dst,
struct hash_netport4_elem *src)
{
swap(dst->ip, src->ip);
swap(dst->port, src->port);
swap(dst->proto, src->proto);
swap(dst->cidr, src->cidr);
}
static inline void
hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr)
{
elem->ip &= NETMASK(cidr);
elem->cidr = cidr;
}
static inline void
hash_netport4_data_zero_out(struct hash_netport4_elem *elem)
{
elem->proto = 0;
}
static inline bool
hash_netport4_data_list(struct sk_buff *skb,
const struct hash_netport4_elem *data)
{
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_netport4_data_tlist(struct sk_buff *skb,
const struct hash_netport4_elem *data)
{
const struct hash_netport4_telem *tdata =
(const struct hash_netport4_telem *)data;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
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_PROTO
#define IP_SET_HASH_WITH_NETS
#define PF 4
#define HOST_MASK 32
#include "ip_set_ahash.h"
static int
hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = {
.cidr = h->nets[0].cidr || HOST_MASK };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= NETMASK(data.cidr);
return adtfn(set, &data, h->timeout);
}
static const struct nla_policy
hash_netport_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_netport4_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = { .cidr = HOST_MASK };
u32 port, port_to;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_netport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &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;
data.ip &= NETMASK(data.cidr);
if (tb[IPSET_ATTR_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
break;
default:
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static bool
hash_netport_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_netport6_elem {
union nf_inet_addr ip;
u16 port;
u8 proto;
u8 cidr;
};
struct hash_netport6_telem {
union nf_inet_addr ip;
u16 port;
u8 proto;
u8 cidr;
unsigned long timeout;
};
static inline bool
hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
const struct hash_netport6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ip1->port == ip2->port
&& ip1->proto == ip2->proto
&& ip1->cidr == ip2->cidr;
}
static inline bool
hash_netport6_data_isnull(const struct hash_netport6_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_netport6_data_copy(struct hash_netport6_elem *dst,
const struct hash_netport6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_netport6_data_swap(struct hash_netport6_elem *dst,
struct hash_netport6_elem *src)
{
struct hash_netport6_elem tmp;
memcpy(&tmp, dst, sizeof(tmp));
memcpy(dst, src, sizeof(tmp));
memcpy(src, &tmp, sizeof(tmp));
}
static inline void
hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
{
elem->proto = 0;
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= NETMASK6(prefix)[0];
ip->ip6[1] &= NETMASK6(prefix)[1];
ip->ip6[2] &= NETMASK6(prefix)[2];
ip->ip6[3] &= NETMASK6(prefix)[3];
}
static inline void
hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr)
{
ip6_netmask(&elem->ip, cidr);
elem->cidr = cidr;
}
static inline bool
hash_netport6_data_list(struct sk_buff *skb,
const struct hash_netport6_elem *data)
{
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
return 1;
}
static inline bool
hash_netport6_data_tlist(struct sk_buff *skb,
const struct hash_netport6_elem *data)
{
const struct hash_netport6_telem *e =
(const struct hash_netport6_telem *)data;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
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 int
hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = {
.cidr = h->nets[0].cidr || HOST_MASK };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
return adtfn(set, &data, h->timeout);
}
static int
hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct ip_set_hash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = { .cidr = HOST_MASK };
u32 port, port_to;
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_netport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
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_PORT])
data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMPV6:
break;
default:
data.port = 0;
break;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST
|| !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP)
|| !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
/* Create hash:ip type of sets */
static const struct nla_policy
hash_netport_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_netport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
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 (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_netport_create_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
* (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
h->table = ip_set_alloc(
sizeof(struct htable)
+ jhash_size(hbits) * sizeof(struct hbucket),
GFP_KERNEL);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
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_netport4_tvariant : &hash_netport6_tvariant;
if (set->family == AF_INET)
hash_netport4_gc_init(set);
else
hash_netport6_gc_init(set);
} else {
set->variant = set->family == AF_INET
? &hash_netport4_variant : &hash_netport6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
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_netport_type __read_mostly = {
.name = "hash:net,port",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC,
.revision = 0,
.create = hash_netport_create,
.me = THIS_MODULE,
};
static int __init
hash_netport_init(void)
{
return ip_set_type_register(&hash_netport_type);
}
static void __exit
hash_netport_fini(void)
{
ip_set_type_unregister(&hash_netport_type);
}
module_init(hash_netport_init);
module_exit(hash_netport_fini);

View File

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

View File

@@ -0,0 +1,27 @@
#ifndef __IP_SET_LIST_H
#define __IP_SET_LIST_H
/* List type specific error codes */
enum {
/* Set name to be added/deleted/tested does not exist. */
IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC,
/* list:set type is not permitted to add */
IPSET_ERR_LOOP,
/* Missing reference set */
IPSET_ERR_BEFORE,
/* Reference set does not exist */
IPSET_ERR_NAMEREF,
/* Set is full */
IPSET_ERR_LIST_FULL,
/* Reference set is not added to the set */
IPSET_ERR_REF_EXIST,
};
#ifdef __KERNEL__
#define IP_SET_LIST_DEFAULT_SIZE 8
#define IP_SET_LIST_MIN_SIZE 4
#endif /* __KERNEL__ */
#endif /* __IP_SET_LIST_H */

View File

@@ -0,0 +1,589 @@
/* Copyright (C) 2008-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the list:set type */
#include "ip_set_kernel.h"
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include "ip_set.h"
#include "ip_set_timeout.h"
#include "ip_set_list.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("list:set type of IP sets");
MODULE_ALIAS("ip_set_list:set");
/* Member elements without and with timeout */
struct set_elem {
ip_set_id_t id;
};
struct set_telem {
ip_set_id_t id;
unsigned long timeout;
};
/* Type structure */
struct list_set {
size_t dsize; /* element size */
u32 size; /* size of set list array */
u32 timeout; /* timeout value */
struct timer_list gc; /* garbage collection */
struct set_elem members[0]; /* the set members */
};
static inline struct set_elem *
list_set_elem(const struct list_set *map, u32 id)
{
return (struct set_elem *)((char *)map->members + id * map->dsize);
}
static inline bool
list_set_timeout(const struct list_set *map, u32 id)
{
const struct set_telem *elem =
(const struct set_telem *) list_set_elem(map, id);
return ip_set_timeout_test(elem->timeout);
}
static inline bool
list_set_expired(const struct list_set *map, u32 id)
{
const struct set_telem *elem =
(const struct set_telem *) list_set_elem(map, id);
return ip_set_timeout_expired(elem->timeout);
}
static inline int
list_set_exist(const struct set_telem *elem)
{
return elem->id != IPSET_INVALID_ID
&& !ip_set_timeout_expired(elem->timeout);
}
/* Set list without and with timeout */
static int
list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct list_set *map = set->data;
struct set_elem *elem;
u32 i;
int ret;
for (i = 0; i < map->size; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID)
return 0;
if (with_timeout(map->timeout) && list_set_expired(map, i))
continue;
switch (adt) {
case IPSET_TEST:
ret = ip_set_test(elem->id, skb, pf, dim, flags);
if (ret > 0)
return ret;
break;
case IPSET_ADD:
ret = ip_set_add(elem->id, skb, pf, dim, flags);
if (ret == 0)
return ret;
break;
case IPSET_DEL:
ret = ip_set_del(elem->id, skb, pf, dim, flags);
if (ret == 0)
return ret;
break;
default:
break;
}
}
return -EINVAL;
}
static const struct nla_policy list_set_adt_policy[IPSET_ATTR_ADT_MAX+1] = {
[IPSET_ATTR_NAME] = { .type = NLA_STRING,
.len = IPSET_MAXNAMELEN },
[IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
.len = IPSET_MAXNAMELEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
};
static inline bool
next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
{
const struct set_elem *elem;
if (i + 1 < map->size) {
elem = list_set_elem(map, i + 1);
return !!(elem->id == id
&& !(with_timeout(map->timeout)
&& list_set_expired(map, i + 1)));
}
return 0;
}
static inline void
list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
{
struct set_elem *e;
for (; i < map->size; i++) {
e = list_set_elem(map, i);
swap(e->id, id);
if (e->id == IPSET_INVALID_ID)
break;
}
}
static inline void
list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
unsigned long timeout)
{
struct set_telem *e;
for (; i < map->size; i++) {
e = (struct set_telem *)list_set_elem(map, i);
swap(e->id, id);
if (e->id == IPSET_INVALID_ID)
break;
swap(e->timeout, timeout);
}
}
static int
list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
unsigned long timeout)
{
const struct set_elem *e = list_set_elem(map, i);
if (i == map->size - 1 && e->id != IPSET_INVALID_ID)
/* Last element replaced: e.g. add new,before,last */
ip_set_put_byindex(e->id);
if (with_timeout(map->timeout))
list_elem_tadd(map, i, id, timeout);
else
list_elem_add(map, i, id);
return 0;
}
static int
list_set_del(struct list_set *map, ip_set_id_t id, u32 i)
{
struct set_elem *a = list_set_elem(map, i), *b;
ip_set_put_byindex(id);
for (; i < map->size - 1; i++) {
b = list_set_elem(map, i + 1);
a->id = b->id;
if (with_timeout(map->timeout))
((struct set_telem *)a)->timeout =
((struct set_telem *)b)->timeout;
a = b;
if (a->id == IPSET_INVALID_ID)
break;
}
/* Last element */
a->id = IPSET_INVALID_ID;
return 0;
}
static int
list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct list_set *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
bool with_timeout = with_timeout(map->timeout);
int before = 0;
u32 timeout = map->timeout;
ip_set_id_t id, refid = IPSET_INVALID_ID;
const struct set_elem *elem;
struct ip_set *s;
u32 i;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
list_set_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
if (tb[IPSET_ATTR_NAME]) {
id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
if (id == IPSET_INVALID_ID)
return -IPSET_ERR_NAME;
/* "Loop detection" */
if (s->type->features & IPSET_TYPE_NAME) {
ret = -IPSET_ERR_LOOP;
goto finish;
}
} else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
before = f & IPSET_FLAG_BEFORE;
}
if (before && !tb[IPSET_ATTR_NAMEREF]) {
ret = -IPSET_ERR_BEFORE;
goto finish;
}
if (tb[IPSET_ATTR_NAMEREF]) {
refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
&s);
if (refid == IPSET_INVALID_ID) {
ret = -IPSET_ERR_NAMEREF;
goto finish;
}
if (!before)
before = -1;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout) {
ret = -IPSET_ERR_TIMEOUT;
goto finish;
}
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
switch (adt) {
case IPSET_TEST:
for (i = 0; i < map->size && !ret; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID
|| (before != 0 && i + 1 >= map->size))
break;
else if (with_timeout && list_set_expired(map, i))
continue;
else if (before > 0 && elem->id == id)
ret = next_id_eq(map, i, refid);
else if (before < 0 && elem->id == refid)
ret = next_id_eq(map, i, id);
else if (before == 0 && elem->id == id)
ret = 1;
}
break;
case IPSET_ADD:
for (i = 0; i < map->size && !ret; i++) {
elem = list_set_elem(map, i);
if (elem->id == id
&& !(with_timeout && list_set_expired(map, i)))
ret = -IPSET_ERR_EXIST;
}
if (ret == -IPSET_ERR_EXIST)
break;
ret = -IPSET_ERR_LIST_FULL;
for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID)
ret = before != 0 ? -IPSET_ERR_REF_EXIST
: list_set_add(map, i, id, timeout);
else if (elem->id != refid)
continue;
else if (with_timeout && list_set_expired(map, i))
ret = -IPSET_ERR_REF_EXIST;
else if (before)
ret = list_set_add(map, i, id, timeout);
else if (i + 1 < map->size)
ret = list_set_add(map, i + 1, id, timeout);
}
break;
case IPSET_DEL:
ret = -IPSET_ERR_EXIST;
for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID) {
ret = before != 0 ? -IPSET_ERR_REF_EXIST
: -IPSET_ERR_EXIST;
break;
} else if (with_timeout && list_set_expired(map, i))
continue;
else if (elem->id == id
&& (before == 0
|| (before > 0
&& next_id_eq(map, i, refid))))
ret = list_set_del(map, id, i);
else if (before < 0
&& elem->id == refid
&& next_id_eq(map, i, id))
ret = list_set_del(map, id, i + 1);
}
break;
default:
break;
}
finish:
if (refid != IPSET_INVALID_ID)
ip_set_put_byindex(refid);
if (adt != IPSET_ADD || ret)
ip_set_put_byindex(id);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
static void
list_set_flush(struct ip_set *set)
{
struct list_set *map = set->data;
struct set_elem *elem;
u32 i;
for (i = 0; i < map->size; i++) {
elem = list_set_elem(map, i);
if (elem->id != IPSET_INVALID_ID) {
ip_set_put_byindex(elem->id);
elem->id = IPSET_INVALID_ID;
}
}
}
static void
list_set_destroy(struct ip_set *set)
{
struct list_set *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
list_set_flush(set);
kfree(map);
set->data = NULL;
}
static int
list_set_head(struct ip_set *set, struct sk_buff *skb)
{
const struct list_set *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size));
if (with_timeout(map->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->size * map->dsize));
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EFAULT;
}
static int
list_set_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct list_set *map = set->data;
struct nlattr *atd, *nested;
u32 i, first = cb->args[2];
const struct set_elem *e;
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EFAULT;
for (; cb->args[2] < map->size; cb->args[2]++) {
i = cb->args[2];
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
goto finish;
if (with_timeout(map->timeout) && list_set_expired(map, i))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (i == first) {
nla_nest_cancel(skb, atd);
return -EFAULT;
} else
goto nla_put_failure;
}
NLA_PUT_STRING(skb, IPSET_ATTR_NAME,
ip_set_name_byindex(e->id));
if (with_timeout(map->timeout)) {
const struct set_telem *te =
(const struct set_telem *) e;
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(te->timeout)));
}
ipset_nest_end(skb, nested);
}
finish:
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
list_set_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct list_set *x = a->data;
const struct list_set *y = b->data;
return x->size == y->size
&& x->timeout == y->timeout;
}
static const struct ip_set_type_variant list_set = {
.kadt = list_set_kadt,
.uadt = list_set_uadt,
.destroy = list_set_destroy,
.flush = list_set_flush,
.head = list_set_head,
.list = list_set_list,
.same_set = list_set_same_set,
};
static void
list_set_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct list_set *map = set->data;
struct set_telem *e;
u32 i;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (i = map->size - 1; i >= 0; i--) {
e = (struct set_telem *) list_set_elem(map, i);
if (e->id != IPSET_INVALID_ID
&& list_set_expired(map, i))
list_set_del(map, e->id, i);
}
read_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static inline void
list_set_gc_init(struct ip_set *set)
{
struct list_set *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = list_set_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
/* Create list:set type of sets */
static const struct nla_policy
list_set_create_policy[IPSET_ATTR_CREATE_MAX+1] = {
[IPSET_ATTR_SIZE] = { .type = NLA_U32 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static inline bool
init_list_set(struct ip_set *set, u32 size, size_t dsize,
unsigned long timeout)
{
struct list_set *map;
struct set_elem *e;
u32 i;
map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
if (!map)
return false;
map->size = size;
map->dsize = dsize;
map->timeout = timeout;
set->data = map;
for (i = 0; i < size; i++) {
e = list_set_elem(map, i);
e->id = IPSET_INVALID_ID;
}
return true;
}
static int
list_set_create(struct ip_set *set, struct nlattr *head, int len,
u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
u32 size = IP_SET_LIST_DEFAULT_SIZE;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
list_set_create_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_SIZE])
size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
if (size < IP_SET_LIST_MIN_SIZE)
size = IP_SET_LIST_MIN_SIZE;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!init_list_set(set, size, sizeof(struct set_telem),
ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
return -ENOMEM;
list_set_gc_init(set);
} else {
if (!init_list_set(set, size, sizeof(struct set_elem),
IPSET_NO_TIMEOUT))
return -ENOMEM;
}
set->variant = &list_set;
return 0;
}
static struct ip_set_type list_set_type __read_mostly = {
.name = "list:set",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.create = list_set_create,
.me = THIS_MODULE,
};
static int __init
list_set_init(void)
{
return ip_set_type_register(&list_set_type);
}
static void __exit
list_set_fini(void)
{
ip_set_type_unregister(&list_set_type);
}
module_init(list_set_init);
module_exit(list_set_fini);

View File

@@ -0,0 +1,127 @@
#ifndef _IP_SET_TIMEOUT_H
#define _IP_SET_TIMEOUT_H
/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifdef __KERNEL__
/* How often should the gc be run by default */
#define IPSET_GC_TIME (3 * 60)
/* Timeout period depending on the timeout value of the given set */
#define IPSET_GC_PERIOD(timeout) \
((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1)
/* Set is defined without timeout support: timeout value may be 0 */
#define IPSET_NO_TIMEOUT UINT_MAX
#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT)
static inline unsigned int
ip_set_timeout_uget(struct nlattr *tb)
{
unsigned int timeout = ip_set_get_h32(tb);
/* Userspace supplied TIMEOUT parameter: adjust crazy size */
return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout;
}
#ifdef IP_SET_BITMAP_TIMEOUT
/* Bitmap specific timeout constants and macros for the entries */
/* Bitmap entry is unset */
#define IPSET_ELEM_UNSET 0
/* Bitmap entry is set with no timeout value */
#define IPSET_ELEM_PERMANENT (UINT_MAX/2)
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
return timeout != IPSET_ELEM_UNSET
&& (timeout == IPSET_ELEM_PERMANENT
|| time_after(timeout, jiffies));
}
static inline bool
ip_set_timeout_expired(unsigned long timeout)
{
return timeout != IPSET_ELEM_UNSET
&& timeout != IPSET_ELEM_PERMANENT
&& time_before(timeout, jiffies);
}
static inline unsigned long
ip_set_timeout_set(u32 timeout)
{
unsigned long t;
if (!timeout)
return IPSET_ELEM_PERMANENT;
t = timeout * HZ + jiffies;
if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT)
/* Bingo! */
t++;
return t;
}
static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
}
#else
/* Hash specific timeout constants and macros for the entries */
/* Hash entry is set with no timeout value */
#define IPSET_ELEM_PERMANENT 0
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT
|| time_after(timeout, jiffies);
}
static inline bool
ip_set_timeout_expired(unsigned long timeout)
{
return timeout != IPSET_ELEM_PERMANENT
&& time_before(timeout, jiffies);
}
static inline unsigned long
ip_set_timeout_set(u32 timeout)
{
unsigned long t;
if (!timeout)
return IPSET_ELEM_PERMANENT;
t = timeout * HZ + jiffies;
if (t == IPSET_ELEM_PERMANENT)
/* Bingo! :-) */
t++;
return t;
}
static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
}
#endif /* ! IP_SET_BITMAP_TIMEOUT */
#endif /* __KERNEL__ */
#endif /* _IP_SET_TIMEOUT_H */

748
extensions/ipset-5/ipset.8 Normal file
View File

@@ -0,0 +1,748 @@
.\" Man page written by 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 as published by
.\" the Free Software Foundation; either version 2 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program; if not, write to the Free Software
.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
.TH "IPSET" "8" "Oct 15, 2010" "Jozsef Kadlecsik" ""
.SH "NAME"
ipset \(em administration tool for IP sets
.SH "SYNOPSIS"
\fBipset\fR [ \fIOPTIONS\fR ] \fICOMMAND\fR [ \fICOMMAND\-OPTIONS\fR ]
.PP
COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR | \fBlist\fR | \fBsave\fR | \fBrestore\fR | \fBflush\fR | \fBrename\fR | \fBswap\fR | \fBhelp\fR | \fBversion\fR | \fB\-\fR }
.PP
\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR }
.PP
\fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ]
.PP
\fBipset\fR \fBadd\fR \fISETNAME\fR \fIADD\-ENTRY\fR [ \fIADD\-OPTIONS\fR ]
.PP
\fBipset\fR \fBdel\fR \fISETNAME\fR \fIDEL\-ENTRY\fR [ \fIDEL\-OPTIONS\fR ]
.PP
\fBipset\fR \fBtest\fR \fISETNAME\fR \fITEST\-ENTRY\fR [ \fITEST\-OPTIONS\fR ]
.PP
\fBipset\fR \fBdestroy\fR [ \fISETNAME\fR ]
.PP
\fBipset\fR \fBlist\fR [ \fISETNAME\fR ]
.PP
\fBipset\fR \fBsave\fR [ \fISETNAME\fR ]
.PP
\fBipset\fR \fBrestore\fR
.PP
\fBipset\fR \fBflush\fR [ \fISETNAME\fR ]
.PP
\fBipset\fR \fBrename\fR \fISETNAME\-FROM\fR \fISETNAME\-TO\fR
.PP
\fBipset\fR \fBswap\fR \fISETNAME\-FROM\fR \fISETNAME\-TO\fR
.PP
\fBipset\fR \fBhelp\fR [ \fITYPENAME\fR ]
.PP
\fBipset\fR \fBversion\fR
.PP
\fBipset\fR \fB\-\fR
.SH "DESCRIPTION"
\fBipset\fR
is used to set up, maintain and inspect so called IP sets in the Linux
kernel. Depending on the type of the set, an IP set may store IP(v4/v6)
addresses, (TCP/UDP) port numbers, IP and MAC address pairs, IP address
and port number pairs, etc. See the set type definitions below.
.PP
\fBIptables\fR
matches and targets referring to sets create references, which
protect the given sets in the kernel. A set cannot be destroyed
while there is a single reference pointing to it.
.SH "OPTIONS"
The options that are recognized by
\fBipset\fR
can be divided into several different groups.
.SS COMMANDS
These options specify the desired action to perform. Only one of them
can be specified on the command line unless otherwise specified below.
For all the long versions of the command names, you need to use only enough
letters to ensure that
\fBipset\fR
can differentiate it from all other commands. The
\fBipset\fR
parser follows the order here when looking for the shortest match
in the long command names.
.TP
\fBn\fP, \fBcreate\fP \fISETNAME\fP \fITYPENAME\fP [ \fICREATE\-OPTIONS\fP ]
Create a set identified with setname and specified type. The type may require
type specific options. If the
\fB\-exist\fR
option is specified,
\fBipset\fR
ignores the error otherwise raised when the same set (setname and create parameters
are identical) already exists.
.TP
\fBadd\fP \fISETNAME\fP \fIADD\-ENTRY\fP [ \fIADD\-OPTIONS\fP ]
Add a given entry to the set. If the
\fB\-exist\fR
option is specified,
\fBipset\fR
ignores if the entry already added to the set.
.TP
\fBdel\fP \fISETNAME\fP \fIDEL\-ENTRY\fP [ \fIDEL\-OPTIONS\fP ]
Delete an entry from a set. If the
\fB\-exist\fR
option is specified,
\fBipset\fR
ignores if the entry does not added to (already expired from) the set.
.TP
\fBtest\fP \fISETNAME\fP \fITEST\-ENTRY\fP [ \fITEST\-OPTIONS\fP ]
Test wether an entry is in a set or not. Exit status number is zero
if the tested entry is in the set and nonzero if it is missing from
the set.
.TP
\fBx\fP, \fBdestroy\fP [ \fISETNAME\fP ]
Destroy the specified set or all the sets if none is given.
If the set has got reference(s), nothing is done and no set destroyed.
.TP
\fBlist\fP [ \fISETNAME\fP ]
List the header data and the entries for the specified set, or for
all sets if none is given. The
\fB\-resolve\fP
option can be used to force name lookups (which may be slow). When the
\fB\-sorted\fP
option is given, the entries are listed sorted (if the given set
type supports the operation). The option
\fB\-output\fR
can be used to control the format of the listing:
\fBplain\fR, \fBsave\fR or \fBxml\fR.
The default is
\fBplain\fR.
.TP
\fBsave\fP [ \fISETNAME\fP ]
Save the given set, or all sets if none is given
to stdout in a format that
\fBrestore\fP
can read.
.TP
\fBrestore\fP
Restore a saved session generated by
\fBsave\fP.
The saved session can be fed from stdin.
.TP
\fBflush\fP [ \fISETNAME\fP ]
Flush all entries from the specified set or flush
all sets if none is given.
.TP
\fBe\fP, \fBrename\fP \fISETNAME\-FROM\fP \fISETNAME\-TO\fP
Rename a set. Set identified by
\fISETNAME\-TO\fR
must not exist.
.TP
\fBw\fP, \fBswap\fP \fISETNAME\-FROM\fP \fISETNAME\-TO\fP
Swap the content of two sets, or in another words,
exchange the name of two sets. The referred sets must exist and
identical type of sets can be swapped only.
.TP
\fBhelp\fP [ \fITYPENAME\fP ]
Print help and set type specific help if
\fITYPENAME\fR
is specified.
.TP
\fBversion\fP
Print program version.
.TP
\fB\-\fP
If a dash is specified as command, then
\fBipset\fR
enters a simple interactive mode and the commands are read from the standard input.
The interactive mode can be finished by entering the pseudo\-command
\fBquit\fR.
.P
.SS "OTHER OPTIONS"
The following additional options can be specified. The long option names
cannot be abbreviated.
.TP
\fB\-!\fP, \fB\-exist\fP
Ignore errors when the exactly the same set is to be created or already
added entry is added or missing entry is deleted.
.TP
\fB\-o\fP, \fB\-output\fP { \fBplain\fR | \fBsave\fR | \fBxml\fR }
Select the output format to the
\fBlist\fR
command.
.TP
\fB\-q\fP, \fB\-quiet\fP
Suppress any output to stdout and stderr.
\fBipset\fR
will still exit with error if it cannot continue.
.TP
\fB\-r\fP, \fB\-resolve\fP
When listing sets, enforce name lookup. The
program will try to display the IP entries resolved to
host names which requires
\fBslow\fR
DNS lookups.
.TP
\fB\-s\fP, \fB\-sorted\fP
Sorted output. When listing sets entries are listed sorted. Not supported yet.
.SH "SET TYPES"
A set type comprises of the storage method by which the data is stored and
the data type(s) which are stored in the set. Therefore the
\fITYPENAME\fR
parameter of the
\fBcreate\fR
command follows the syntax
\fITYPENAME\fR := \fImethod\fR\fB:\fR\fIdatatype\fR[\fB,\fR\fIdatatype\fR[\fB,\fR\fIdatatype\fR]]
where the current list of the methods are
\fBbitmap\fR, \fBhash\fR, and \fBlist\fR and the possible data types
are \fBip\fR, \fBmac\fR and \fBport\fR. The dimension of a set
is equal to the number of data types in its type name.
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
ipset add foo ipaddr,portnum,ipaddr
The \fBbitmap\fR and \fBlist\fR types use a fixed sized storage. The \fBhash\fR
types use a hash to store the elements. In order to avoid clashes in the hash,
a limited number of chaining, and if that is exhausted, the doubling of the hash size
is performed when adding entries by the
\fBipset\fR
command. When entries added by the
\fBSET\fR
target of
\fBiptables/ip6tables\fR,
then the hash size is fixed and the set won't be duplicated, even if the new
entry cannot be added to the set.
All set types support the optional
\fBtimeout\fR \fIvalue\fR
parameter when creating a set and adding entries. The value of the \fBtimeout\fR
parameter for the \fBcreate\fR command means the default timeout value (in seconds)
for new entries. If a set is created with timeout support, then the same
\fBtimeout\fR option can be used to specify non\-default timeout values
when adding entries. Zero timeout value means the entry is added permanent to the set.
.SS bitmap:ip
The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host
(default) or IPv4 network addresses. A \fBbitmap:ip\fR type of set can store up
to 65536 entries.
.PP
\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfromip\fP\-\fItoip\fR|\fIip\fR/\fIcidr\fR [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := { \fIip\fR | \fIfromip\fR\-\fItoip\fR | \fIip\fR/\fIcidr\fR }
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := { \fIip\fR | \fIfromip\fR\-\fItoip\fR | \fIip\fR/\fIcidr\fR }
.PP
\fITEST\-ENTRY\fR := \fIip\fR
.PP
Mandatory \fBcreate\fR options:
.TP
\fBrange\fP \fIfromip\fP\-\fItoip\fR|\fIip\fR/\fIcidr\fR
Create the set from the specified inclusive address range expressed in an
IPv4 address range or network. The size of the range (in entries) cannot exceed
the limit of maximum 65536 elements.
.PP
Optional \fBcreate\fR options:
.TP
\fBnetmask\fP \fIcidr\fP
When the optional \fBnetmask\fP parameter specified, network addresses will be
stored in the set instead of IP host addresses. The \fIcidr\fR prefix value must be
between 1\-32.
An IP address will be in the set if the network address, which is resulted by
masking the address with the specified netmask calculated from the prefix,
can be found in the set.
.PP
The \fBbitmap:ip\fR type supports adding or deleting multiple entries in one
command.
.PP
Examples:
.IP
ipset create foo bitmap:ip range 192.168.0.0/16
.IP
ipset add foo 192.168.1/24
.IP
ipset test foo 192.168.1.1
.SS bitmap:ip,mac
The \fBbitmap:ip,mac\fR set type uses a memory range to store IPv4 and a MAC address pairs. A \fBbitmap:ip,mac\fR type of set can store up to 65536 entries.
.PP
\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfromip\fP\-\fItoip\fR|\fIip\fR/\fIcidr\fR [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIip\fR[,\fImacaddr\fR]
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIip\fR[,\fImacaddr\fR]
.PP
\fITEST\-ENTRY\fR := \fIip\fR[,\fImacaddr\fR]
.PP
Mandatory options to use when creating a \fBbitmap:ip,mac\fR type of set:
.TP
\fBrange\fP \fIfromip\fP\-\fItoip\fR|\fIip\fR/\fIcidr\fR
Create the set from the specified inclusive address range expressed in an
IPv4 address range or network. The size of the range cannot exceed the limit
of maximum 65536 entries.
.PP
The \fBbitmap:ip,mac\fR type is exceptional in the sense that the MAC part can
be left out when adding/deleting/testing entries in the set. If we add an entry
without the MAC address specified, then when the first time the entry is
matched by the kernel, it will automatically fill out the missing MAC address with the
source MAC address from the packet. If the entry was specified with a timeout value,
the timer starts off when the IP and MAC address pair is complete.
.PP
Please note, the \fBset\fR match and \fBSET\fR target netfilter kernel modules
\fBalways\fR use the source MAC address from the packet to match, add or delete
entries from a \fBbitmap:ip,mac\fR type of set.
.PP
Examples:
.IP
ipset create foo bitmap:ip,mac range 192.168.0.0/16
.IP
ipset add foo 192.168.1.1,12:34:56:78:9A:BC
.IP
ipset test foo 192.168.1.1
.SS bitmap:port
The \fBbitmap:port\fR set type uses a memory range to store port numbers
and such a set can store up to 65536 ports.
.PP
\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfromport\fP\-\fItoport [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := { \fIport\fR | \fIfromport\fR\-\fItoport\fR }
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := { \fIport\fR | \fIfromport\fR\-\fItoport\fR }
.PP
\fITEST\-ENTRY\fR := \fIport\fR
.PP
Mandatory options to use when creating a \fBbitmap:port\fR type of set:
.TP
\fBrange\fP \fIfromport\fP\-\fItoport\fR
Create the set from the specified inclusive port range.
.PP
Examples:
.IP
ipset create foo bitmap:port range 0\-1024
.IP
ipset add foo 80
.IP
ipset test foo 80
.SS hash:ip
The \fBhash:ip\fR set type uses a hash to store IP host addresses (default) or
network addresses. Zero valued IP address cannot be stored in a \fBhash:ip\fR
type of set.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIipaddr\fR
.PP
\fITEST\-ENTRY\fR := \fIipaddr\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.
.TP
\fBnetmask\fP \fIcidr\fP
When the optional \fBnetmask\fP parameter specified, network addresses will be
stored in the set instead of IP host addresses. The \fIcidr\fP prefix value must be
between 1\-32 for IPv4 and between 1\-128 for IPv6. An IP address will be in the set
if the network address, which is resulted by masking the address with the netmask
calculated from the prefix, can be found in the set.
.PP
For the \fBinet\fR family one can add or delete multiple entries by specifying
a range or a network:
.PP
\fIipaddr\fR := { \fIip\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIip\fR/\fIcidr\fR }
.PP
Examples:
.IP
ipset create foo hash:ip netmask 24
.IP
ipset add foo 192.168.1.1\-192.168.1.2
.IP
ipset test foo 192.168.1.2
.SS hash:net
The \fBhash:net\fR set type uses a hash to store different sized IP network addresses.
Network address with zero prefix size cannot be stored in this type of sets.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
.PP
\fITEST\-ENTRY\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
When adding/deleting/testing entries, if the cidr prefix parameter is not specified,
then the host prefix value is assumed. When adding/deleting entries, overlapping
elements are not checked.
.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 lookup time grows linearly with the number of the different prefix
values added to the set.
.PP
Examples:
.IP
ipset create foo hash:net
.IP
ipset add foo 192.168.0/24
.IP
ipset add foo 10.1.0.0/16
.IP
ipset test foo 192.168.0/24
.SS hash:ip,port
The \fBhash:ip,port\fR set type uses a hash to store IP address and port number pairs.
The port number is interpreted together with a protocol (default TCP) and zero
protocol number cannot be used.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\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 \fBinet\fR family one can add or delete multiple entries by specifying
a range or a network of IPv4 addresses in the IP address part of the entry:
.PP
\fIipaddr\fR := { \fIip\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIip\fR/\fIcidr\fR }
.PP
The
[\fIproto\fR:]\fIport\fR
part of the elements may be expressed in the following forms, where the range
variations are valid when adding or deleting entries:
.TP
\fIportname[\-portname]\fR
TCP port or range of ports expressed in TCP portname identifiers from /etc/services
.TP
\fIportnumber[\-portnumber]\fR
TCP port or range of ports expressed in TCP port numbers
.TP
\fBtcp\fR|\fBudp\fR:\fIportname\fR|\fIportnumber\fR[\-\fIportname\fR|\fIportnumber\fR]
TCP or UDP port or port range expressed in port name(s) or port number(s)
.TP
\fBicmp\fR:\fIcodename\fR|\fItype\fR/\fIcode\fR
ICMP codename or type/code. The supported ICMP codename identifiers can always
be listed by the help command.
.TP
\fBicmpv6\fR:\fIcodename\fR|\fItype\fR/\fIcode\fR
ICMPv6 codename or type/code. The supported ICMPv6 codename identifiers can always
be listed by the help command.
.TP
\fIproto\fR:0
All other protocols, as an identifier from /etc/protocols or number. The pseudo
port number must be zero.
.PP
The \fBhash:ip,port\fR type of sets require
two \fBsrc\fR/\fBdst\fR parameters of the \fBset\fR match and \fBSET\fR
target kernel modules.
.PP
Examples:
.IP
ipset create foo hash:ip,port
.IP
ipset add foo 192.168.1.0/24,80\-82
.IP
ipset add foo 192.168.1.1,udp:53
.IP
ipset add foo 192.168.1.1,ospf:0
.IP
ipset test foo 192.168.1.1,80
.SS hash:net,port
The \fBhash:net,port\fR set type uses a hash to store different sized IP network
address and port pairs. The port number is interpreted together with a protocol
(default TCP) and zero protocol number cannot be used. Network
address with zero prefix size is not accepted either.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
.PP
\fITEST\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\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
[\fIproto\fR:]\fIport\fR
part of the elements see the description at the
\fBhash:ip,port\fR set type.
.PP
When adding/deleting/testing entries, if the cidr prefix parameter is not specified,
then the host prefix value is assumed. When adding/deleting entries, overlapping
elements are not checked.
.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 lookup time grows linearly with the number of the different prefix
values added to the set.
.PP
Examples:
.IP
ipset create foo hash:net,port
.IP
ipset add foo 192.168.0/24,25
.IP
ipset add foo 10.1.0.0/16,80
.IP
ipset test foo 192.168.0/24,25
.SS hash:ip,port,ip
The \fBhash:ip,port,ip\fR set type uses a hash to store IP address, port number
and a second IP address triples. The port number is interpreted together with a
protocol (default TCP) and zero protocol number cannot be used.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR
.PP
\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR
.PP
For the first \fIipaddr\fR and
[\fIproto\fR:]\fIport\fR
parts of the elements see the descriptions at the
\fBhash:ip,port\fR set type.
.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
The \fBhash:ip,port,ip\fR type of sets require
three \fBsrc\fR/\fBdst\fR parameters of the \fBset\fR match and \fBSET\fR
target kernel modules.
.PP
Examples:
.IP
ipset create foo hash:ip,port,ip
.IP
ipset add foo 192.168.1.1,80,10.0.0.1
.IP
ipset test foo 192.168.1.1,udp:53,10.0.0.1
.SS hash:ip,port,net
The \fBhash:ip,port,net\fR set type uses a hash to store IP address, port number
and IP network address triples. The port number is interpreted together with a
protocol (default TCP) and zero protocol number cannot be used. Network
address with zero prefix size cannot be stored either.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
.PP
\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
.PP
For the first \fIipaddr\fR and
[\fIproto\fR:]\fIport\fR
parts of the elements see the descriptions at the
\fBhash:ip,port\fR set type.
.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
From the \fBset\fR netfilter match point of view the searching for a match
always starts from the smallest size of netblock (most specific
cidr) to the largest one (least specific cidr) added to the set.
When adding/deleting triples
to the set by the \fBSET\fR netfilter target, it will be
added/deleted by the most specific cidr which can be found in the
set, or by the host cidr value if the set is empty.
.PP
The lookup time grows linearly with the number of the different \fIcidr\fR
values added to the set.
.PP
The \fBhash:ip,port,net\fR type of sets require three \fBsrc\fR/\fBdst\fR parameters of
the \fBset\fR match and \fBSET\fR target kernel modules.
.PP
Examples:
.IP
ipset create foo hash:ip,port,net
.IP
ipset add foo 192.168.1,80,10.0.0/24
.IP
ipset add foo 192.168.2,25,10.1.0.0/16
.IP
ipset test foo 192.168.1,80.10.0.0/24
.SS list:set
The \fBlist:set\fR type uses a simple list in which you can store
set names.
.PP
\fICREATE\-OPTIONS\fR := [ \fBsize\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIADD\-ENTRY\fR := \fIsetname\fR [ { \fBbefore\fR | \fBafter\fR } \fIsetname\fR ]
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
\fIDEL\-ENTRY\fR := \fIsetname\fR [ { \fBbefore\fR | \fBafter\fR } \fIsetname\fR ]
.PP
\fITEST\-ENTRY\fR := \fIsetname\fR [ { \fBbefore\fR | \fBafter\fR } \fIsetname\fR ]
.PP
Optional \fBcreate\fR options:
.TP
\fBsize\fR \fIvalue\fR
The size of the list, the default is 8.
.PP
By the \fBipset\fR commad you can add, delete and test set names in a
\fBlist:set\fR type of set.
.PP
By the \fBset\fR match or \fBSET\fR target of netfilter
you can test, add or delete entries in the sets added to the \fBlist:set\fR
type of set. The match will try to find a matching entry in the sets and
the target will try to add an entry to the first set to which it can be added.
The number of direction options of the match and target are important: sets which
require more parameters than specified are skipped, while sets with equal
or less parameters are checked, elements added/deleted. For example if \fIa\fR and
\fIb\fR are \fBlist:set\fR type of sets then in the command
.IP
iptables \-m set \-\-match\-set a src,dst \-j SET \-\-add\-set b src,dst
.PP
the match and target will skip any set in \fIa\fR and \fIb\fR
which stores data triples, but will match all sets with single or double
data storage in \fIa\fR set and stop matching at the first successful set,
and add src to the first single or src,dst to the first double data storage set
in \fIb\fR to which the entry can be added. You can imagine a \fBlist:set\fR
type of set as an ordered union of the set elements.
.PP
Please note: by the \fBipset\fR commad you can add, delete and \fBtest\fR
the setnames in a \fBlist:set\fR type of set, and \fBnot\fR the presence of
a set's member (such as an IP address).
.SH "GENERAL RESTRICTIONS"
Zero valued set entries cannot be used with hash methods. Zero protocol value with ports
cannot be used.
.SH "COMMENTS"
If you want to store same size subnets from a given network
(say /24 blocks from a /8 network), use the \fBbitmap:ip\fR set type.
If you want to store random same size networks (say random /24 blocks),
use the \fBhash:ip\fR set type. If you have got random size of netblocks,
use \fBhash:net\fR.
.PP
Backward compatibility is maintained and old \fBipset\fR syntax is still supported.
.PP
The \fBiptree\fR and \fBiptreemap\fR set types are removed: if you refer to them,
they are automatically replaced by \fBhash:ip\fR type of sets.
.SH "DIAGNOSTICS"
Various error messages are printed to standard error. The exit code
is 0 for correct functioning.
.SH "BUGS"
Bugs? No, just funny features. :\-)
OK, just kidding...
.SH "SEE ALSO"
\fBiptables\fR(8),
\fBip6tables\fR(8)
.SH "AUTHORS"
Jozsef Kadlecsik wrote ipset, which is based on ippool by
Joakim Axelsson, Patrick Schaaf and Martin Josefsson.
.br
Sven Wegener wrote the iptreemap type.
.SH "LAST REMARK"
\fBI stand on the shoulders of giants.\fR

169
extensions/ipset-5/jhash.h Normal file
View File

@@ -0,0 +1,169 @@
#ifndef _LINUX_JHASH_H
#define _LINUX_JHASH_H
/* jhash.h: Jenkins hash support.
*
* Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
*
* http://burtleburtle.net/bob/hash/
*
* These are the credits from Bob's sources:
*
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
*
* These are functions for producing 32-bit hashes for hash table lookup.
* hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
* are externally useful functions. Routines to test the hash are included
* if SELF_TEST is defined. You can use this free for any purpose. It's in
* the public domain. It has no warranty.
*
* Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* I've modified Bob's hash to be useful in the Linux kernel, and
* any bugs present are my fault.
* Jozsef
*/
#include <linux/bitops.h>
#include <linux/unaligned/packed_struct.h>
/* Best hash sizes are of power of two */
#define jhash_size(n) ((u32)1<<(n))
/* Mask the hash value, i.e (value & jhash_mask(n)) instead of (value % n) */
#define jhash_mask(n) (jhash_size(n)-1)
/* __jhash_mix -- mix 3 32-bit values reversibly. */
#define __jhash_mix(a, b, c) \
{ \
a -= c; a ^= rol32(c, 4); c += b; \
b -= a; b ^= rol32(a, 6); a += c; \
c -= b; c ^= rol32(b, 8); b += a; \
a -= c; a ^= rol32(c, 16); c += b; \
b -= a; b ^= rol32(a, 19); a += c; \
c -= b; c ^= rol32(b, 4); b += a; \
}
/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
#define __jhash_final(a, b, c) \
{ \
c ^= b; c -= rol32(b, 14); \
a ^= c; a -= rol32(c, 11); \
b ^= a; b -= rol32(a, 25); \
c ^= b; c -= rol32(b, 16); \
a ^= c; a -= rol32(c, 4); \
b ^= a; b -= rol32(a, 14); \
c ^= b; c -= rol32(b, 24); \
}
/* An arbitrary initial parameter */
#define JHASH_INITVAL 0xdeadbeef
/* jhash - hash an arbitrary key
* @k: sequence of bytes as key
* @length: the length of the key
* @initval: the previous hash, or an arbitray value
*
* The generic version, hashes an arbitrary sequence of bytes.
* No alignment or length assumptions are made about the input key.
*
* Returns the hash value of the key. The result depends on endianness.
*/
static inline u32 jhash(const void *key, u32 length, u32 initval)
{
u32 a, b, c;
const u8 *k = key;
/* Set up the internal state */
a = b = c = JHASH_INITVAL + length + initval;
/* All but the last block: affect some 32 bits of (a,b,c) */
while (length > 12) {
a += __get_unaligned_cpu32(k);
b += __get_unaligned_cpu32(k + 4);
c += __get_unaligned_cpu32(k + 8);
__jhash_mix(a, b, c);
length -= 12;
k += 12;
}
/* Last block: affect all 32 bits of (c) */
/* All the case statements fall through */
switch (length) {
case 12: c += (u32)k[11]<<24;
case 11: c += (u32)k[10]<<16;
case 10: c += (u32)k[9]<<8;
case 9: c += k[8];
case 8: b += (u32)k[7]<<24;
case 7: b += (u32)k[6]<<16;
case 6: b += (u32)k[5]<<8;
case 5: b += k[4];
case 4: a += (u32)k[3]<<24;
case 3: a += (u32)k[2]<<16;
case 2: a += (u32)k[1]<<8;
case 1: a += k[0];
__jhash_final(a, b, c);
case 0: /* Nothing left to add */
break;
}
return c;
}
/* jhash2 - hash an array of u32's
* @k: the key which must be an array of u32's
* @length: the number of u32's in the key
* @initval: the previous hash, or an arbitray value
*
* Returns the hash value of the key.
*/
static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
{
u32 a, b, c;
/* Set up the internal state */
a = b = c = JHASH_INITVAL + (length<<2) + initval;
/* Handle most of the key */
while (length > 3) {
a += k[0];
b += k[1];
c += k[2];
__jhash_mix(a, b, c);
length -= 3;
k += 3;
}
/* Handle the last 3 u32's: all the case statements fall through */
switch (length) {
case 3: c += k[2];
case 2: b += k[1];
case 1: a += k[0];
__jhash_final(a, b, c);
case 0: /* Nothing left to add */
break;
}
return c;
}
/* jhash_3words - hash exactly 3, 2 or 1 word(s) */
static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
{
a += JHASH_INITVAL;
b += JHASH_INITVAL;
c += initval;
__jhash_final(a, b, c);
return c;
}
static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
{
return jhash_3words(a, b, 0, initval);
}
static inline u32 jhash_1word(u32 a, u32 initval)
{
return jhash_3words(a, 0, 0, initval);
}
#endif /* _LINUX_JHASH_H */

View File

@@ -0,0 +1,568 @@
/* 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 <assert.h> /* assert */
#include <arpa/inet.h> /* ntoh* */
#include <net/ethernet.h> /* ETH_ALEN */
#include <sys/socket.h> /* AF_ */
#include <stdlib.h> /* malloc, free */
#include <string.h> /* memset */
#include <libipset/linux_ip_set.h> /* IPSET_MAXNAMELEN */
#include <libipset/debug.h> /* D() */
#include <libipset/types.h> /* struct ipset_type */
#include <libipset/utils.h> /* inXcpy */
#include <libipset/data.h> /* prototypes */
/* Internal data structure to hold
* a) input data entered by the user or
* b) data received from kernel
*
* We always store the data in host order, *except* IP addresses.
*/
struct ipset_data {
/* Option bits: which fields are set */
uint64_t bits;
/* Option bits: which options are ignored */
uint64_t ignored;
/* Setname */
char setname[IPSET_MAXNAMELEN];
/* Set type */
const struct ipset_type *type;
/* Common CADT options */
uint8_t cidr;
uint8_t family;
uint32_t flags; /* command level flags */
uint32_t cadt_flags; /* data level flags */
uint32_t timeout;
union nf_inet_addr ip;
union nf_inet_addr ip_to;
uint16_t port;
uint16_t port_to;
union {
/* RENAME/SWAP */
char setname2[IPSET_MAXNAMELEN];
/* CREATE/LIST/SAVE */
struct {
uint8_t probes;
uint8_t resize;
uint8_t netmask;
uint32_t hashsize;
uint32_t maxelem;
uint32_t gc;
uint32_t size;
/* Filled out by kernel */
uint32_t references;
uint32_t elements;
uint32_t memsize;
char typename[IPSET_MAXNAMELEN];
uint8_t revision_min;
uint8_t revision;
} create;
/* ADT/LIST/SAVE */
struct {
union nf_inet_addr ip2;
uint8_t cidr2;
uint8_t proto;
char ether[ETH_ALEN];
char name[IPSET_MAXNAMELEN];
char nameref[IPSET_MAXNAMELEN];
} adt;
};
};
static void
copy_addr(uint8_t family, union nf_inet_addr *ip, const void *value)
{
if (family == AF_INET)
in4cpy(&ip->in, value);
else
in6cpy(&ip->in6, value);
}
/**
* ipset_strlcpy - copy the string from src to dst
* @dst: the target string buffer
* @src: the source string buffer
* @len: the length of bytes to copy, including the terminating null byte.
*
* Copy the string from src to destination, but at most len bytes are
* copied. The target is unconditionally terminated by the null byte.
*/
void
ipset_strlcpy(char *dst, const char *src, size_t len)
{
assert(dst);
assert(src);
strncpy(dst, src, len);
dst[len - 1] = '\0';
}
/**
* ipset_data_flags_test - test option bits in the data blob
* @data: data blob
* @flags: the option flags to test
*
* Returns true if the options are already set in the data blob.
*/
bool
ipset_data_flags_test(const struct ipset_data *data, uint64_t flags)
{
assert(data);
return !!(data->bits & flags);
}
/**
* ipset_data_flags_set - set option bits in the data blob
* @data: data blob
* @flags: the option flags to set
*
* The function sets the flags in the data blob so that
* the corresponding fields are regarded as if filled with proper data.
*/
void
ipset_data_flags_set(struct ipset_data *data, uint64_t flags)
{
assert(data);
data->bits |= flags;
}
/**
* ipset_data_flags_unset - unset option bits in the data blob
* @data: data blob
* @flags: the option flags to unset
*
* The function unsets the flags in the data blob.
* This is the quick way to clear specific fields.
*/
void
ipset_data_flags_unset(struct ipset_data *data, uint64_t flags)
{
assert(data);
data->bits &= ~flags;
}
#define flag_type_attr(data, opt, flag) \
do { \
data->flags |= flag; \
opt = IPSET_OPT_FLAGS; \
} while (0)
#define cadt_flag_type_attr(data, opt, flag) \
do { \
data->cadt_flags |= flag; \
opt = IPSET_OPT_CADT_FLAGS; \
} while (0)
/**
* ipset_data_ignored - test and set ignored bits in the data blob
* @data: data blob
* @flags: the option flag to be ignored
*
* Returns true if the option was not already ignored.
*/
bool
ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt)
{
bool ignored;
assert(data);
ignored = data->ignored & IPSET_FLAG(opt);
data->ignored |= IPSET_FLAG(opt);
return ignored;
}
/**
* ipset_data_set - put data into the data blob
* @data: data blob
* @opt: the option kind of the data
* @value: the value of the data
*
* Put a given kind of data into the data blob and mark the
* option kind as already set in the blob.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
{
assert(data);
assert(opt != IPSET_OPT_NONE);
assert(value);
switch (opt) {
/* Common ones */
case IPSET_SETNAME:
ipset_strlcpy(data->setname, value, IPSET_MAXNAMELEN);
break;
case IPSET_OPT_TYPE:
data->type = value;
break;
case IPSET_OPT_FAMILY:
data->family = *(const uint8_t *) value;
D("family set to %u", data->family);
break;
/* CADT options */
case IPSET_OPT_IP:
if (!(data->family == AF_INET || data->family == AF_INET6))
return -1;
copy_addr(data->family, &data->ip, value);
break;
case IPSET_OPT_IP_TO:
if (!(data->family == AF_INET || data->family == AF_INET6))
return -1;
copy_addr(data->family, &data->ip_to, value);
break;
case IPSET_OPT_CIDR:
data->cidr = *(const uint8_t *) value;
break;
case IPSET_OPT_PORT:
data->port = *(const uint16_t *) value;
break;
case IPSET_OPT_PORT_TO:
data->port_to = *(const uint16_t *) value;
break;
case IPSET_OPT_TIMEOUT:
data->timeout = *(const uint32_t *) value;
break;
/* Create-specific options */
case IPSET_OPT_GC:
data->create.gc = *(const uint32_t *) value;
break;
case IPSET_OPT_HASHSIZE:
data->create.hashsize = *(const uint32_t *) value;
break;
case IPSET_OPT_MAXELEM:
data->create.maxelem = *(const uint32_t *) value;
break;
case IPSET_OPT_NETMASK:
data->create.netmask = *(const uint8_t *) value;
break;
case IPSET_OPT_PROBES:
data->create.probes = *(const uint8_t *) value;
break;
case IPSET_OPT_RESIZE:
data->create.resize = *(const uint8_t *) value;
break;
case IPSET_OPT_SIZE:
data->create.size = *(const uint32_t *) value;
break;
/* Create-specific options, filled out by the kernel */
case IPSET_OPT_ELEMENTS:
data->create.elements = *(const uint32_t *) value;
break;
case IPSET_OPT_REFERENCES:
data->create.references = *(const uint32_t *) value;
break;
case IPSET_OPT_MEMSIZE:
data->create.memsize = *(const uint32_t *) value;
break;
/* Create-specific options, type */
case IPSET_OPT_TYPENAME:
ipset_strlcpy(data->create.typename, value,
IPSET_MAXNAMELEN);
break;
case IPSET_OPT_REVISION:
data->create.revision = *(const uint8_t *) value;
break;
case IPSET_OPT_REVISION_MIN:
data->create.revision_min = *(const uint8_t *) value;
break;
/* ADT-specific options */
case IPSET_OPT_ETHER:
memcpy(data->adt.ether, value, ETH_ALEN);
break;
case IPSET_OPT_NAME:
ipset_strlcpy(data->adt.name, value, IPSET_MAXNAMELEN);
break;
case IPSET_OPT_NAMEREF:
ipset_strlcpy(data->adt.nameref, value, IPSET_MAXNAMELEN);
break;
case IPSET_OPT_IP2:
if (!(data->family == AF_INET || data->family == AF_INET6))
return -1;
copy_addr(data->family, &data->adt.ip2, value);
break;
case IPSET_OPT_CIDR2:
data->adt.cidr2 = *(const uint8_t *) value;
break;
case IPSET_OPT_PROTO:
data->adt.proto = *(const uint8_t *) value;
break;
/* Swap/rename */
case IPSET_OPT_SETNAME2:
ipset_strlcpy(data->setname2, value, IPSET_MAXNAMELEN);
break;
/* flags */
case IPSET_OPT_EXIST:
flag_type_attr(data, opt, IPSET_FLAG_EXIST);
break;
case IPSET_OPT_BEFORE:
cadt_flag_type_attr(data, opt, IPSET_FLAG_BEFORE);
break;
case IPSET_OPT_FLAGS:
data->flags = *(const uint32_t *)value;
break;
case IPSET_OPT_CADT_FLAGS:
data->cadt_flags = *(const uint32_t *)value;
break;
default:
return -1;
};
ipset_data_flags_set(data, IPSET_FLAG(opt));
return 0;
}
/**
* ipset_data_get - get data from the data blob
* @data: data blob
* @opt: option kind of the requested data
*
* Returns the pointer to the requested kind of data from the data blob
* if it is set. If the option kind is not set or is an unkown type,
* NULL is returned.
*/
const void *
ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
{
assert(data);
assert(opt != IPSET_OPT_NONE);
if (!(opt == IPSET_OPT_TYPENAME || ipset_data_test(data, opt)))
return NULL;
switch (opt) {
/* Common ones */
case IPSET_SETNAME:
return data->setname;
case IPSET_OPT_TYPE:
return data->type;
case IPSET_OPT_TYPENAME:
if (ipset_data_test(data, IPSET_OPT_TYPE))
return data->type->name;
else if (ipset_data_test(data, IPSET_OPT_TYPENAME))
return data->create.typename;
return NULL;
case IPSET_OPT_FAMILY:
return &data->family;
/* CADT options */
case IPSET_OPT_IP:
return &data->ip;
case IPSET_OPT_IP_TO:
return &data->ip_to;
case IPSET_OPT_CIDR:
return &data->cidr;
case IPSET_OPT_PORT:
return &data->port;
case IPSET_OPT_PORT_TO:
return &data->port_to;
case IPSET_OPT_TIMEOUT:
return &data->timeout;
/* Create-specific options */
case IPSET_OPT_GC:
return &data->create.gc;
case IPSET_OPT_HASHSIZE:
return &data->create.hashsize;
case IPSET_OPT_MAXELEM:
return &data->create.maxelem;
case IPSET_OPT_NETMASK:
return &data->create.netmask;
case IPSET_OPT_PROBES:
return &data->create.probes;
case IPSET_OPT_RESIZE:
return &data->create.resize;
case IPSET_OPT_SIZE:
return &data->create.size;
/* Create-specific options, filled out by the kernel */
case IPSET_OPT_ELEMENTS:
return &data->create.elements;
case IPSET_OPT_REFERENCES:
return &data->create.references;
case IPSET_OPT_MEMSIZE:
return &data->create.memsize;
/* Create-specific options, TYPE */
case IPSET_OPT_REVISION:
return &data->create.revision;
case IPSET_OPT_REVISION_MIN:
return &data->create.revision_min;
/* ADT-specific options */
case IPSET_OPT_ETHER:
return data->adt.ether;
case IPSET_OPT_NAME:
return data->adt.name;
case IPSET_OPT_NAMEREF:
return data->adt.nameref;
case IPSET_OPT_IP2:
return &data->adt.ip2;
case IPSET_OPT_CIDR2:
return &data->adt.cidr2;
case IPSET_OPT_PROTO:
return &data->adt.proto;
/* Swap/rename */
case IPSET_OPT_SETNAME2:
return data->setname2;
/* flags */
case IPSET_OPT_FLAGS:
case IPSET_OPT_EXIST:
return &data->flags;
case IPSET_OPT_CADT_FLAGS:
case IPSET_OPT_BEFORE:
return &data->cadt_flags;
default:
return NULL;
}
}
/**
* ipset_data_sizeof - calculates the size of the data type
* @opt: option kind of the data
* @family: INET family
*
* Returns the size required to store the given data type.
*/
size_t
ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
{
assert(opt != IPSET_OPT_NONE);
switch (opt) {
case IPSET_OPT_IP:
case IPSET_OPT_IP_TO:
case IPSET_OPT_IP2:
return family == AF_INET ? sizeof(uint32_t)
: sizeof(struct in6_addr);
case IPSET_OPT_PORT:
case IPSET_OPT_PORT_TO:
return sizeof(uint16_t);
case IPSET_SETNAME:
case IPSET_OPT_NAME:
case IPSET_OPT_NAMEREF:
return IPSET_MAXNAMELEN;
case IPSET_OPT_TIMEOUT:
case IPSET_OPT_GC:
case IPSET_OPT_HASHSIZE:
case IPSET_OPT_MAXELEM:
case IPSET_OPT_SIZE:
case IPSET_OPT_ELEMENTS:
case IPSET_OPT_REFERENCES:
case IPSET_OPT_MEMSIZE:
return sizeof(uint32_t);
case IPSET_OPT_CIDR:
case IPSET_OPT_CIDR2:
case IPSET_OPT_NETMASK:
case IPSET_OPT_PROBES:
case IPSET_OPT_RESIZE:
case IPSET_OPT_PROTO:
return sizeof(uint8_t);
case IPSET_OPT_ETHER:
return ETH_ALEN;
/* Flags counted once */
case IPSET_OPT_BEFORE:
return sizeof(uint32_t);
default:
return 0;
};
}
/**
* ipset_setname - return the name of the set from the data blob
* @data: data blob
*
* Return the name of the set from the data blob or NULL if the
* name not set yet.
*/
const char *
ipset_data_setname(const struct ipset_data *data)
{
assert(data);
return ipset_data_test(data, IPSET_SETNAME) ? data->setname : NULL;
}
/**
* ipset_family - return the INET family of the set from the data blob
* @data: data blob
*
* Return the INET family supported by the set from the data blob.
* If the family is not set yet, AF_UNSPEC is returned.
*/
uint8_t
ipset_data_family(const struct ipset_data *data)
{
assert(data);
return ipset_data_test(data, IPSET_OPT_FAMILY)
? data->family : AF_UNSPEC;
}
/**
* ipset_data_cidr - return the value of IPSET_OPT_CIDR
* @data: data blob
*
* Return the value of IPSET_OPT_CIDR stored in the data blob.
* If it is not set, then the returned value corresponds to
* the default one according to the family type or zero.
*/
uint8_t
ipset_data_cidr(const struct ipset_data *data)
{
assert(data);
return ipset_data_test(data, IPSET_OPT_CIDR) ? data->cidr :
data->family == AF_INET ? 32 :
data->family == AF_INET6 ? 128 : 0;
}
/**
* ipset_flags - return which fields are set in the data blob
* @data: data blob
*
* Returns the value of the bit field which elements are set.
*/
uint64_t
ipset_data_flags(const struct ipset_data *data)
{
assert(data);
return data->bits;
}
/**
* ipset_data_reset - reset the data blob to unset
* @data: data blob
*
* Resets the data blob to the unset state for every field.
*/
void
ipset_data_reset(struct ipset_data *data)
{
assert(data);
memset(data, 0, sizeof(*data));
}
/**
* ipset_data_init - create a new data blob
*
* Return the new data blob initialized to empty. In case of
* an error, NULL is retured.
*/
struct ipset_data *
ipset_data_init(void)
{
return calloc(1, sizeof(struct ipset_data));
}
/**
* ipset_data_fini - release a data blob created by ipset_data_init
*
* Release the data blob created by ipset_data_init previously.
*/
void
ipset_data_fini(struct ipset_data *data)
{
assert(data);
free(data);
}

View File

@@ -0,0 +1,79 @@
/* 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/utils.h> /* STRNEQ */
#include <libipset/icmp.h> /* prototypes */
struct icmp_names {
const char *name;
uint8_t type, code;
};
static const struct icmp_names icmp_typecodes[] = {
{ "echo-reply", 0, 0 },
{ "pong", 0, 0 },
{ "network-unreachable", 3, 0 },
{ "host-unreachable", 3, 1 },
{ "protocol-unreachable", 3, 2 },
{ "port-unreachable", 3, 3 },
{ "fragmentation-needed", 3, 4 },
{ "source-route-failed", 3, 5 },
{ "network-unknown", 3, 6 },
{ "host-unknown", 3, 7 },
{ "network-prohibited", 3, 9 },
{ "host-prohibited", 3, 10 },
{ "TOS-network-unreachable", 3, 11 },
{ "TOS-host-unreachable", 3, 12 },
{ "communication-prohibited", 3, 13 },
{ "host-precedence-violation", 3, 14 },
{ "precedence-cutoff", 3, 15 },
{ "source-quench", 4, 0 },
{ "network-redirect", 5, 0 },
{ "host-redirect", 5, 1 },
{ "TOS-network-redirect", 5, 2 },
{ "TOS-host-redirect", 5, 3 },
{ "echo-request", 8, 0 },
{ "ping", 8, 0 },
{ "router-advertisement", 9, 0 },
{ "router-solicitation", 10, 0 },
{ "ttl-zero-during-transit", 11, 0 },
{ "ttl-zero-during-reassembly", 11, 1 },
{ "ip-header-bad", 12, 0 },
{ "required-option-missing", 12, 1 },
{ "timestamp-request", 13, 0 },
{ "timestamp-reply", 14, 0 },
{ "address-mask-request", 17, 0 },
{ "address-mask-reply", 18, 0 },
};
const char * id_to_icmp(uint8_t id)
{
return id < ARRAY_SIZE(icmp_typecodes) ? icmp_typecodes[id].name : NULL;
}
const char * icmp_to_name(uint8_t type, uint8_t code)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
if (icmp_typecodes[i].type == type && icmp_typecodes[i].code == code)
return icmp_typecodes[i].name;
return NULL;
}
int name_to_icmp(const char *str, uint16_t *typecode)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
if (STRNCASEQ(icmp_typecodes[i].name, str, strlen(str))) {
*typecode = (icmp_typecodes[i].type << 8) | icmp_typecodes[i].code;
return 0;
}
return -1;
}

View File

@@ -0,0 +1,66 @@
/* 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/utils.h> /* STRNEQ */
#include <libipset/icmpv6.h> /* prototypes */
struct icmpv6_names {
const char *name;
uint8_t type, code;
};
static const struct icmpv6_names icmpv6_typecodes[] = {
{ "no-route", 1, 0 },
{ "communication-prohibited", 1, 1 },
{ "address-unreachable", 1, 3 },
{ "port-unreachable", 1, 4 },
{ "packet-too-big", 2, 0 },
{ "ttl-zero-during-transit", 3, 0 },
{ "ttl-zero-during-reassembly", 3, 1 },
{ "bad-header", 4, 0 },
{ "unknown-header-type", 4, 1 },
{ "unknown-option", 4, 2 },
{ "echo-request", 128, 0 },
{ "ping", 128, 0 },
{ "echo-reply", 129, 0 },
{ "pong", 129, 0 },
{ "router-solicitation", 133, 0 },
{ "router-advertisement", 134, 0 },
{ "neighbour-solicitation", 135, 0 },
{ "neigbour-solicitation", 135, 0 },
{ "neighbour-advertisement", 136, 0 },
{ "neigbour-advertisement", 136, 0 },
{ "redirect", 137, 0 },
};
const char * id_to_icmpv6(uint8_t id)
{
return id < ARRAY_SIZE(icmpv6_typecodes) ? icmpv6_typecodes[id].name : NULL;
}
const char * icmpv6_to_name(uint8_t type, uint8_t code)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
if (icmpv6_typecodes[i].type == type && icmpv6_typecodes[i].code == code)
return icmpv6_typecodes[i].name;
return NULL;
}
int name_to_icmpv6(const char *str, uint16_t *typecode)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
if (STRNCASEQ(icmpv6_typecodes[i].name, str, strlen(str))) {
*typecode = (icmpv6_typecodes[i].type << 8) | icmpv6_typecodes[i].code;
return 0;
}
return -1;
}

View File

@@ -0,0 +1,258 @@
/* 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 <assert.h> /* assert */
#include <errno.h> /* errno */
#include <stdint.h>
#include <stdlib.h> /* calloc, free */
#include <time.h> /* time */
#include <arpa/inet.h> /* hto* */
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
#include <libipset/debug.h> /* D() */
#include <libipset/session.h> /* ipset_session_handle */
#include <libipset/ui.h> /* IPSET_ENV_EXIST */
#include <libipset/utils.h> /* UNUSED */
#include <libipset/mnl.h> /* prototypes */
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
#include <linux/netlink.h>
#ifndef NFNL_SUBSYS_IPSET
#define NFNL_SUBSYS_IPSET 6
#endif
/* Internal data structure for the kernel-userspace communication parameters */
struct ipset_handle {
struct mnl_socket *h; /* the mnl socket */
unsigned int seq; /* netlink message sequence number */
unsigned int portid; /* the socket port identifier */
mnl_cb_t *cb_ctl; /* control block callbacks */
void *data; /* data pointer */
unsigned int genl_id; /* genetlink ID of ip_set */
};
/* Netlink flags of the commands */
static const uint16_t cmdflags[] = {
[IPSET_CMD_CREATE-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE|NLM_F_EXCL,
[IPSET_CMD_DESTROY-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_FLUSH-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_RENAME-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_SWAP-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_LIST-1] = NLM_F_REQUEST,
[IPSET_CMD_SAVE-1] = NLM_F_REQUEST,
[IPSET_CMD_ADD-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
[IPSET_CMD_DEL-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
[IPSET_CMD_TEST-1] = NLM_F_REQUEST|NLM_F_ACK,
[IPSET_CMD_HEADER-1] = NLM_F_REQUEST,
[IPSET_CMD_TYPE-1] = NLM_F_REQUEST,
[IPSET_CMD_PROTOCOL-1] = NLM_F_REQUEST,
};
/**
* ipset_get_nlmsg_type - get ipset netlink message type
* @nlh: pointer to the netlink message header
*
* Returns the ipset netlink message type, i.e. the ipset command.
*/
int
ipset_get_nlmsg_type(const struct nlmsghdr *nlh)
{
const struct genlmsghdr *ghdr = mnl_nlmsg_get_payload(nlh);
return ghdr->cmd;
}
static void
ipset_mnl_fill_hdr(struct ipset_handle *handle, enum ipset_cmd cmd,
void *buffer, size_t len UNUSED, uint8_t envflags)
{
struct nlmsghdr *nlh;
struct genlmsghdr *ghdr;
assert(handle);
assert(buffer);
assert(cmd > IPSET_CMD_NONE && cmd < IPSET_MSG_MAX);
nlh = mnl_nlmsg_put_header(buffer);
nlh->nlmsg_type = handle->genl_id;
nlh->nlmsg_flags = NLM_F_REQUEST;
if (cmdflags[cmd-1] & NLM_F_ACK)
nlh->nlmsg_flags |= NLM_F_ACK;
nlh->nlmsg_seq = handle->seq = time(NULL);
ghdr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
ghdr->cmd = cmd;
/* (ge)netlink is dumb, NLM_F_CREATE and NLM_F_DUMP overlap, get misinterpreted. */
ghdr->reserved = cmdflags[cmd-1];
if (envflags & IPSET_ENV_EXIST)
ghdr->reserved &= ~NLM_F_EXCL;
}
static int
ipset_mnl_query(struct ipset_handle *handle, void *buffer, size_t len)
{
struct nlmsghdr *nlh = buffer;
int ret;
assert(handle);
assert(buffer);
if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0)
return -ECOMM;
D("message sent");
ret = mnl_socket_recvfrom(handle->h, buffer, len);
D("message received, ret: %d", ret);
while (ret > 0) {
ret = mnl_cb_run2(buffer, ret,
handle->seq, handle->portid,
handle->cb_ctl[NLMSG_MIN_TYPE],
handle->data,
handle->cb_ctl, NLMSG_MIN_TYPE);
D("nfln_cb_run2, ret: %d", ret);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(handle->h, buffer, len);
D("message received, ret: %d", ret);
}
return ret > 0 ? 0 : ret;
}
static int ipset_mnl_getid_acb(const struct nlattr *attr, void *datap)
{
const struct nlattr **tb = datap;
uint16_t type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
return MNL_CB_OK;
tb[type] = attr;
return MNL_CB_OK;
}
static int ipset_mnl_getid_cb(const struct nlmsghdr *nlhdr, void *datap)
{
struct ipset_handle *h = datap;
const struct nlattr *tb[CTRL_ATTR_MAX+1] = {0};
const struct genlmsghdr *ghdr = mnl_nlmsg_get_payload(nlhdr);
int ret;
ret = mnl_attr_parse(nlhdr, sizeof(*ghdr), ipset_mnl_getid_acb, tb);
if (ret != MNL_CB_OK)
return ret;
if (tb[CTRL_ATTR_FAMILY_ID] != NULL)
h->genl_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
return MNL_CB_OK;
}
/**
* Look up the GENL identifier for the ip_set subsystem, and store it in
* @h->genl_id. On success, 0 is returned, otherwise error encoded as
* negative number.
*/
static int ipset_mnl_getid(struct ipset_handle *h, bool modprobe)
{
size_t buf_size = 8192; //MNL_SOCKET_BUFFER_SIZE;
struct nlmsghdr *nlhdr;
struct genlmsghdr *ghdr;
char *buf;
int ret = -ENOENT;
h->genl_id = 0;
if (modprobe) {
/* genetlink has no autoloading like nfnetlink... */
system("/sbin/modprobe -q ip_set");
}
buf = malloc(buf_size);
if (buf == NULL)
return -errno;
nlhdr = mnl_nlmsg_put_header(buf);
nlhdr->nlmsg_type = GENL_ID_CTRL;
nlhdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
ghdr = mnl_nlmsg_put_extra_header(nlhdr, sizeof(struct genlmsghdr));
ghdr->cmd = CTRL_CMD_GETFAMILY;
ghdr->version = 2;
if (!mnl_attr_put_strz_check(nlhdr, buf_size,
CTRL_ATTR_FAMILY_NAME, "ip_set"))
goto out;
ret = mnl_socket_sendto(h->h, buf, nlhdr->nlmsg_len);
if (ret < 0)
goto out;
ret = mnl_socket_recvfrom(h->h, buf, buf_size);
while (ret > 0) {
ret = mnl_cb_run(buf, ret, 0, 0, ipset_mnl_getid_cb, h);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(h->h, buf, buf_size);
}
if (h->genl_id == 0 && !modprobe)
/* Redo, with modprobe this time. */
ret = ipset_mnl_getid(h, true);
if (h->genl_id > 0)
ret = 0;
out:
free(buf);
return ret;
}
static struct ipset_handle *
ipset_mnl_init(mnl_cb_t *cb_ctl, void *data)
{
struct ipset_handle *handle;
assert(cb_ctl);
assert(data);
handle = calloc(1, sizeof(*handle));
if (!handle)
return NULL;
handle->h = mnl_socket_open(NETLINK_GENERIC);
if (!handle->h)
goto free_handle;
if (mnl_socket_bind(handle->h, 0, MNL_SOCKET_AUTOPID) < 0)
goto close_nl;
handle->portid = mnl_socket_get_portid(handle->h);
handle->cb_ctl = cb_ctl;
handle->data = data;
if (ipset_mnl_getid(handle, false) < 0)
goto close_nl;
return handle;
close_nl:
mnl_socket_close(handle->h);
free_handle:
free(handle);
return NULL;
}
static int
ipset_mnl_fini(struct ipset_handle *handle)
{
assert(handle);
if (handle->h)
mnl_socket_close(handle->h);
free(handle);
return 0;
}
const struct ipset_transport ipset_mnl_transport = {
.init = ipset_mnl_init,
.fini = ipset_mnl_fini,
.fill_hdr = ipset_mnl_fill_hdr,
.query = ipset_mnl_query,
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,743 @@
/* 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 <assert.h> /* assert */
#include <errno.h> /* errno */
#include <stdio.h> /* snprintf */
#include <netdb.h> /* getservbyport */
#include <sys/types.h> /* inet_ntop */
#include <sys/socket.h> /* inet_ntop */
#include <arpa/inet.h> /* inet_ntop */
#include <net/ethernet.h> /* ETH_ALEN */
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_* */
#include <libipset/icmp.h> /* icmp_to_name */
#include <libipset/icmpv6.h> /* icmpv6_to_name */
#include <libipset/parse.h> /* IPSET_*_SEPARATOR */
#include <libipset/types.h> /* ipset set types */
#include <libipset/session.h> /* IPSET_FLAG_ */
#include <libipset/utils.h> /* UNUSED */
#include <libipset/ui.h> /* IPSET_ENV_* */
#include <libipset/print.h> /* prototypes */
/* Print data (to output buffer). All function must follow snprintf. */
#define SNPRINTF_FAILURE(size, len, offset) \
do { \
if (size < 0 || (unsigned int) size >= len) \
return size; \
offset += size; \
len -= size; \
} while (0)
/**
* ipset_print_ether - print ethernet address to string
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print Ethernet address to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_ether(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
const unsigned char *ether;
int i, size, offset = 0;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_ETHER);
if (len < ETH_ALEN*3)
return -1;
ether = ipset_data_get(data, opt);
assert(ether);
size = snprintf(buf, len, "%02X", ether[0]);
SNPRINTF_FAILURE(size, len, offset);
for (i = 1; i < ETH_ALEN; i++) {
size = snprintf(buf + offset, len, ":%02X", ether[i]);
SNPRINTF_FAILURE(size, len, offset);
}
return offset;
}
/**
* ipset_print_family - print INET family
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print INET family string to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_family(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
uint8_t family;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_FAMILY);
if (len < strlen("inet6") + 1)
return -1;
family = ipset_data_family(data);
return snprintf(buf, len, "%s",
family == AF_INET ? "inet" :
family == AF_INET6 ? "inet6" : "any");
}
/**
* ipset_print_type - print ipset type string
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print ipset module string identifier to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_type(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
const struct ipset_type *type;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_TYPE);
type = ipset_data_get(data, opt);
assert(type);
if (len < strlen(type->name) + 1)
return -1;
return snprintf(buf, len, "%s", type->name);
}
#define GETNAMEINFO(family, f, n) \
static inline int \
__getnameinfo##f(char *buf, unsigned int len, \
int flags, const union nf_inet_addr *addr) \
{ \
struct sockaddr_in##n saddr; \
int err; \
\
memset(&saddr, 0, sizeof(saddr)); \
in##f##cpy(&saddr.sin##n##_addr, &addr->in##n); \
saddr.sin##n##_family = family; \
\
err = getnameinfo((const struct sockaddr *)&saddr, \
sizeof(saddr), \
buf, len, NULL, 0, flags); \
\
if (err == EAI_AGAIN && !(flags & NI_NUMERICHOST)) \
err = getnameinfo((const struct sockaddr *)&saddr, \
sizeof(saddr), \
buf, len, NULL, 0, \
flags | NI_NUMERICHOST); \
D("getnameinfo err: %i, errno %i", err, errno); \
return (err == 0 ? (int)strlen(buf) : \
(err == EAI_OVERFLOW || err == EAI_SYSTEM) ? (int)len : -1);\
}
#define SNPRINTF_IP(mask, f) \
static int \
snprintf_ipv##f(char *buf, unsigned int len, int flags, \
const union nf_inet_addr *ip, uint8_t cidr) \
{ \
int size, offset = 0; \
\
size = __getnameinfo##f(buf, len, flags, ip); \
SNPRINTF_FAILURE(size, len, offset); \
\
D("cidr %u mask %u", cidr, mask); \
if (cidr == mask) \
return offset; \
D("print cidr"); \
size = snprintf(buf + offset, len, \
"%s%u", IPSET_CIDR_SEPARATOR, cidr); \
SNPRINTF_FAILURE(size, len, offset); \
return offset; \
}
GETNAMEINFO(AF_INET, 4, )
SNPRINTF_IP(32, 4)
GETNAMEINFO(AF_INET6, 6, 6)
SNPRINTF_IP(128, 6)
/**
* ipset_print_ip - print IPv4|IPv6 address to string
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print IPv4|IPv6 address, address/cidr or address range to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_ip(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env)
{
const union nf_inet_addr *ip;
uint8_t family, cidr;
int flags, size, offset = 0;
enum ipset_opt cidropt;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
D("len: %u", len);
family = ipset_data_family(data);
cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
if (ipset_data_test(data, cidropt)) {
cidr = *(const uint8_t *) ipset_data_get(data, cidropt);
D("CIDR: %u", cidr);
} else
cidr = family == AF_INET6 ? 128 : 32;
flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
ip = ipset_data_get(data, opt);
assert(ip);
if (family == AF_INET)
size = snprintf_ipv4(buf, len, flags, ip, cidr);
else if (family == AF_INET6)
size = snprintf_ipv6(buf, len, flags, ip, cidr);
else
return -1;
D("size %i, len %u", size, len);
SNPRINTF_FAILURE(size, len, offset);
D("len: %u, offset %u", len, offset);
if (!ipset_data_test(data, IPSET_OPT_IP_TO))
return offset;
size = snprintf(buf + offset, len, "%s", IPSET_RANGE_SEPARATOR);
SNPRINTF_FAILURE(size, len, offset);
ip = ipset_data_get(data, IPSET_OPT_IP_TO);
if (family == AF_INET)
size = snprintf_ipv4(buf + offset, len, flags, ip, cidr);
else if (family == AF_INET6)
size = snprintf_ipv6(buf + offset, len, flags, ip, cidr);
else
return -1;
SNPRINTF_FAILURE(size, len, offset);
return offset;
}
/**
* ipset_print_ipaddr - print IPv4|IPv6 address to string
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print IPv4|IPv6 address or address/cidr to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_ipaddr(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env)
{
const union nf_inet_addr *ip;
uint8_t family, cidr;
enum ipset_opt cidropt;
int flags;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_IP
|| opt == IPSET_OPT_IP_TO
|| opt == IPSET_OPT_IP2);
family = ipset_data_family(data);
cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
if (ipset_data_test(data, cidropt))
cidr = *(const uint8_t *) ipset_data_get(data, cidropt);
else
cidr = family == AF_INET6 ? 128 : 32;
flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
ip = ipset_data_get(data, opt);
assert(ip);
if (family == AF_INET)
return snprintf_ipv4(buf, len, flags, ip, cidr);
else if (family == AF_INET6)
return snprintf_ipv6(buf, len, flags, ip, cidr);
return -1;
}
/**
* ipset_print_number - print number to string
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print number to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_number(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
size_t maxsize;
const void *number;
assert(buf);
assert(len > 0);
assert(data);
number = ipset_data_get(data, opt);
maxsize = ipset_data_sizeof(opt, AF_INET);
D("opt: %u, maxsize %zu", opt, maxsize);
if (maxsize == sizeof(uint8_t))
return snprintf(buf, len, "%u", *(const uint8_t *) number);
else if (maxsize == sizeof(uint16_t))
return snprintf(buf, len, "%u", *(const uint16_t *) number);
else if (maxsize == sizeof(uint32_t))
return snprintf(buf, len, "%lu",
(long unsigned) *(const uint32_t *) number);
else
assert(0);
return 0;
}
/**
* ipset_print_name - print setname element string
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print setname element string to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_name(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_NAME);
if (len < 2*IPSET_MAXNAMELEN + 2 + strlen("before"))
return -1;
name = ipset_data_get(data, opt);
assert(name);
size = snprintf(buf, len, "%s", name);
SNPRINTF_FAILURE(size, len, offset);
if (ipset_data_test(data, IPSET_OPT_NAMEREF)) {
bool before = false;
if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FLAGS))) {
const uint32_t *flags =
ipset_data_get(data, IPSET_OPT_FLAGS);
before = (*flags) & IPSET_FLAG_BEFORE;
}
size = snprintf(buf + offset, len,
" %s %s", before ? "before" : "after",
(const char *) ipset_data_get(data,
IPSET_OPT_NAMEREF));
SNPRINTF_FAILURE(size, len, offset);
}
return offset;
}
/**
* ipset_print_port - print port or port range
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print port or port range to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_port(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
const uint16_t *port;
int size, offset = 0;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_PORT);
if (len < 2*strlen("65535") + 2)
return -1;
port = ipset_data_get(data, IPSET_OPT_PORT);
assert(port);
size = snprintf(buf, len, "%u", *port);
SNPRINTF_FAILURE(size, len, offset);
if (ipset_data_test(data, IPSET_OPT_PORT_TO)) {
port = ipset_data_get(data, IPSET_OPT_PORT_TO);
size = snprintf(buf + offset, len,
"%s%u",
IPSET_RANGE_SEPARATOR, *port);
SNPRINTF_FAILURE(size, len, offset);
}
return offset;
}
/**
* ipset_print_proto - print protocol name
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print protocol name to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_proto(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
const struct protoent *protoent;
uint8_t proto;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_PROTO);
proto = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
assert(proto);
protoent = getprotobynumber(proto);
if (protoent)
return snprintf(buf, len, "%s", protoent->p_name);
/* Should not happen */
return snprintf(buf, len, "%u", proto);
}
/**
* ipset_print_icmp - print ICMP code name or type/code
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print ICMP code name or type/code name to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_icmp(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
const char *name;
uint16_t typecode;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_PORT);
typecode = *(const uint16_t *) ipset_data_get(data, IPSET_OPT_PORT);
name = icmp_to_name(typecode >> 8, typecode & 0xFF);
if (name != NULL)
return snprintf(buf, len, "%s", name);
else
return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF);
}
/**
* ipset_print_icmpv6 - print ICMPv6 code name or type/code
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print ICMPv6 code name or type/code name to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_icmpv6(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
const char *name;
uint16_t typecode;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_PORT);
typecode = *(const uint16_t *) ipset_data_get(data, IPSET_OPT_PORT);
name = icmpv6_to_name(typecode >> 8, typecode & 0xFF);
if (name != NULL)
return snprintf(buf, len, "%s", name);
else
return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF);
}
/**
* ipset_print_proto_port - print proto:port
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print protocol and port to output buffer.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_proto_port(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
int size, offset = 0;
assert(buf);
assert(len > 0);
assert(data);
assert(opt == IPSET_OPT_PORT);
if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) {
uint8_t proto = *(const uint8_t *) ipset_data_get(data,
IPSET_OPT_PROTO);
size = ipset_print_proto(buf, len, data, IPSET_OPT_PROTO, env);
SNPRINTF_FAILURE(size, len, offset);
if (len < 2)
return -ENOSPC;
size = snprintf(buf + offset, len, IPSET_PROTO_SEPARATOR);
SNPRINTF_FAILURE(size, len, offset);
switch (proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
break;
case IPPROTO_ICMP:
return ipset_print_icmp(buf + offset, len, data,
IPSET_OPT_PORT, env);
case IPPROTO_ICMPV6:
return ipset_print_icmpv6(buf + offset, len, data,
IPSET_OPT_PORT, env);
default:
break;
}
}
size = ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
SNPRINTF_FAILURE(size, len, offset);
return offset;
}
#define print_second(data) \
ipset_data_flags_test(data, \
IPSET_FLAG(IPSET_OPT_PORT)|IPSET_FLAG(IPSET_OPT_ETHER))
#define print_third(data) \
ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_IP2))
/**
* ipset_print_elem - print ADT elem according to settype
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print (multipart) element according to settype
*
* Return lenght of printed string or error size.
*/
int
ipset_print_elem(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt UNUSED,
uint8_t env)
{
const struct ipset_type *type;
int size, offset = 0;
assert(buf);
assert(len > 0);
assert(data);
type = ipset_data_get(data, IPSET_OPT_TYPE);
if (!type)
return -1;
size = type->elem[IPSET_DIM_ONE].print(buf, len, data,
type->elem[IPSET_DIM_ONE].opt, env);
SNPRINTF_FAILURE(size, len, offset);
IF_D(ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt),
"print second elem");
if (type->dimension == IPSET_DIM_ONE
|| (type->last_elem_optional
&& !ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt)))
return offset;
size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
SNPRINTF_FAILURE(size, len, offset);
size = type->elem[IPSET_DIM_TWO].print(buf + offset, len, data,
type->elem[IPSET_DIM_TWO].opt, env);
SNPRINTF_FAILURE(size, len, offset);
if (type->dimension == IPSET_DIM_TWO
|| (type->last_elem_optional
&& !ipset_data_test(data, type->elem[IPSET_DIM_THREE].opt)))
return offset;
size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
SNPRINTF_FAILURE(size, len, offset);
size = type->elem[IPSET_DIM_THREE].print(buf + offset, len, data,
type->elem[IPSET_DIM_THREE].opt, env);
SNPRINTF_FAILURE(size, len, offset);
return offset;
}
/**
* ipset_print_flag - print a flag
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Print a flag, i.e. option without value
*
* Return lenght of printed string or error size.
*/
int
ipset_print_flag(char *buf UNUSED, unsigned int len UNUSED,
const struct ipset_data *data UNUSED,
enum ipset_opt opt UNUSED, uint8_t env UNUSED)
{
return 0;
}
/**
* ipset_print_data - print data, generic fuction
* @buf: printing buffer
* @len: length of available buffer space
* @data: data blob
* @opt: the option kind
* @env: environment flags
*
* Generic wrapper of the printing functions.
*
* Return lenght of printed string or error size.
*/
int
ipset_print_data(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env)
{
int size = 0, offset = 0;
assert(buf);
assert(len > 0);
assert(data);
switch (opt) {
case IPSET_OPT_FAMILY:
size = ipset_print_family(buf, len, data, opt, env);
break;
case IPSET_OPT_TYPE:
size = ipset_print_type(buf, len, data, opt, env);
break;
case IPSET_SETNAME:
size = snprintf(buf, len, "%s", ipset_data_setname(data));
break;
case IPSET_OPT_ELEM:
size = ipset_print_elem(buf, len, data, opt, env);
break;
case IPSET_OPT_IP:
size = ipset_print_ip(buf, len, data, opt, env);
break;
case IPSET_OPT_PORT:
size = ipset_print_port(buf, len, data, opt, env);
break;
case IPSET_OPT_GC:
case IPSET_OPT_HASHSIZE:
case IPSET_OPT_MAXELEM:
case IPSET_OPT_NETMASK:
case IPSET_OPT_PROBES:
case IPSET_OPT_RESIZE:
case IPSET_OPT_TIMEOUT:
case IPSET_OPT_REFERENCES:
case IPSET_OPT_ELEMENTS:
case IPSET_OPT_SIZE:
size = ipset_print_number(buf, len, data, opt, env);
break;
default:
return -1;
}
SNPRINTF_FAILURE(size, len, offset);
return offset;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,538 @@
/* 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 <assert.h> /* assert */
#include <errno.h> /* errno */
#include <net/ethernet.h> /* ETH_ALEN */
#include <netinet/in.h> /* struct in6_addr */
#include <sys/socket.h> /* AF_ */
#include <stdlib.h> /* malloc, free */
#include <stdio.h> /* FIXME: debug */
#include <libmnl/libmnl.h> /* MNL_ALIGN */
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_* */
#include <libipset/session.h> /* ipset_cmd */
#include <libipset/utils.h> /* STREQ */
#include <libipset/types.h> /* prototypes */
/* Userspace cache of sets which exists in the kernel */
struct ipset {
char name[IPSET_MAXNAMELEN]; /* set name */
const struct ipset_type *type; /* set type */
uint8_t family; /* family */
struct ipset *next;
};
static struct ipset_type *typelist = NULL; /* registered set types */
static struct ipset *setlist = NULL; /* cached sets */
/**
* ipset_cache_add - add a set to the cache
* @name: set name
* @type: set type structure
*
* Add the named set to the internal cache with the specified
* set type. The set name must be unique.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_cache_add(const char *name, const struct ipset_type *type,
uint8_t family)
{
struct ipset *s, *n;
assert(name);
assert(type);
n = malloc(sizeof(*n));
if (n == NULL)
return -ENOMEM;
ipset_strlcpy(n->name, name, IPSET_MAXNAMELEN);
n->type = type;
n->family = family;
n->next = NULL;
if (setlist == NULL) {
setlist = n;
return 0;
}
for (s = setlist; s->next != NULL; s = s->next) {
if (STREQ(name, s->name)) {
free(n);
return -EEXIST;
}
}
s->next = n;
return 0;
}
/**
* ipset_cache_del - delete set from the cache
* @name: set name
*
* Delete the named set from the internal cache. If NULL is
* specified as setname, the whole cache is emptied.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_cache_del(const char *name)
{
struct ipset *s, *match = NULL, *prev = NULL;
if (!name) {
for (s = setlist; s != NULL; ) {
prev = s;
s = s->next;
free(prev);
}
setlist = NULL;
return 0;
}
for (s = setlist; s != NULL && match == NULL; s = s->next) {
if (STREQ(s->name, name)) {
match = s;
if (prev == NULL)
setlist = match->next;
else
prev->next = match->next;
}
prev = s;
}
if (match == NULL)
return -EEXIST;
free(match);
return 0;
}
/**
* ipset_cache_rename - rename a set in the cache
* @from: the set to rename
* @to: the new name of the set
*
* Rename the given set in the cache.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_cache_rename(const char *from, const char *to)
{
struct ipset *s;
assert(from);
assert(to);
for (s = setlist; s != NULL; s = s->next) {
if (STREQ(s->name, from)) {
ipset_strlcpy(s->name, to, IPSET_MAXNAMELEN);
return 0;
}
}
return -EEXIST;
}
/**
* ipset_cache_swap - swap two sets in the cache
* @from: the first set
* @to: the second set
*
* Swap two existing sets in the cache.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_cache_swap(const char *from, const char *to)
{
struct ipset *s, *a = NULL, *b = NULL;
assert(from);
assert(to);
for (s = setlist; s != NULL && (a == NULL || b == NULL); s = s->next) {
if (a == NULL && STREQ(s->name, from))
a = s;
if (b == NULL && STREQ(s->name, to))
b = s;
}
if (a != NULL && b != NULL) {
ipset_strlcpy(a->name, to, IPSET_MAXNAMELEN);
ipset_strlcpy(b->name, from, IPSET_MAXNAMELEN);
return 0;
}
return -EEXIST;
}
#define MATCH_FAMILY(type, f) \
(f == AF_UNSPEC || type->family == f || type->family == AF_INET46)
bool
ipset_match_typename(const char *name, const struct ipset_type *type)
{
const char * const * alias = type->alias;
if (STREQ(name, type->name))
return true;
while (alias[0]) {
if (STREQ(name, alias[0]))
return true;
alias++;
}
return false;
}
static inline const struct ipset_type *
create_type_get(struct ipset_session *session)
{
struct ipset_type *t, *match = NULL;
struct ipset_data *data;
const char *typename;
uint8_t family, tmin = 0, tmax = 0;
const uint8_t *kmin, *kmax;
int ret;
data = ipset_session_data(session);
assert(data);
typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
assert(typename);
family = ipset_data_family(data);
/* Check registered types in userspace */
for (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 (match == NULL) {
match = t;
tmax = t->revision;
} else if (t->family == match->family)
tmin = t->revision;
}
}
if (!match)
return ipset_errptr(session,
"Syntax error: unknown settype %s",
typename);
/* Family is unspecified yet: set from matching set type */
if (family == AF_UNSPEC && match->family != AF_UNSPEC) {
family = match->family == AF_INET46 ? AF_INET : match->family;
ipset_data_set(data, IPSET_OPT_FAMILY, &family);
}
if (match->kernel_check == IPSET_KERNEL_OK)
goto found;
/* Check kernel */
ret = ipset_cmd(session, IPSET_CMD_TYPE, 0);
if (ret != 0)
return NULL;
kmax = ipset_data_get(data, IPSET_OPT_REVISION);
if (ipset_data_test(data, IPSET_OPT_REVISION_MIN))
kmin = ipset_data_get(data, IPSET_OPT_REVISION_MIN);
else
kmin = kmax;
if (MAX(tmin, *kmin) > MIN(tmax, *kmax)) {
if (*kmin > tmax)
return ipset_errptr(session,
"Kernel supports %s type with family %s "
"in minimal revision %u while ipset library "
"in maximal revision %u. "
"You need to upgrade your ipset library.",
typename,
family == AF_INET ? "INET" :
family == AF_INET6 ? "INET6" : "UNSPEC",
*kmin, tmax);
else
return ipset_errptr(session,
"Kernel supports %s type with family %s "
"in maximal revision %u while ipset library "
"in minimal revision %u. "
"You need to upgrade your kernel.",
typename,
family == AF_INET ? "INET" :
family == AF_INET6 ? "INET6" : "UNSPEC",
*kmax, tmin);
}
match->kernel_check = IPSET_KERNEL_OK;
found:
ipset_data_set(data, IPSET_OPT_TYPE, match);
return match;
}
#define set_family_and_type(data, match, family) do { \
if (family == AF_UNSPEC && match->family != AF_UNSPEC) \
family = match->family == AF_INET46 ? AF_INET : match->family;\
ipset_data_set(data, IPSET_OPT_FAMILY, &family); \
ipset_data_set(data, IPSET_OPT_TYPE, match); \
} while (0)
static inline const struct ipset_type *
adt_type_get(struct ipset_session *session)
{
struct ipset_data *data;
struct ipset *s;
struct ipset_type *t;
const struct ipset_type *match;
const char *setname, *typename;
const uint8_t *revision;
uint8_t family = AF_UNSPEC;
int ret;
data = ipset_session_data(session);
assert(data);
setname = ipset_data_setname(data);
assert(setname);
/* Check existing sets in cache */
for (s = setlist; s != NULL; s = s->next) {
if (STREQ(setname, s->name)) {
ipset_data_set(data, IPSET_OPT_FAMILY, &s->family);
ipset_data_set(data, IPSET_OPT_TYPE, s->type);
return s->type;
}
}
/* Check kernel */
ret = ipset_cmd(session, IPSET_CMD_HEADER, 0);
if (ret != 0)
return NULL;
typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
revision = ipset_data_get(data, IPSET_OPT_REVISION);
family = ipset_data_family(data);
/* Check registered types */
for (t = typelist, match = NULL;
t != NULL && match == NULL; t = t->next) {
if (t->kernel_check == IPSET_KERNEL_MISMATCH)
continue;
if (STREQ(typename, t->name)
&& MATCH_FAMILY(t, family)
&& *revision == t->revision) {
t->kernel_check = IPSET_KERNEL_OK;
match = t;
}
}
if (!match)
return ipset_errptr(session,
"Kernel-library incompatibility: "
"set %s in kernel has got settype %s "
"with family %s and revision %u while "
"ipset library does not support the "
"settype with that family and revision.",
setname, typename,
family == AF_INET ? "inet" :
family == AF_INET6 ? "inet6" : "unspec",
*revision);
set_family_and_type(data, match, family);
return match;
}
/**
* ipset_type_get - get a set type from the kernel
* @session: session structure
* @cmd: the command which needs the set type
*
* Build up and send a private message to the kernel in order to
* get the set type. When creating the set, we send the typename
* and family and get the supported revisions of the given set type.
* When adding/deleting/testing an entry, we send the setname and
* receive the typename, family and revision.
*
* Returns the set type for success and NULL for failure.
*/
const struct ipset_type *
ipset_type_get(struct ipset_session *session, enum ipset_cmd cmd)
{
assert(session);
switch (cmd) {
case IPSET_CMD_CREATE:
return create_type_get(session);
case IPSET_CMD_ADD:
case IPSET_CMD_DEL:
case IPSET_CMD_TEST:
return adt_type_get(session);
default:
break;
}
assert(cmd == IPSET_CMD_NONE);
return NULL;
}
/**
* ipset_type_check - check the set type received from kernel
* @session: session structure
*
* Check the set type received from the kernel (typename, revision,
* family) against the userspace types looking for a matching type.
*
* Returns the set type for success and NULL for failure.
*/
const struct ipset_type *
ipset_type_check(struct ipset_session *session)
{
const struct ipset_type *t, *match = NULL;
struct ipset_data *data;
const char *typename;
uint8_t family = AF_UNSPEC, revision;
assert(session);
data = ipset_session_data(session);
assert(data);
typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
family = ipset_data_family(data);
revision = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_REVISION);
/* Check registered types */
for (t = typelist; t != NULL && match == NULL; t = t->next) {
if (t->kernel_check == IPSET_KERNEL_MISMATCH)
continue;
if (ipset_match_typename(typename, t)
&& MATCH_FAMILY(t, family)
&& t->revision == revision)
match = t;
}
if (!match)
return ipset_errptr(session,
"Kernel and userspace incompatible: "
"settype %s with revision %u not supported ",
"by userspace.", typename, revision);
set_family_and_type(data, match, family);
return match;
}
/**
* ipset_type_add - add (register) a userspace set type
* @type: pointer to the set type structure
*
* Add the given set type to the type list. The types
* are added sorted, in descending revision number.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_type_add(struct ipset_type *type)
{
struct ipset_type *t, *prev;
assert(type);
/* Add to the list: higher revision numbers first */
for (t = typelist, prev = NULL; t != NULL; t = t->next) {
if (STREQ(t->name, type->name)) {
if (t->revision == type->revision) {
errno = EEXIST;
return -1;
} else if (t->revision < type->revision) {
type->next = t;
if (prev)
prev->next = type;
else
typelist = type;
return 0;
}
}
if (t->next != NULL && STREQ(t->next->name, type->name)) {
if (t->next->revision == type->revision) {
errno = EEXIST;
return -1;
} else if (t->next->revision < type->revision) {
type->next = t->next;
t->next = type;
return 0;
}
}
prev = t;
}
type->next = typelist;
typelist = type;
return 0;
}
/**
* ipset_typename_resolve - resolve typename alias
* @str: typename or alias
*
* Check the typenames (and aliases) and return the
* preferred name of the set type.
*
* Returns the name of the matching set type or NULL.
*/
const char *
ipset_typename_resolve(const char *str)
{
const struct ipset_type *t;
for (t = typelist; t != NULL; t = t->next)
if (ipset_match_typename(str, t))
return t->name;
return NULL;
}
/**
* ipset_types - return the list of the set types
*
* The types can be unchecked with respect of the running kernel.
* Only useful for type specific help.
*
* Returns the list of the set types.
*/
const struct ipset_type *
ipset_types(void)
{
return typelist;
}
/**
* ipset_cache_init - initialize set cache
*
* Initialize the set cache in userspace.
*
* Returns 0 on success or a negative error code.
*/
int
ipset_cache_init(void)
{
return 0;
}
/**
* ipset_cache_fini - release the set cache
*
* Release the set cache.
*/
void
ipset_cache_fini(void)
{
struct ipset *set;
while (setlist) {
set = setlist;
setlist = setlist->next;
free(set);
}
}

View File

@@ -0,0 +1,89 @@
#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

@@ -0,0 +1,194 @@
/* 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 <assert.h> /* assert */
#include <errno.h> /* errno */
#include <string.h> /* strerror */
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_get */
#include <libipset/session.h> /* ipset_err */
#include <libipset/types.h> /* struct ipset_type */
#include <libipset/utils.h> /* STRNEQ */
#include <libipset/errcode.h> /* prototypes */
#include <libipset/linux_ip_set_bitmap.h> /* bitmap specific errcodes */
#include <libipset/linux_ip_set_hash.h> /* hash specific errcodes */
#include <libipset/linux_ip_set_list.h> /* list specific errcodes */
/* Core kernel error codes */
static const struct ipset_errcode_table core_errcode_table[] = {
/* Generic error codes */
{ EEXIST, 0,
"The set with the given name does not exist" },
{ IPSET_ERR_PROTOCOL, 0,
"Kernel error received: ipset protocol error" },
/* CREATE specific error codes */
{ EEXIST, IPSET_CMD_CREATE,
"Set cannot be created: set with the same name already exists" },
{ IPSET_ERR_FIND_TYPE, 0,
"Kernel error received: set type does not supported" },
{ IPSET_ERR_MAX_SETS, 0,
"Kernel error received: maximal number of sets reached, "
"cannot create more." },
{ IPSET_ERR_INVALID_NETMASK, 0,
"The value of the netmask parameter is invalid" },
{ IPSET_ERR_INVALID_FAMILY, 0,
"The protocol family not supported by the set type" },
/* DESTROY specific error codes */
{ IPSET_ERR_BUSY, IPSET_CMD_DESTROY,
"Set cannot be destroyed: it is in use by a kernel component" },
/* FLUSH specific error codes */
/* RENAME specific error codes */
{ IPSET_ERR_EXIST_SETNAME2, IPSET_CMD_RENAME,
"Set cannot be renamed: a set with the new name already exists" },
{ IPSET_ERR_REFERENCED, IPSET_CMD_RENAME,
"Set cannot be renamed: it is in use by another system" },
/* SWAP specific error codes */
{ IPSET_ERR_EXIST_SETNAME2, IPSET_CMD_SWAP,
"Sets cannot be swapped: the second set does not exist" },
{ IPSET_ERR_TYPE_MISMATCH, IPSET_CMD_SWAP,
"The sets cannot be swapped: they type does not match" },
/* LIST/SAVE specific error codes */
/* Generic (CADT) error codes */
{ IPSET_ERR_INVALID_CIDR, 0,
"The value of the CIDR parameter of the IP address is invalid" },
{ IPSET_ERR_TIMEOUT, 0,
"Timeout cannot be used: set was created without timeout support" },
{ IPSET_ERR_IPADDR_IPV4, 0,
"An IPv4 address is expected, but not received" },
{ IPSET_ERR_IPADDR_IPV6, 0,
"An IPv6 address is expected, but not received" },
/* ADD specific error codes */
{ IPSET_ERR_EXIST, IPSET_CMD_ADD,
"Element cannot be added to the set: it's already added" },
/* DEL specific error codes */
{ IPSET_ERR_EXIST, IPSET_CMD_DEL,
"Element cannot be deleted from the set: it's not added" },
/* TEST specific error codes */
/* HEADER specific error codes */
/* TYPE specific error codes */
{ EEXIST, IPSET_CMD_TYPE,
"Kernel error received: set type does not supported" },
/* PROTOCOL specific error codes */
{ },
};
/* Bitmap type-specific error codes */
static const struct ipset_errcode_table bitmap_errcode_table[] = {
/* Generic (CADT) error codes */
{ IPSET_ERR_BITMAP_RANGE, 0,
"Element is out of the range of the set" },
{ IPSET_ERR_BITMAP_RANGE_SIZE, IPSET_CMD_CREATE,
"The range you specified exceeds the size limit of the set type" },
{ },
};
/* Hash type-specific error codes */
static const struct ipset_errcode_table hash_errcode_table[] = {
/* Generic (CADT) error codes */
{ IPSET_ERR_HASH_FULL, 0,
"Hash is full, cannot add more elements" },
{ IPSET_ERR_HASH_ELEM, 0,
"Null-valued element, cannot be stored in a hash type of set" },
{ IPSET_ERR_INVALID_PROTO, 0,
"Invalid protocol specified" },
{ IPSET_ERR_MISSING_PROTO, 0,
"Protocol missing, but must be specified" },
{ },
};
/* List type-specific error codes */
static const struct ipset_errcode_table list_errcode_table[] = {
/* Generic (CADT) error codes */
{ IPSET_ERR_NAME, 0,
"Set to be added/deleted/tested as element does not exist." },
{ IPSET_ERR_LOOP, 0,
"Sets with list:set type cannot be added to the set." },
{ IPSET_ERR_BEFORE, 0,
"No reference set specified." },
{ IPSET_ERR_NAMEREF, 0,
"The set to which you referred with 'before' or 'after' "
"does not exist." },
{ IPSET_ERR_LIST_FULL, 0,
"The set is full, more elements cannot be added." },
{ IPSET_ERR_REF_EXIST, 0,
"The set to which you referred with 'before' or 'after' "
"is not added to the set." },
{ },
};
#define MATCH_TYPENAME(a, b) STRNEQ(a, b, strlen(b))
/**
* ipset_errcode - interpret a kernel error code
* @session: session structure
* @errcode: errcode
*
* Find the error code and print the appropriate
* error message into the error buffer.
*
* Returns -1.
*/
int
ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode)
{
const struct ipset_errcode_table *table = core_errcode_table;
int i, generic;
if (errcode >= IPSET_ERR_TYPE_SPECIFIC) {
const struct ipset_type *type;
type = ipset_saved_type(session);
if (type) {
if (MATCH_TYPENAME(type->name, "bitmap:"))
table = bitmap_errcode_table;
else if (MATCH_TYPENAME(type->name, "hash:"))
table = hash_errcode_table;
else if (MATCH_TYPENAME(type->name, "list:"))
table = list_errcode_table;
}
}
retry:
for (i = 0, generic = -1; table[i].errcode; i++) {
if (table[i].errcode == errcode
&& (table[i].cmd == cmd || table[i].cmd == 0)) {
if (table[i].cmd == 0) {
generic = i;
continue;
}
return ipset_err(session, table[i].message);
}
}
if (generic != -1)
return ipset_err(session, table[generic].message);
/* Fall back to the core table */
if (table != core_errcode_table) {
table = core_errcode_table;
goto retry;
}
if (errcode < IPSET_ERR_PRIVATE)
return ipset_err(session, "Kernel error received: %s",
strerror(errcode));
else
return ipset_err(session,
"Undecoded error %u received from kernel",
errcode);
}

View File

@@ -0,0 +1,747 @@
/* Copyright 2000-2002 Joakim Axelsson (gozem@linux.nu)
* Patrick Schaaf (bof@bof.de)
* Copyright 2003-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <ctype.h> /* isspace */
#include <stdarg.h> /* va_* */
#include <stdbool.h> /* bool */
#include <stdio.h> /* fprintf, fgets */
#include <stdlib.h> /* exit */
#include <string.h> /* str* */
#include <config.h>
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* enum ipset_data */
#include <libipset/parse.h> /* ipset_parse_* */
#include <libipset/session.h> /* ipset_session_* */
#include <libipset/types.h> /* struct ipset_type */
#include <libipset/ui.h> /* core options, commands */
#include <libipset/utils.h> /* STREQ */
static char program_name[] = PACKAGE;
static char program_version[] = PACKAGE_VERSION;
static struct ipset_session *session = NULL;
static uint32_t restore_line = 0;
static bool interactive = false;
static char cmdline[1024];
static char *newargv[255];
static int newargc = 0;
/* The known set types: (typename, revision, family) is unique */
extern struct ipset_type ipset_bitmap_ip0;
extern struct ipset_type ipset_bitmap_ipmac0;
extern struct ipset_type ipset_bitmap_port0;
extern struct ipset_type ipset_hash_ip0;
extern struct ipset_type ipset_hash_net0;
extern struct ipset_type ipset_hash_netport0;
extern struct ipset_type ipset_hash_ipport0;
extern struct ipset_type ipset_hash_ipportip0;
extern struct ipset_type ipset_hash_ipportnet0;
extern struct ipset_type ipset_list_set0;
enum exittype {
NO_PROBLEM = 0,
OTHER_PROBLEM,
PARAMETER_PROBLEM,
VERSION_PROBLEM,
SESSION_PROBLEM,
};
static int __attribute__((format(printf,2,3)))
exit_error(int status, const char *msg, ...)
{
bool quiet = !interactive
&& session
&& ipset_envopt_test(session, IPSET_ENV_QUIET);
if (status && msg && !quiet) {
va_list args;
fprintf(stderr, "%s v%s: ", program_name, program_version);
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
if (status != SESSION_PROBLEM)
fprintf(stderr, "\n");
if (status == PARAMETER_PROBLEM)
fprintf(stderr,
"Try `%s help' for more information.\n",
program_name);
}
/* Ignore errors in interactive mode */
if (status && interactive) {
if (session)
ipset_session_report_reset(session);
return -1;
}
if (session)
ipset_session_fini(session);
D("status: %u", status);
exit(status > VERSION_PROBLEM ? OTHER_PROBLEM : status);
/* Unreached */
return -1;
}
static int
handle_error(void)
{
if (ipset_session_warning(session)
&& !ipset_envopt_test(session, IPSET_ENV_QUIET))
fprintf(stderr, "Warning: %s\n",
ipset_session_warning(session));
if (ipset_session_error(session))
return exit_error(SESSION_PROBLEM, "%s",
ipset_session_error(session));
if (!interactive) {
ipset_session_fini(session);
exit(OTHER_PROBLEM);
}
ipset_session_report_reset(session);
return -1;
}
static void
help(void)
{
const struct ipset_commands *c;
const struct ipset_envopts *opt = ipset_envopts;
printf("%s v%s\n\n"
"Usage: %s [options] COMMAND\n\nCommands:\n",
program_name, program_version, program_name);
for (c = ipset_commands; c->cmd; c++) {
printf("%s %s\n", c->name[0], c->help);
}
printf("\nOptions:\n");
while (opt->flag) {
if (opt->help)
printf("%s %s\n", opt->name[0], opt->help);
opt++;
}
}
/* Build faked argv from parsed line */
static void
build_argv(char *buffer)
{
char *ptr;
int i;
/* Reset */
for (i = 1; i < newargc; i++)
newargv[i] = NULL;
newargc = 1;
ptr = strtok(buffer, " \t\n");
newargv[newargc++] = ptr;
while ((ptr = strtok(NULL, " \t\n")) != NULL) {
if ((newargc + 1) < (int)(sizeof(newargv)/sizeof(char *)))
newargv[newargc++] = ptr;
else {
exit_error(PARAMETER_PROBLEM,
"Line is too long to parse.");
return;
}
}
}
/* Main parser function, workhorse */
int parse_commandline(int argc, char *argv[]);
/*
* Performs a restore from stdin
*/
static int
restore(char *argv0)
{
int ret = 0;
char *c;
/* Initialize newargv/newargc */
newargc = 0;
newargv[newargc++] = argv0;
while (fgets(cmdline, sizeof(cmdline), stdin)) {
restore_line++;
c = cmdline;
while (isspace(c[0]))
c++;
if (c[0] == '\0' || c[0] == '#')
continue;
else if (strcmp(c, "COMMIT\n") == 0) {
ret = ipset_commit(session);
if (ret < 0)
handle_error();
continue;
}
/* Build faked argv, argc */
build_argv(c);
/* Execute line */
ret = parse_commandline(newargc, newargv);
if (ret < 0)
handle_error();
}
/* implicit "COMMIT" at EOF */
ret = ipset_commit(session);
if (ret < 0)
handle_error();
return ret;
}
static int
call_parser(int *argc, char *argv[], const struct ipset_arg *args)
{
int i = 1, ret = 0;
const struct ipset_arg *arg;
const char *optstr;
/* Currently CREATE and ADT may have got additional arguments */
if (!args)
goto done;
for (arg = args; arg->opt; arg++) {
for (i = 1; i < *argc; ) {
D("argc: %u, i: %u: %s vs %s",
*argc, i, argv[i], arg->name[0]);
if (!(ipset_match_option(argv[i], arg->name))) {
i++;
continue;
}
optstr = argv[i];
/* Shift off matched option */
D("match %s", arg->name[0]);
ipset_shift_argv(argc, argv, i);
D("argc: %u, i: %u", *argc, i);
switch (arg->has_arg) {
case IPSET_MANDATORY_ARG:
if (i + 1 > *argc)
return exit_error(PARAMETER_PROBLEM,
"Missing mandatory argument "
"of option `%s'",
arg->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
if (i + 1 <= *argc) {
ret = ipset_call_parser(session,
arg->parse,
optstr, arg->opt,
argv[i]);
if (ret < 0)
return ret;
ipset_shift_argv(argc, argv, i);
break;
}
/* Fall through */
default:
ret = ipset_call_parser(session,
arg->parse,
optstr, arg->opt,
optstr);
if (ret < 0)
return ret;
}
}
}
done:
if (i < *argc)
return exit_error(PARAMETER_PROBLEM,
"Unknown argument: `%s'",
argv[i]);
return ret;
}
static enum ipset_adt
cmd2cmd(int cmd)
{
switch(cmd) {
case IPSET_CMD_ADD:
return IPSET_ADD;
case IPSET_CMD_DEL:
return IPSET_DEL;
case IPSET_CMD_TEST:
return IPSET_TEST;
case IPSET_CMD_CREATE:
return IPSET_CREATE;
default:
return 0;
}
}
static void
check_mandatory(const struct ipset_type *type, enum ipset_cmd command)
{
enum ipset_adt cmd = cmd2cmd(command);
uint64_t flags = ipset_data_flags(ipset_session_data(session));
uint64_t mandatory = type->mandatory[cmd];
const struct ipset_arg *arg = type->args[cmd];
/* Range can be expressed by ip/cidr */
if (flags & IPSET_FLAG(IPSET_OPT_CIDR))
flags |= IPSET_FLAG(IPSET_OPT_IP_TO);
mandatory &= ~flags;
if (!mandatory)
return;
if (!arg) {
exit_error(OTHER_PROBLEM,
"There are missing mandatory flags "
"but can't check them. "
"It's a bug, please report the problem.");
return;
}
for (; arg->opt; arg++)
if (mandatory & IPSET_FLAG(arg->opt)) {
exit_error(PARAMETER_PROBLEM,
"Mandatory option `%s' is missing",
arg->name[0]);
return;
}
}
static const char *
cmd2name(enum ipset_cmd cmd)
{
const struct ipset_commands *c;
for (c = ipset_commands; c->cmd; c++)
if (cmd == c->cmd)
return c->name[0];
return "unknown command";
}
static const char *
session_family(void)
{
switch (ipset_data_family(ipset_session_data(session))) {
case AF_INET:
return "inet";
case AF_INET6:
return "inet6";
default:
return "unspec";
}
}
static void
check_allowed(const struct ipset_type *type, enum ipset_cmd command)
{
uint64_t flags = ipset_data_flags(ipset_session_data(session));
enum ipset_adt cmd = cmd2cmd(command);
uint64_t allowed = type->full[cmd];
uint64_t cmdflags = command == IPSET_CMD_CREATE
? IPSET_CREATE_FLAGS : IPSET_ADT_FLAGS;
const struct ipset_arg *arg = type->args[cmd];
enum ipset_opt i;
/* Range can be expressed by ip/cidr or from-to */
if (allowed & IPSET_FLAG(IPSET_OPT_IP_TO))
allowed |= IPSET_FLAG(IPSET_OPT_CIDR);
for (i = IPSET_OPT_IP; i < IPSET_OPT_FLAGS; i++) {
if (!(cmdflags & IPSET_FLAG(i))
|| (allowed & IPSET_FLAG(i))
|| !(flags & IPSET_FLAG(i)))
continue;
/* Not allowed element-expressions */
switch (i) {
case IPSET_OPT_CIDR:
exit_error(OTHER_PROBLEM,
"IP/CIDR range is not allowed in command %s "
"with set type %s and family %s",
cmd2name(command), type->name, session_family());
return;
case IPSET_OPT_IP_TO:
exit_error(OTHER_PROBLEM,
"FROM-TO IP range is not allowed in command %s "
"with set type %s and family %s",
cmd2name(command), type->name, session_family());
return;
case IPSET_OPT_PORT_TO:
exit_error(OTHER_PROBLEM,
"FROM-TO port range is not allowed in command %s "
"with set type %s and family %s",
cmd2name(command), type->name, session_family());
return;
default:
break;
}
/* Other options */
if (!arg) {
exit_error(OTHER_PROBLEM,
"There are not allowed options (%u) "
"but option list is NULL. "
"It's a bug, please report the problem.", i);
return;
}
for (; arg->opt; arg++) {
if (arg->opt != i)
continue;
exit_error(OTHER_PROBLEM,
"%s parameter is not allowed in command %s "
"with set type %s and family %s",
arg->name[0],
cmd2name(command), type->name, session_family());
return;
}
exit_error(OTHER_PROBLEM,
"There are not allowed options (%u) "
"but can't resolve them. "
"It's a bug, please report the problem.", i);
return;
}
}
static const struct ipset_type *
type_find(const char *name)
{
const struct ipset_type *t = ipset_types();
while (t) {
if (ipset_match_typename(name, t))
return t;
t = t->next;
}
return NULL;
}
/* Workhorse */
int
parse_commandline(int argc, char *argv[])
{
int ret = 0;
enum ipset_cmd cmd = IPSET_CMD_NONE;
int i;
char *arg0 = NULL, *arg1 = NULL, *c;
const struct ipset_envopts *opt;
const struct ipset_commands *command;
const struct ipset_type *type;
/* Initialize session */
if (session == NULL) {
session = ipset_session_init(printf);
if (session == NULL)
return exit_error(OTHER_PROBLEM,
"Cannot initialize ipset session, aborting.");
}
/* Commandline parsing, somewhat similar to that of 'ip' */
/* First: parse core options */
for (opt = ipset_envopts; opt->flag; opt++) {
for (i = 1; i < argc; ) {
if (!ipset_match_envopt(argv[i], opt->name)) {
i++;
continue;
}
/* Shift off matched option */
ipset_shift_argv(&argc, argv, i);
switch (opt->has_arg) {
case IPSET_MANDATORY_ARG:
if (i + 1 > argc)
return exit_error(PARAMETER_PROBLEM,
"Missing mandatory argument "
"to option %s",
opt->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
if (i + 1 <= argc) {
ret = opt->parse(session, opt->flag,
argv[i]);
if (ret < 0)
return handle_error();
ipset_shift_argv(&argc, argv, i);
}
break;
case IPSET_NO_ARG:
ret = opt->parse(session, opt->flag,
opt->name[0]);
if (ret < 0)
return handle_error();
break;
default:
break;
}
}
}
/* Second: parse command */
for (command = ipset_commands;
command->cmd && cmd == IPSET_CMD_NONE;
command++) {
for (i = 1; i < argc; ) {
if (!ipset_match_cmd(argv[1], command->name)) {
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 */
ipset_shift_argv(&argc, argv, i);
cmd = command->cmd;
switch (command->has_arg) {
case IPSET_MANDATORY_ARG:
case IPSET_MANDATORY_ARG2:
if (i + 1 > argc)
return exit_error(PARAMETER_PROBLEM,
"Missing mandatory argument "
"to command %s",
command->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
arg0 = argv[i];
if (i + 1 <= argc)
/* Shift off first arg */
ipset_shift_argv(&argc, argv, i);
break;
default:
break;
}
if (command->has_arg == IPSET_MANDATORY_ARG2) {
if (i + 1 > argc)
return exit_error(PARAMETER_PROBLEM,
"Missing second mandatory "
"argument to command %s",
command->name[0]);
arg1 = argv[i];
/* Shift off second arg */
ipset_shift_argv(&argc, argv, i);
}
break;
}
}
/* Third: catch interactive mode, handle help, version */
switch (cmd) {
case IPSET_CMD_NONE:
if (interactive) {
printf("No command specified\n");
return 0;
}
if (argc > 1 && STREQ(argv[1], "-")) {
interactive = true;
printf("%s> ", program_name);
/* Initialize newargv/newargc */
newargv[newargc++] = program_name;
while (fgets(cmdline, sizeof(cmdline), stdin)) {
c = cmdline;
while (isspace(c[0]))
c++;
if (c[0] == '\0' || c[0] == '#')
continue;
/* Build fake argv, argc */
build_argv(c);
/* Execute line: ignore errors */
parse_commandline(newargc, newargv);
printf("%s> ", program_name);
}
return exit_error(NO_PROBLEM, NULL);
}
if (argc > 1)
return exit_error(PARAMETER_PROBLEM,
"No command specified: unknown argument %s",
argv[1]);
return exit_error(PARAMETER_PROBLEM, "No command specified.");
case IPSET_CMD_VERSION:
printf("%s v%s.\n", program_name, program_version);
if (interactive)
return 0;
return exit_error(NO_PROBLEM, NULL);
case IPSET_CMD_HELP:
help();
if (interactive
|| !ipset_envopt_test(session, IPSET_ENV_QUIET)) {
if (arg0) {
/* Type-specific help, without kernel checking */
type = type_find(arg0);
if (!type)
return exit_error(PARAMETER_PROBLEM,
"Unknown settype: `%s'", arg0);
printf("\n%s type specific options:\n\n%s",
type->name, type->usage);
if (type->usagefn)
type->usagefn();
if (type->family == AF_UNSPEC)
printf("\nType %s is family neutral.\n",
type->name);
else if (type->family == AF_INET46)
printf("\nType %s supports INET "
"and INET6.\n",
type->name);
else
printf("\nType %s supports family "
"%s only.\n",
type->name,
type->family == AF_INET
? "INET" : "INET6");
} else {
printf("\nSupported set types:\n");
type = ipset_types();
while (type) {
printf(" %s\n", type->name);
type = type->next;
}
}
}
if (interactive)
return 0;
return exit_error(NO_PROBLEM, NULL);
case IPSET_CMD_QUIT:
return exit_error(NO_PROBLEM, NULL);
default:
break;
}
/* Forth: parse command args and issue the command */
switch (cmd) {
case IPSET_CMD_CREATE:
/* Args: setname typename [type specific options] */
ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
if (ret < 0)
return handle_error();
ret = ipset_parse_typename(session, IPSET_OPT_TYPENAME, arg1);
if (ret < 0)
return handle_error();
type = ipset_type_get(session, cmd);
if (type == NULL)
return handle_error();
/* Parse create options */
ret = call_parser(&argc, argv, type->args[IPSET_CREATE]);
if (ret < 0)
return handle_error();
else if (ret)
return ret;
/* Check mandatory, then allowed options */
check_mandatory(type, cmd);
check_allowed(type, cmd);
break;
case IPSET_CMD_DESTROY:
case IPSET_CMD_FLUSH:
case IPSET_CMD_LIST:
case IPSET_CMD_SAVE:
/* Args: [setname] */
if (arg0) {
ret = ipset_parse_setname(session,
IPSET_SETNAME, arg0);
if (ret < 0)
return handle_error();
}
break;
case IPSET_CMD_RENAME:
case IPSET_CMD_SWAP:
/* Args: from-setname to-setname */
ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
if (ret < 0)
return handle_error();
ret = ipset_parse_setname(session, IPSET_OPT_SETNAME2, arg1);
if (ret < 0)
return handle_error();
break;
case IPSET_CMD_RESTORE:
/* Restore mode */
if (argc > 1)
return exit_error(PARAMETER_PROBLEM,
"Unknown argument %s", argv[1]);
return restore(argv[0]);
case IPSET_CMD_ADD:
case IPSET_CMD_DEL:
case IPSET_CMD_TEST:
D("ADT: setname %s", arg0);
/* Args: setname ip [options] */
ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
if (ret < 0)
return handle_error();
type = ipset_type_get(session, cmd);
if (type == NULL)
return handle_error();
ret = ipset_parse_elem(session, type->last_elem_optional, arg1);
if (ret < 0)
return handle_error();
/* Parse additional ADT options */
ret = call_parser(&argc, argv, type->args[cmd2cmd(cmd)]);
if (ret < 0)
return handle_error();
else if (ret)
return ret;
/* Check mandatory, then allowed options */
check_mandatory(type, cmd);
check_allowed(type, cmd);
break;
default:
break;
}
if (argc > 1)
return exit_error(PARAMETER_PROBLEM,
"Unknown argument %s", argv[1]);
ret = ipset_cmd(session, cmd, restore_line);
D("ret %d", ret);
/* Special case for TEST and non-quiet mode */
if (cmd == IPSET_CMD_TEST && ipset_session_warning(session)) {
if (!ipset_envopt_test(session, IPSET_ENV_QUIET))
fprintf(stderr, "%s", ipset_session_warning(session));
ipset_session_report_reset(session);
}
if (ret < 0)
handle_error();
return ret;
}
int
main(int argc, char *argv[])
{
/* Register types */
ipset_type_add(&ipset_bitmap_ip0);
ipset_type_add(&ipset_bitmap_ipmac0);
ipset_type_add(&ipset_bitmap_port0);
ipset_type_add(&ipset_hash_ip0);
ipset_type_add(&ipset_hash_net0);
ipset_type_add(&ipset_hash_netport0);
ipset_type_add(&ipset_hash_ipport0);
ipset_type_add(&ipset_hash_ipportip0);
ipset_type_add(&ipset_hash_ipportnet0);
ipset_type_add(&ipset_list_set0);
return parse_commandline(argc, argv);
}

View File

@@ -0,0 +1,97 @@
/* 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/types.h> /* prototypes */
/* Parse commandline arguments */
static const struct ipset_arg bitmap_ip_create_args[] = {
{ .name = { "range", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_netrange, .print = ipset_print_ip,
},
{ .name = { "netmask", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NETMASK,
.parse = ipset_parse_netmask, .print = ipset_print_number,
},
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
{ .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_single_ip,
},
{ .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
.parse = ipset_parse_single_ip,
},
{ .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_net,
},
{ },
};
static const struct ipset_arg bitmap_ip_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 bitmap_ip_usage[] =
"create SETNAME bitmap:ip range IP/CIDR|FROM-TO\n"
" [netmask CIDR] [timeout VALUE]\n"
"add SETNAME IP|IP/CIDR|FROM-TO [timeout VALUE]\n"
"del SETNAME IP|IP/CIDR|FROM-TO\n"
"test SETNAME IP\n\n"
"where IP, FROM and TO are IPv4 addresses (or hostnames),\n"
" CIDR is a valid IPv4 CIDR prefix.\n";
struct ipset_type ipset_bitmap_ip0 = {
.name = "bitmap:ip",
.alias = { "ipmap", NULL },
.revision = 0,
.family = AF_INET,
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_ip,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
},
.args = {
[IPSET_CREATE] = bitmap_ip_create_args,
[IPSET_ADD] = bitmap_ip_add_args,
},
.mandatory = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_IP_TO),
[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_IP)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_NETMASK)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_IP_TO),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
},
.usage = bitmap_ip_usage,
};

View File

@@ -0,0 +1,100 @@
/* 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/types.h> /* prototypes */
/* Parse commandline arguments */
static const struct ipset_arg bitmap_ipmac_create_args[] = {
{ .name = { "range", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_netrange, .print = ipset_print_ip,
},
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
{ .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_single_ip,
},
{ .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
.parse = ipset_parse_single_ip,
},
{ .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_net,
},
{ },
};
static const struct ipset_arg bitmap_ipmac_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 bitmap_ipmac_usage[] =
"create SETNAME bitmap:ip,mac range IP/CIDR|FROM-TO\n"
" [matchunset] [timeout VALUE]\n"
"add SETNAME IP[,MAC] [timeout VALUE]\n"
"del SETNAME IP[,MAC]\n"
"test SETNAME IP[,MAC]\n\n"
"where IP, FROM and TO are IPv4 addresses (or hostnames),\n"
" CIDR is a valid IPv4 CIDR prefix,\n"
" MAC is a valid MAC address.\n";
struct ipset_type ipset_bitmap_ipmac0 = {
.name = "bitmap:ip,mac",
.alias = { "macipmap", NULL },
.revision = 0,
.family = AF_INET,
.dimension = IPSET_DIM_TWO,
.last_elem_optional = true,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_single_ip,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
[IPSET_DIM_TWO] = {
.parse = ipset_parse_ether,
.print = ipset_print_ether,
.opt = IPSET_OPT_ETHER
},
},
.args = {
[IPSET_CREATE] = bitmap_ipmac_create_args,
[IPSET_ADD] = bitmap_ipmac_add_args,
},
.mandatory = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_IP_TO),
[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_IP)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_ETHER)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_ETHER),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_ETHER),
},
.usage = bitmap_ipmac_usage,
};

View File

@@ -0,0 +1,87 @@
/* 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/types.h> /* prototypes */
/* Parse commandline arguments */
static const struct ipset_arg bitmap_port_create_args[] = {
{ .name = { "range", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT,
.parse = ipset_parse_tcp_port, .print = ipset_print_port,
},
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
{ .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT,
.parse = ipset_parse_single_tcp_port,
},
{ .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT_TO,
.parse = ipset_parse_single_tcp_port,
},
{ },
};
static const struct ipset_arg bitmap_port_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 bitmap_port_usage[] =
"create SETNAME bitmap:port range FROM-TO\n"
" [timeout VALUE]\n"
"add SETNAME PORT|FROM-TO [timeout VALUE]\n"
"del SETNAME PORT|FROM-TO\n"
"test SETNAME PORT\n\n"
"where PORT, FROM and TO are port numbers or port names from /etc/services.\n";
struct ipset_type ipset_bitmap_port0 = {
.name = "bitmap:port",
.alias = { "portmap", NULL },
.revision = 0,
.family = AF_UNSPEC,
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_tcp_port,
.print = ipset_print_port,
.opt = IPSET_OPT_PORT
},
},
.args = {
[IPSET_CREATE] = bitmap_port_create_args,
[IPSET_ADD] = bitmap_port_add_args,
},
.mandatory = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_PORT),
},
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_PORT),
},
.usage = bitmap_port_usage,
};

View File

@@ -0,0 +1,120 @@
/* 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/types.h> /* prototypes */
/* Parse commandline arguments */
static const struct ipset_arg hash_ip_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 = { "netmask", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NETMASK,
.parse = ipset_parse_netmask, .print = ipset_print_number,
},
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Ignored options: backward compatibilty */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "resize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "gc", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_GC,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ },
};
static const struct ipset_arg hash_ip_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_ip_usage[] =
"create SETNAME hash:ip\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [netmask CIDR] [timeout VALUE]\n"
"add SETNAME IP [timeout VALUE]\n"
"del SETNAME IP\n"
"test SETNAME IP\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 in IP/CIDR or FROM-TO form\n"
" is supported for IPv4.\n";
struct ipset_type ipset_hash_ip0 = {
.name = "hash:ip",
.alias = { "iphash", "iptree", "iptreemap", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_ip4_single6,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
},
.compat_parse_elem = ipset_parse_iptimeout,
.args = {
[IPSET_CREATE] = hash_ip_create_args,
[IPSET_ADD] = hash_ip_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_NETMASK)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_IP_TO),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
},
.usage = hash_ip_usage,
};

View File

@@ -0,0 +1,144 @@
/* 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_ipport_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,
},
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "resize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_ignored,
},
{ .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
.parse = ipset_parse_ignored,
},
{ .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_ignored,
},
{ },
};
static const struct ipset_arg hash_ipport_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_ipport_usage[] =
"create SETNAME hash:ip,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP,PROTO:PORT [timeout VALUE]\n"
"del SETNAME IP,PROTO:PORT\n"
"test SETNAME IP,PROTO:PORT\n\n"
"where depending on the INET family\n"
" IP is a valid IPv4 or IPv6 address (or hostname).\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n"
" is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_ipport0 = {
.name = "hash:ip,port",
.alias = { "ipporthash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_TWO,
.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
},
},
.args = {
[IPSET_CREATE] = hash_ipport_create_args,
[IPSET_ADD] = hash_ipport_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_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_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_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO),
},
.usage = hash_ipport_usage,
.usagefn = ipset_port_usage,
};

View File

@@ -0,0 +1,155 @@
/* 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_ipportip_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,
},
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "resize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_ignored,
},
{ .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
.parse = ipset_parse_ignored,
},
{ .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_ignored,
},
{ },
};
static const struct ipset_arg hash_ipportip_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_ipportip_usage[] =
"create SETNAME hash:ip,port,ip\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP,PROTO:PORT,IP [timeout VALUE]\n"
"del SETNAME IP,PROTO:PORT,IP\n"
"test SETNAME IP,PROTO:PORT,IP\n\n"
"where depending on the INET family\n"
" IP is a valid IPv4 or IPv6 address (or hostname).\n"
" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
" in the first IP component is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n"
" is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_ipportip0 = {
.name = "hash:ip,port,ip",
.alias = { "ipportiphash", NULL },
.revision = 0,
.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_single_ip,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP2
},
},
.args = {
[IPSET_CREATE] = hash_ipportip_create_args,
[IPSET_ADD] = hash_ipportip_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_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_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| 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_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_IP2),
},
.usage = hash_ipportip_usage,
.usagefn = ipset_port_usage,
};

View File

@@ -0,0 +1,159 @@
/* 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_ipportnet_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,
},
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "resize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_ignored,
},
{ .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
.parse = ipset_parse_ignored,
},
{ .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_ignored,
},
{ },
};
static const struct ipset_arg hash_ipportnet_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_ipportnet_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 the first IP component is supported for IPv4.\n"
" Adding/deleting multiple elements with TCP/UDP port range\n"
" is supported both for IPv4 and IPv6.\n";
struct ipset_type ipset_hash_ipportnet0 = {
.name = "hash:ip,port,net",
.alias = { "ipportnethash", NULL },
.revision = 0,
.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_ipnet,
.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_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_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| 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_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_ipportnet_usage,
.usagefn = ipset_port_usage,
};

View File

@@ -0,0 +1,109 @@
/* 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/types.h> /* prototypes */
/* Parse commandline arguments */
static const struct ipset_arg hash_net_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,
},
/* Ignored options: backward compatibilty */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ .name = { "resize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
.parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ },
};
static const struct ipset_arg hash_net_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_net_usage[] =
"create SETNAME hash:net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP[/CIDR] [timeout VALUE]\n"
"del SETNAME IP[/CIDR]\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";
struct ipset_type ipset_hash_net0 = {
.name = "hash:net",
.alias = { "nethash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_ipnet,
.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_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR),
},
.usage = hash_net_usage,
};

View File

@@ -0,0 +1,122 @@
/* 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_netport_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/UDP port range supported.\n";
struct ipset_type ipset_hash_netport0 = {
.name = "hash:net,port",
.alias = { "netporthash", NULL },
.revision = 0,
.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_netport_usage,
.usagefn = ipset_port_usage,
};

View File

@@ -0,0 +1,91 @@
/* 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/types.h> /* prototypes */
/* Parse commandline arguments */
static const struct ipset_arg list_set_create_args[] = {
{ .name = { "size", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_SIZE,
.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 list_set_adt_args[] = {
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
{ .name = { "before", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NAMEREF,
.parse = ipset_parse_before,
},
{ .name = { "after", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NAMEREF,
.parse = ipset_parse_after,
},
{ },
};
static const char list_set_usage[] =
"create SETNAME list:set\n"
" [size VALUE] [timeout VALUE]\n"
"add SETNAME NAME [before|after NAME] [timeout VALUE]\n"
"del SETNAME NAME [before|after NAME]\n"
"test SETNAME NAME [before|after NAME]\n\n"
"where NAME are existing set names.\n";
struct ipset_type ipset_list_set0 = {
.name = "list:set",
.alias = { "setlist", NULL },
.revision = 0,
.family = AF_UNSPEC,
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
.parse = ipset_parse_setname,
.print = ipset_print_name,
.opt = IPSET_OPT_NAME
},
},
.compat_parse_elem = ipset_parse_name_compat,
.args = {
[IPSET_CREATE] = list_set_create_args,
[IPSET_ADD] = list_set_adt_args,
[IPSET_DEL] = list_set_adt_args,
[IPSET_TEST] = list_set_adt_args,
},
.mandatory = {
[IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_NAME),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_NAME),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_NAME),
},
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_SIZE)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_NAME)
| IPSET_FLAG(IPSET_OPT_BEFORE)
| IPSET_FLAG(IPSET_OPT_NAMEREF)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_NAME)
| IPSET_FLAG(IPSET_OPT_BEFORE)
| IPSET_FLAG(IPSET_OPT_NAMEREF),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_NAME)
| IPSET_FLAG(IPSET_OPT_BEFORE)
| IPSET_FLAG(IPSET_OPT_NAMEREF),
},
.usage = list_set_usage,
};

276
extensions/ipset-5/src/ui.c Normal file
View File

@@ -0,0 +1,276 @@
/* 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 <assert.h> /* assert */
#include <ctype.h> /* tolower */
#include <string.h> /* memcmp, str* */
#include <libipset/linux_ip_set.h> /* IPSET_CMD_* */
#include <libipset/icmp.h> /* id_to_icmp */
#include <libipset/icmpv6.h> /* id_to_icmpv6 */
#include <libipset/types.h> /* IPSET_*_ARG */
#include <libipset/session.h> /* ipset_envopt_parse */
#include <libipset/parse.h> /* ipset_parse_family */
#include <libipset/print.h> /* ipset_print_family */
#include <libipset/utils.h> /* STREQ */
#include <libipset/ui.h> /* prototypes */
/* Commands and environment options */
const struct ipset_commands ipset_commands[] = {
/* Order is important */
{ /* c[reate], --create, n, -N */
.cmd = IPSET_CMD_CREATE,
.name = { "create", "n" },
.has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME TYPENAME [type-specific-options]\n"
" Create a new set",
},
{ /* a[dd], --add, -A */
.cmd = IPSET_CMD_ADD,
.name = { "add", NULL },
.has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME ENTRY\n"
" Add entry to the named set",
},
{ /* d[el], --del, -D */
.cmd = IPSET_CMD_DEL,
.name = { "del", NULL },
.has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME ENTRY\n"
" Delete entry from the named set",
},
{ /* t[est], --test, -T */
.cmd = IPSET_CMD_TEST,
.name = { "test", NULL },
.has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME ENTRY\n"
" Test entry in the named set",
},
{ /* des[troy], --destroy, x, -X */
.cmd = IPSET_CMD_DESTROY,
.name = { "destroy", "x" },
.has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n"
" Destroy a named set or all sets",
},
{ /* l[ist], --list, -L */
.cmd = IPSET_CMD_LIST,
.name = { "list", NULL },
.has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n"
" List the entries of a named set or all sets",
},
{ /* s[save], --save, -S */
.cmd = IPSET_CMD_SAVE,
.name = { "save", NULL },
.has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n"
" Save the named set or all sets to stdout",
},
{ /* r[estore], --restore, -R */
.cmd = IPSET_CMD_RESTORE,
.name = { "restore", NULL },
.has_arg = IPSET_NO_ARG,
.help = "\n"
" Restore a saved state",
},
{ /* f[lush], --flush, -F */
.cmd = IPSET_CMD_FLUSH,
.name = { "flush", NULL },
.has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n"
" Flush a named set or all sets",
},
{ /* ren[ame], --rename, e, -E */
.cmd = IPSET_CMD_RENAME,
.name = { "rename", "e" },
.has_arg = IPSET_MANDATORY_ARG2,
.help = "FROM-SETNAME TO-SETNAME\n"
" Rename two sets",
},
{ /* sw[ap], --swap, w, -W */
.cmd = IPSET_CMD_SWAP,
.name = { "swap", "w" },
.has_arg = IPSET_MANDATORY_ARG2,
.help = "FROM-SETNAME TO-SETNAME\n"
" Swap the contect of two existing sets",
},
{ /* h[elp, --help, -H */
.cmd = IPSET_CMD_HELP,
.name = { "help", NULL },
.has_arg = IPSET_OPTIONAL_ARG,
.help = "[TYPENAME]\n"
" Print help, and settype specific help",
},
{ /* v[ersion], --version, -V */
.cmd = IPSET_CMD_VERSION,
.name = { "version", NULL },
.has_arg = IPSET_NO_ARG,
.help = "\n"
" Print version information",
},
{ /* q[uit] */
.cmd = IPSET_CMD_QUIT,
.name = { "quit", NULL },
.has_arg = IPSET_NO_ARG,
.help = "\n"
" Quit interactive mode",
},
{ },
};
/* Match a command: try to match as a prefix or letter-command */
bool
ipset_match_cmd(const char *arg, const char * const name[])
{
size_t len;
assert(arg);
assert(name && name[0]);
/* Ignore (two) leading dashes */
if (arg[0] == '-')
arg++;
if (arg[0] == '-')
arg++;
len = strlen(arg);
if (len > strlen(name[0]) || !len)
return false;
else if (strncmp(arg, name[0], len) == 0)
return true;
else if (len != 1)
return false;
else if (name[1] == NULL)
return tolower(arg[0]) == name[0][0];
else
return tolower(arg[0]) == name[1][0];
}
const struct ipset_envopts ipset_envopts[] = {
{ .name = { "-o", "-output" },
.has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX,
.parse = ipset_parse_output,
.help = "plain|save|xml\n"
" Specify output mode for listing sets.\n"
" Default value for \"list\" command is mode \"plain\"\n"
" and for \"save\" command is mode \"save\".",
},
{ .name = { "-s", "-sorted" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_SORTED,
.help = "\n"
" Print elements sorted (if supported by the set type).",
},
{ .name = { "-q", "-quiet" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_QUIET,
.help = "\n"
" Suppress any notice or warning message.",
},
{ .name = { "-r", "-resolve" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_RESOLVE,
.help = "\n"
" Try to resolve IP addresses in the output (slow!)",
},
{ .name = { "-!", "-exist" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_EXIST,
.help = "\n"
" Ignore errors when creating already created sets,\n"
" when adding already existing elements\n"
" or when deleting non-existing elements.",
},
{ },
};
/* Strict option matching */
bool
ipset_match_option(const char *arg, const char * const name[])
{
assert(arg);
assert(name && name[0]);
/* Skip two leading dashes */
if (arg[0] == '-' && arg[1] == '-')
arg++, arg++;
return STREQ(arg, name[0])
|| (name[1] != NULL && STREQ(arg, name[1]));
}
/* Strict envopt matching */
bool
ipset_match_envopt(const char *arg, const char * const name[])
{
assert(arg);
assert(name && name[0]);
/* Skip one leading dash */
if (arg[0] == '-' && arg[1] == '-')
arg++;
return STREQ(arg, name[0])
|| (name[1] != NULL && STREQ(arg, name[1]));
}
/**
* ipset_shift_argv - shift off an argument
* @arc: argument count
* @argv: array of argument strings
* @from: from where shift off an argument
*
* Shift off the argument at "from" from the array of
* arguments argv of size argc.
*/
void
ipset_shift_argv(int *argc, char *argv[], int from)
{
int i;
assert(*argc >= from + 1);
for (i = from + 1; i <= *argc; i++) {
argv[i-1] = argv[i];
}
(*argc)--;
return;
}
/**
* ipset_port_usage - prints the usage for the port parameter
*
* Print the usage for the port parameter to stdout.
*/
void
ipset_port_usage(void)
{
int i;
const char *name;
printf(" [PROTO:]PORT is a valid pattern of the following:\n"
" PORTNAME port name from /etc/services\n"
" PORTNUMBER port number identifier\n"
" tcp|udp:PORTNAME|PORTNUMBER\n"
" icmp:CODENAME supported ICMP codename\n"
" icmp:TYPE/CODE ICMP type/code value\n"
" icmpv6:CODENAME supported ICMPv6 codename\n"
" icmpv6:TYPE/CODE ICMPv6 type/code value\n"
" PROTO:0 all other protocols\n\n");
printf(" Supported ICMP codenames:\n");
i = 0;
while ((name = id_to_icmp(i++)) != NULL)
printf(" %s\n", name);
printf(" Supported ICMPv6 codenames:\n");
i = 0;
while ((name = id_to_icmpv6(i++)) != NULL)
printf(" %s\n", name);
}

419
extensions/ipset-5/xt_set.c Normal file
View File

@@ -0,0 +1,419 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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 which implements the set match and SET target
* for netfilter/iptables. */
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/version.h>
#include <linux/netfilter/x_tables.h>
#include "xt_set.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("Xtables: IP set match and target module");
MODULE_ALIAS("xt_SET");
MODULE_ALIAS("ipt_set");
MODULE_ALIAS("ip6t_set");
MODULE_ALIAS("ipt_SET");
MODULE_ALIAS("ip6t_SET");
static inline int
match_set(ip_set_id_t index, const struct sk_buff *skb,
u8 pf, u8 dim, u8 flags, int inv)
{
if (ip_set_test(index, skb, pf, dim, flags))
inv = !inv;
return inv;
}
/* Revision 0 interface: backward compatible with netfilter/iptables */
/* Backward compatibility constrains (incomplete):
* 2.6.24: [NETLINK]: Introduce nested and byteorder flag to netlink attribute
* 2.6.25: is_vmalloc_addr(): Check if an address is within the vmalloc
* boundaries
* 2.6.27: rcu: split list.h and move rcu-protected lists into rculist.h
* 2.6.28: netfilter: ctnetlink: remove bogus module dependency between
* ctnetlink and nf_nat (nfnl_lock/nfnl_unlock)
* 2.6.29: generic swap(): introduce global macro swap(a, b)
* 2.6.31: netfilter: passive OS fingerprint xtables match
* 2.6.34: rcu: Add lockdep-enabled variants of rcu_dereference()
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
#error "Linux kernel version too old: must be >= 2.6.34"
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
#define CHECK_OK 1
#define CHECK_FAIL 0
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
#define CHECK_OK 0
#define CHECK_FAIL (-EINVAL)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_match_v0(const struct sk_buff *skb, const struct xt_match_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static bool
set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
#endif
{
const struct xt_set_info_match_v0 *info = par->matchinfo;
return match_set(info->match_set.index, skb, par->family,
info->match_set.u.compat.dim,
info->match_set.u.compat.flags,
info->match_set.u.compat.flags & IPSET_INV_MATCH);
}
static void
compat_flags(struct xt_set_info_v0 *info)
{
u_int8_t i;
/* Fill out compatibility data according to enum ip_set_kopt */
info->u.compat.dim = IPSET_DIM_ZERO;
if (info->u.flags[0] & IPSET_MATCH_INV)
info->u.compat.flags |= IPSET_INV_MATCH;
for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
info->u.compat.dim++;
if (info->u.flags[i] & IPSET_SRC)
info->u.compat.flags |= (1<<info->u.compat.dim);
}
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_match_v0_checkentry(const struct xt_mtchk_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_match_v0_checkentry(const struct xt_mtchk_param *par)
#endif
{
struct xt_set_info_match_v0 *info = par->matchinfo;
ip_set_id_t index;
index = ip_set_nfnl_get_byindex(info->match_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match",
info->match_set.index);
return CHECK_FAIL; /* error */
}
if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("That's nasty!");
return CHECK_FAIL; /* error */
}
/* Fill out compatibility data */
compat_flags(&info->match_set);
return CHECK_OK;
}
static void
set_match_v0_destroy(const struct xt_mtdtor_param *par)
{
struct xt_set_info_match_v0 *info = par->matchinfo;
ip_set_nfnl_put(info->match_set.index);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static unsigned int
set_target_v0(struct sk_buff *skb, const struct xt_target_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static unsigned int
set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
#endif
{
const struct xt_set_info_target_v0 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par->family,
info->add_set.u.compat.dim,
info->add_set.u.compat.flags);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, skb, par->family,
info->del_set.u.compat.dim,
info->del_set.u.compat.flags);
return XT_CONTINUE;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_target_v0_checkentry(const struct xt_tgchk_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_target_v0_checkentry(const struct xt_tgchk_param *par)
#endif
{
struct xt_set_info_target_v0 *info = par->targinfo;
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("cannot find add_set index %u as target",
info->add_set.index);
return CHECK_FAIL; /* error */
}
}
if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("cannot find del_set index %u as target",
info->del_set.index);
return CHECK_FAIL; /* error */
}
}
if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0
|| info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("That's nasty!");
return CHECK_FAIL; /* error */
}
/* Fill out compatibility data */
compat_flags(&info->add_set);
compat_flags(&info->del_set);
return CHECK_OK;
}
static void
set_target_v0_destroy(const struct xt_tgdtor_param *par)
{
const struct xt_set_info_target_v0 *info = par->targinfo;
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);
}
/* Revision 1: current interface to netfilter/iptables */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_match(const struct sk_buff *skb, const struct xt_match_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static bool
set_match(const struct sk_buff *skb, struct xt_action_param *par)
#endif
{
const struct xt_set_info_match *info = par->matchinfo;
return match_set(info->match_set.index, skb, par->family,
info->match_set.dim,
info->match_set.flags,
info->match_set.flags & IPSET_INV_MATCH);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_match_checkentry(const struct xt_mtchk_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_match_checkentry(const struct xt_mtchk_param *par)
#endif
{
struct xt_set_info_match *info = par->matchinfo;
ip_set_id_t index;
index = ip_set_nfnl_get_byindex(info->match_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match",
info->match_set.index);
return CHECK_FAIL; /* error */
}
if (info->match_set.dim > IPSET_DIM_MAX) {
pr_warning("That's nasty!");
return CHECK_FAIL; /* error */
}
return CHECK_OK;
}
static void
set_match_destroy(const struct xt_mtdtor_param *par)
{
struct xt_set_info_match *info = par->matchinfo;
ip_set_nfnl_put(info->match_set.index);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static unsigned int
set_target(struct sk_buff *skb, const struct xt_target_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static unsigned int
set_target(struct sk_buff *skb, const struct xt_action_param *par)
#endif
{
const struct xt_set_info_target *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index,
skb, par->family,
info->add_set.dim,
info->add_set.flags);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index,
skb, par->family,
info->add_set.dim,
info->del_set.flags);
return XT_CONTINUE;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static bool
set_target_checkentry(const struct xt_tgchk_param *par)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_target_checkentry(const struct xt_tgchk_param *par)
#endif
{
const struct xt_set_info_target *info = par->targinfo;
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("cannot find add_set index %u as target",
info->add_set.index);
return CHECK_FAIL; /* error */
}
}
if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("cannot find del_set index %u as target",
info->del_set.index);
return CHECK_FAIL; /* error */
}
}
if (info->add_set.dim > IPSET_DIM_MAX
|| info->del_set.flags > IPSET_DIM_MAX) {
pr_warning("That's nasty!");
return CHECK_FAIL; /* error */
}
return CHECK_OK;
}
static void
set_target_destroy(const struct xt_tgdtor_param *par)
{
const struct xt_set_info_target *info = par->targinfo;
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);
}
static struct xt_match set_matches[] __read_mostly = {
{
.name = "set",
.family = NFPROTO_IPV4,
.revision = 0,
.match = set_match_v0,
.matchsize = sizeof(struct xt_set_info_match_v0),
.checkentry = set_match_v0_checkentry,
.destroy = set_match_v0_destroy,
.me = THIS_MODULE
},
{
.name = "set",
.family = NFPROTO_IPV4,
.revision = 1,
.match = set_match,
.matchsize = sizeof(struct xt_set_info_match),
.checkentry = set_match_checkentry,
.destroy = set_match_destroy,
.me = THIS_MODULE
},
{
.name = "set",
.family = NFPROTO_IPV6,
.revision = 1,
.match = set_match,
.matchsize = sizeof(struct xt_set_info_match),
.checkentry = set_match_checkentry,
.destroy = set_match_destroy,
.me = THIS_MODULE
},
};
static struct xt_target set_targets[] __read_mostly = {
{
.name = "SET",
.revision = 0,
.family = NFPROTO_IPV4,
.target = set_target_v0,
.targetsize = sizeof(struct xt_set_info_target_v0),
.checkentry = set_target_v0_checkentry,
.destroy = set_target_v0_destroy,
.me = THIS_MODULE
},
{
.name = "SET",
.revision = 1,
.family = NFPROTO_IPV4,
.target = set_target,
.targetsize = sizeof(struct xt_set_info_target),
.checkentry = set_target_checkentry,
.destroy = set_target_destroy,
.me = THIS_MODULE
},
{
.name = "SET",
.revision = 1,
.family = NFPROTO_IPV6,
.target = set_target,
.targetsize = sizeof(struct xt_set_info_target),
.checkentry = set_target_checkentry,
.destroy = set_target_destroy,
.me = THIS_MODULE
},
};
static int __init xt_set_init(void)
{
int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
if (!ret) {
ret = xt_register_targets(set_targets,
ARRAY_SIZE(set_targets));
if (ret)
xt_unregister_matches(set_matches,
ARRAY_SIZE(set_matches));
}
return ret;
}
static void __exit xt_set_fini(void)
{
xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
}
module_init(xt_set_init);
module_exit(xt_set_fini);

View File

@@ -0,0 +1,55 @@
#ifndef _XT_SET_H
#define _XT_SET_H
#include "ip_set.h"
/* Revision 0 interface: backward compatible with netfilter/iptables */
/*
* Option flags for kernel operations (xt_set_info_v0)
*/
#define IPSET_SRC 0x01 /* Source match/add */
#define IPSET_DST 0x02 /* Destination match/add */
#define IPSET_MATCH_INV 0x04 /* Inverse matching */
struct xt_set_info_v0 {
ip_set_id_t index;
union {
__u32 flags[IPSET_DIM_MAX + 1];
struct {
__u32 __flags[IPSET_DIM_MAX];
__u8 dim;
__u8 flags;
} compat;
} u;
};
/* match and target infos */
struct xt_set_info_match_v0 {
struct xt_set_info_v0 match_set;
};
struct xt_set_info_target_v0 {
struct xt_set_info_v0 add_set;
struct xt_set_info_v0 del_set;
};
/* Revision 1: current interface to netfilter/iptables */
struct xt_set_info {
ip_set_id_t index;
__u8 dim;
__u8 flags;
};
/* match and target infos */
struct xt_set_info_match {
struct xt_set_info match_set;
};
struct xt_set_info_target {
struct xt_set_info add_set;
struct xt_set_info del_set;
};
#endif /*_XT_SET_H*/

View File

@@ -20,6 +20,7 @@ build_gradm=m
build_iface=m build_iface=m
build_ipp2p=m build_ipp2p=m
build_ipset4=m build_ipset4=m
build_ipset5=
build_ipv4options=m build_ipv4options=m
build_length2=m build_length2=m
build_lscan=m build_lscan=m