--- /dev/null
+*~
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+
+/aclocal.m4
+/autom4te.cache
+/compile
+/config.*
+/configure
+/depcomp
+/install-sh
+/libtool
+/ltmain.sh
+/missing
+/stamp-h1
## Process this file with automake to produce Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
include $(top_srcdir)/Make_global.am
if ! WITH_KBUILDDIR
tidy: distclean modules_clean
rm -rf .deps $(foreach dir,$(cleanup_dirs),$(wildcard $(dir)/*~))
rm -rf aclocal.m4 autom4te.cache
- rm -rf config.* configure depcomp install-sh libtool ltmain.sh
+ rm -rf config.* configure compile depcomp install-sh libtool ltmain.sh
rm -rf Makefile Makefile.in lib/Makefile lib/Makefile.in src/Makefile src/Makefile.in
- rm -rf missing stamp-h1
+ rm -rf missing stamp-h1 m4/*
.PHONY: modules modules_instal modules_clean update_includes tests
#!/bin/sh
-run ()
-{
- echo "running: $*"
- eval $*
-
- if test $? != 0 ; then
- echo "error: while running '$*'"
- exit 1
- fi
-}
-
-run aclocal
-run autoheader
-run libtoolize -f
-run automake -a
-run autoconf
+mkdir -p m4
+autoreconf -fi
+rm -rf autom4te.cache
dnl Boilerplate
AC_INIT([ipset], [5.0], [kadlec@blackhole.kfki.hu])
-AC_CANONICAL_SYSTEM
+AC_CANONICAL_TARGET
+AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADER([config.h])
-AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
dnl Shortcut: Linux supported alone
case $target in
dnl Additional arguments
dnl Kernel build directory or source tree
-AC_ARG_WITH([kernel],
- AS_HELP_STRING([--with-kernel=PATH],
- [Path to kernel source/build directory]),
- [KBUILDDIR="$withval";])
+AC_ARG_WITH([kbuild],
+ AS_HELP_STRING([--with-kbuild=PATH],
+ [Path to kernel build directory]),
+ [KBUILDDIR="$withval";])
+AC_ARG_WITH([ksource],
+ AS_HELP_STRING([--with-ksource=PATH],
+ [Path to kernel source directory, if not the same as the kernel build directory]),
+ [KSOURCEDIR="$withval";])
AM_CONDITIONAL(WITH_KBUILDDIR, test "$KBUILDDIR" != "")
AC_SUBST(KBUILDDIR)
kbuilddir="/lib/modules/`uname -r`/build"
fi
-if test ! -e "$kbuilddir/include/linux/netfilter/nfnetlink.h"
+if test -n "$KSOURCEDIR"; then
+ ksourcedir="$KSOURCEDIR"
+elif test -e "$kbuilddir/include/linux/netfilter/nfnetlink.h"; then
+ ksourcedir="$kbuilddir"
+else
+ ksourcedir="/lib/modules/$(uname -r)/source"
+fi
+if test ! -e "$ksourcedir/include/linux/netfilter/nfnetlink.h"
then
- AC_MSG_ERROR([Invalid kernel build directory $kbuilddir])
+ AC_MSG_ERROR([Invalid kernel source directory $ksourcedir])
fi
if test ! -e "$kbuilddir/.config"
fi
dnl Check kernel dependencies: nfnetlink.h
-NFNL_CB_CONST="`./check_const $kbuilddir/include/linux/netfilter/nfnetlink.h`"
+NFNL_CB_CONST="`./check_const $ksourcedir/include/linux/netfilter/nfnetlink.h`"
AC_SUBST(NFNL_CB_CONST)
dnl Check kernel dependencies: netlink.h
-NETLINK_DUMP_CONST="`./check_const $kbuilddir/include/linux/netlink.h`"
+NETLINK_DUMP_CONST="`./check_const $ksourcedir/include/linux/netlink.h`"
AC_SUBST(NETLINK_DUMP_CONST)
dnl Maximal number of sets supported by the kernel, default 256
dnl Checks for programs
AC_PROG_CC
-AC_PROG_LIBTOOL
+AM_PROG_CC_C_O
+LT_INIT
AC_PROG_INSTALL
AC_PROG_LN_S
dnl Checks for libraries
-AC_CHECK_LIB([mnl], [mnl_socket_open])
-if test x"${ac_cv_lib_mnl_mnl_socket_open}" = xno; then
- AC_MSG_ERROR(libmnl not found)
-fi
+PKG_CHECK_MODULES([libmnl], [libmnl >= 1])
dnl Checks for header files
struct ipset_session;
+/* Kernel error code to message table */
struct ipset_errcode_table {
- int errcode;
- enum ipset_cmd cmd;
- const char *message;
+ 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,
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
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, /* rename/swap */
+ 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_TIMEOUT, /* 6 */
IPSET_ATTR_PROTO, /* 7 */
IPSET_ATTR_CADT_FLAGS, /* 8 */
- IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,
+ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
};
#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_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),
IPSET_CADT_MAX,
};
-#define IPSET_IPPROTO_ANY 255
-#define IPSET_IPPROTO_TCPUDP 254
-
#endif /* __IP_SET_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,
};
#ifndef __IP_SET_HASH_H
#define __IP_SET_HASH_H
-/* Bitmap type specific error codes */
+/* 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,
};
/* 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,
};
#include <stdint.h> /* uint32_t */
#include <netinet/in.h> /* struct in[6]_addr */
-/* The same structure to hold IP addresses as in linux/netfilter.h */
+/* The structure to hold IP addresses, same as in linux/netfilter.h */
union nf_inet_addr {
uint32_t all[4];
uint32_t ip;
extern int ipset_parse_ether(struct ipset_session *session,
enum ipset_opt opt, const char *str);
-extern int ipset_parse_single_port(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);
+ 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,
#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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ const struct ipset_data *data,
+ enum ipset_opt opt, uint8_t env);
#endif /* LIBIPSET_PRINT_H */
* 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 except elem */
- uint64_t mandatory[IPSET_CADT_MAX]; /* create/ADT mandatory flags */
- uint64_t full[IPSET_CADT_MAX]; /* full args flags */
- size_t maxsize[IPSET_MAXSIZE_MAX]; /* max sizes */
- const char *usage; /* terse usage */
+ 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 */
+ size_t maxsize[IPSET_MAXSIZE_MAX]; /* max sizes */
+ const char *usage; /* terse usage */
+ void (*usagefn)(void); /* additional usage */
struct ipset_type *next;
- const char *alias[]; /* name alias(es) */
+ const char *alias[]; /* name alias(es) */
};
extern int ipset_cache_add(const char *name, const struct ipset_type *type,
#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;
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 */
#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)
{
obj-m += ip_set.o xt_set.o
obj-m += ip_set_bitmap_ip.o ip_set_bitmap_ipmac.o ip_set_bitmap_port.o
obj-m += ip_set_hash_ip.o ip_set_hash_ipport.o ip_set_hash_ipportip.o
-obj-m += ip_set_hash_net.o ip_set_hash_ipportnet.o
+obj-m += ip_set_hash_net.o ip_set_hash_ipportnet.o ip_set_hash_netport.o
obj-m += ip_set_list_set.o
# It's for me...
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, /* rename/swap */
+ 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_TIMEOUT, /* 6 */
IPSET_ATTR_PROTO, /* 7 */
IPSET_ATTR_CADT_FLAGS, /* 8 */
- IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,
+ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
};
#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_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),
IPSET_CADT_MAX,
};
-#define IPSET_IPPROTO_ANY 255
-#define IPSET_IPPROTO_TCPUDP 254
-
#ifdef __KERNEL__
#include <linux/ip.h>
#include <linux/ipv6.h>
IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG),
IPSET_TYPE_NAME_FLAG = 4,
IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG),
- /* Actually just a flag for dumping */
+ /* 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),
};
int (*uadt)(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags);
- /* Low level add/del/test entries */
+ /* Low level add/del/test functions */
ipset_adtfn adt[IPSET_ADT_MAX];
/* When adding entries and set is full, try to resize the set */
struct netlink_callback *cb);
/* Return true if "b" set is the same as "a"
- * according to the set parameters */
+ * according to the create set parameters */
bool (*same_set)(const struct ip_set *a, const struct ip_set *b);
};
const struct ip_set_type *type;
/* The type variant doing the real job */
const struct ip_set_type_variant *variant;
- /* The actual INET family */
+ /* The actual INET family of the set */
u8 family;
/* The type specific data */
void *data;
kfree(members);
}
+/* Ignore IPSET_ERR_EXIST errors if asked to do so? */
static inline bool
ip_set_eexist(int ret, u32 flags)
{
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htons(value);
}
+static const struct nla_policy
+ipaddr_policy[IPSET_ATTR_IPADDR_MAX + 1] __read_mostly = {
+ [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_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)
/* 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,
};
#include <linux/netfilter/ip_set_slist.h>
#include <linux/netfilter/ip_set_timeout.h>
-#define CONCAT(a, b, c) a##b##c
-#define TOKEN(a, b, c) CONCAT(a, b, c)
-
-/* Cache friendly hash with resizing when linear searching becomes too long.
- * Internally jhash is used with the assumption that the size of the stored
- * data is a multiple of sizeof(u32). If storage supports timeout, the
- * timeout field must be the last one in the data structure.
+/* Cacheline friendly hash with resizing when linear searching becomes too
+ * long. Internally jhash is used with the assumption that the size of the
+ * stored data is a multiple of sizeof(u32). If storage supports timeout,
+ * the timeout field must be the last one in the data structure - that field
+ * is ignored when computing the hash key.
*/
/* Number of elements to store in an array block */
/* Number of arrays: max ARRAY_SIZE * CHAIN_LIMIT "long" chains */
#define CHASH_DEFAULT_CHAIN_LIMIT 3
+/* Book-keeping of the prefixes added to the set */
struct chash_nets {
+ u8 cidr; /* the different cidr values in the set */
u32 nets; /* number of elements per cidr */
- u8 cidr; /* the cidr values added to the set */
};
struct chash {
#ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask; /* netmask value for subnets to store */
#endif
-#ifdef IP_SET_HASH_WITH_PROTO
- u8 proto; /* default protocol for SET target */
-#endif
#ifdef IP_SET_HASH_WITH_NETS
- struct chash_nets nets[0]; /* book keeping of networks */
+ struct chash_nets nets[0]; /* book-keeping of prefixes */
#endif
};
+/* Compute htable_bits from the user input parameter hashsize */
static inline u8
htable_bits(u32 hashsize)
{
return bits;
}
+#ifdef IP_SET_HASH_WITH_NETS
+
+#define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128)
+
+/* Network cidr size book keeping when the hash stores different
+ * sized networks */
static inline void
-add_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr)
+add_cidr(struct chash *h, u8 cidr, u8 host_mask)
{
u8 i;
- pr_debug("add_cidr %u", cidr);
- for (i = 0; i < host_mask - 1 && nets[i].cidr; i++) {
+ ++h->nets[cidr-1].nets;
+
+ pr_debug("add_cidr added %u: %u", cidr, h->nets[cidr-1].nets);
+
+ if (h->nets[cidr-1].nets > 1)
+ return;
+
+ /* New cidr size */
+ for (i = 0; i < host_mask && h->nets[i].cidr; i++) {
/* Add in increasing prefix order, so larger cidr first */
- if (nets[i].cidr < cidr)
- swap(nets[i].cidr, cidr);
+ if (h->nets[i].cidr < cidr)
+ swap(h->nets[i].cidr, cidr);
}
- if (i < host_mask - 1)
- nets[i].cidr = cidr;
+ if (i < host_mask)
+ h->nets[i].cidr = cidr;
}
static inline void
-del_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr)
+del_cidr(struct chash *h, u8 cidr, u8 host_mask)
{
u8 i;
- pr_debug("del_cidr %u", cidr);
- for (i = 0; i < host_mask - 2 && nets[i].cidr; i++) {
- if (nets[i].cidr == cidr)
- nets[i].cidr = cidr = nets[i+1].cidr;
+ --h->nets[cidr-1].nets;
+
+ pr_debug("del_cidr deleted %u: %u", cidr, h->nets[cidr-1].nets);
+
+ if (h->nets[cidr-1].nets != 0)
+ return;
+
+ /* All entries with this cidr size deleted, so cleanup h->cidr[] */
+ for (i = 0; i < host_mask - 1 && h->nets[i].cidr; i++) {
+ if (h->nets[i].cidr == cidr)
+ h->nets[i].cidr = cidr = h->nets[i+1].cidr;
}
- nets[host_mask - 2].cidr = 0;
+ h->nets[i - 1].cidr = 0;
}
+#endif
+/* Destroy the hashtable part of the set */
static void
chash_destroy(struct slist *t, u8 htable_bits)
{
for (i = 0; i < jhash_size(htable_bits); i++)
slist_for_each_safe(n, tmp, &t[i])
- /* FIXME: slab cache */
+ /* FIXME: use slab cache */
kfree(n);
ip_set_free(t);
}
+/* Calculate the actual memory size of the set data */
static size_t
chash_memsize(const struct chash *h, size_t dsize, u8 host_mask)
{
u32 i;
size_t memsize = sizeof(*h)
#ifdef IP_SET_HASH_WITH_NETS
- + sizeof(struct chash_nets) * (host_mask - 1)
+ + sizeof(struct chash_nets) * host_mask
#endif
+ jhash_size(h->htable_bits) * sizeof(struct slist);
return memsize;
}
+/* Flush a hash type of set: destroy all elements */
static void
ip_set_hash_flush(struct ip_set *set)
{
}
#ifdef IP_SET_HASH_WITH_NETS
memset(h->nets, 0, sizeof(struct chash_nets)
- * (set->family == AF_INET ? 31 : 127));
+ * SET_HOST_MASK(set->family));
#endif
h->elements = 0;
}
+/* Destroy a hash type of set */
static void
ip_set_hash_destroy(struct ip_set *set)
{
set->data = NULL;
}
-#define JHASH2(data, initval, htable_bits) \
-jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \
- & jhash_mask(htable_bits)
+#define JHASH2(data, initval, htable_bits) \
+(jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \
+ & jhash_mask(htable_bits))
#endif /* _IP_SET_CHASH_H */
+#define CONCAT(a, b, c) a##b##c
+#define TOKEN(a, b, c) CONCAT(a, b, c)
+
/* Type/family dependent function prototypes */
#define type_pf_data_equal TOKEN(TYPE, PF, _data_equal)
/* Flavour without timeout */
+/* Get the ith element from the array block n */
#define chash_data(n, i) \
(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) \
+ (i)*sizeof(struct type_pf_elem))
+/* Add an element to the hash table when resizing the set:
+ * we spare the maintenance of the internal counters. */
static int
type_pf_chash_readd(struct chash *h, struct slist *t, u8 htable_bits,
const struct type_pf_elem *value, gfp_t gfp_flags)
prev->next = (struct slist *) tmp;
data = chash_data(tmp, 0);
} else {
- /* Rehashing */
+ /* Trigger rehashing */
return -EAGAIN;
}
found:
return 0;
}
+/* Delete an element from the hash table: swap it with the last
+ * element in the hash bucket and free up the array if it was
+ * completely emptied */
static void
type_pf_chash_del_elem(struct chash *h, struct slist *prev,
struct slist *n, int i)
{
struct type_pf_elem *data = chash_data(n, i);
struct slist *tmp;
- int j;
+ int j; /* Index in array */
if (n->next != NULL) {
for (prev = n, tmp = n->next;
type_pf_data_swap(data, chash_data(tmp, j));
}
#ifdef IP_SET_HASH_WITH_NETS
- if (--h->nets[data->cidr-1].nets == 0)
- del_cidr(h->nets, HOST_MASK, data->cidr);
+ del_cidr(h, data->cidr, HOST_MASK);
#endif
if (j == 0) {
prev->next = NULL;
h->elements--;
}
+/* Resize a hash: create a new hash table with doubling the hashsize
+ * and inserting the elements to it. Repeat until we succeed or
+ * fail due to memory pressures. */
static int
type_pf_resize(struct ip_set *set, gfp_t gfp_flags, bool retried)
{
int ret;
retry:
- ret = 0;
+ ret = i = 0;
htable_bits++;
if (!htable_bits)
/* In case we have plenty of memory :-) */
return -ENOMEM;
write_lock_bh(&set->lock);
- for (i = 0; i < jhash_size(h->htable_bits); i++) {
next_slot:
+ for (; i < jhash_size(h->htable_bits); i++) {
slist_for_each(n, &h->htable[i]) {
for (j = 0; j < h->array_size; j++) {
data = chash_data(n, j);
return 0;
}
+/* Add an element to a hash and update the internal counters when succeeded,
+ * otherwise report the proper error code. */
static int
type_pf_chash_add(struct ip_set *set, void *value,
gfp_t gfp_flags, u32 timeout)
int i = 0, j = 0;
u32 hash;
-#ifdef IP_SET_HASH_WITH_NETS
- if (h->elements >= h->maxelem || h->nets[d->cidr-1].nets == UINT_MAX)
-#else
if (h->elements >= h->maxelem)
-#endif
return -IPSET_ERR_HASH_FULL;
hash = JHASH2(value, h->initval, h->htable_bits);
found:
type_pf_data_copy(data, d);
#ifdef IP_SET_HASH_WITH_NETS
- if (h->nets[d->cidr-1].nets++ == 0)
- add_cidr(h->nets, HOST_MASK, d->cidr);
+ add_cidr(h, d->cidr, HOST_MASK);
#endif
h->elements++;
return 0;
}
+/* Delete an element from the hash */
static int
type_pf_chash_del(struct ip_set *set, void *value,
gfp_t gfp_flags, u32 timeout)
}
#ifdef IP_SET_HASH_WITH_NETS
+
+/* Special test function which takes into account the different network
+ * sizes added to the set */
static inline int
type_pf_chash_test_cidrs(struct ip_set *set,
struct type_pf_elem *d,
const struct type_pf_elem *data;
int i, j = 0;
u32 hash;
- u8 host_mask = set->family == AF_INET ? 32 : 128;
+ u8 host_mask = SET_HOST_MASK(set->family);
retry:
pr_debug("test by nets");
- for (; j < host_mask - 1 && h->nets[j].cidr; j++) {
+ for (; j < host_mask && h->nets[j].cidr; j++) {
type_pf_data_netmask(d, h->nets[j].cidr);
hash = JHASH2(d, h->initval, h->htable_bits);
slist_for_each(n, &h->htable[hash])
}
#endif
+/* Test whether the element is added to the set */
static inline int
type_pf_chash_test(struct ip_set *set, void *value,
gfp_t gfp_flags, u32 timeout)
const struct type_pf_elem *data;
int i;
u32 hash;
-#ifdef IP_SET_HASH_WITH_NETS
- u8 host_mask = set->family == AF_INET ? 32 : 128;
- if (d->cidr == host_mask)
+#ifdef IP_SET_HASH_WITH_NETS
+ /* If we test an IP address and not a network address,
+ * try all possible network sizes */
+ if (d->cidr == SET_HOST_MASK(set->family))
return type_pf_chash_test_cidrs(set, d, gfp_flags, timeout);
#endif
return 0;
}
+/* Reply a HEADER request: fill out the header part of the set */
static int
type_pf_head(struct ip_set *set, struct sk_buff *skb)
{
#ifdef IP_SET_HASH_WITH_NETMASK
if (h->netmask != HOST_MASK)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask);
-#endif
-#ifdef IP_SET_HASH_WITH_PROTO
- if (h->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, h->proto);
#endif
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
return -EFAULT;
}
+/* Reply a LIST/SAVE request: dump the elements of the specified set */
static int
type_pf_list(struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
/* Flavour with timeout support */
#define chash_tdata(n, i) \
-(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_telem))
+(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) \
+ + (i)*sizeof(struct type_pf_telem))
static inline u32
type_pf_data_timeout(const struct type_pf_elem *data)
prev->next = (struct slist *) tmp;
data = chash_tdata(tmp, 0);
} else {
- /* Rehashing */
+ /* Trigger rehashing */
return -EAGAIN;
}
found:
{
struct type_pf_elem *d, *data = chash_tdata(n, i);
struct slist *tmp;
- int j;
+ int j; /* Index in array */
pr_debug("del %u", i);
if (n->next != NULL) {
type_pf_data_swap_timeout(data, d);
}
#ifdef IP_SET_HASH_WITH_NETS
- if (--h->nets[data->cidr-1].nets == 0)
- del_cidr(h->nets, HOST_MASK, data->cidr);
+ del_cidr(h, data->cidr, HOST_MASK);
#endif
if (j == 0) {
prev->next = NULL;
h->elements--;
}
+/* Delete expired elements from the hashtable */
static void
type_pf_chash_expire(struct chash *h)
{
}
retry:
- ret = 0;
+ ret = i = 0;
htable_bits++;
if (!htable_bits)
/* In case we have plenty of memory :-) */
return -ENOMEM;
write_lock_bh(&set->lock);
- for (i = 0; i < jhash_size(h->htable_bits); i++) {
next_slot:
+ for (; i < jhash_size(h->htable_bits); i++) {
slist_for_each(n, &h->htable[i]) {
for (j = 0; j < h->array_size; j++) {
data = chash_tdata(n, j);
goto next_slot;
}
ret = type_pf_chash_treadd(h, t, htable_bits,
- data, gfp_flags,
- type_pf_data_timeout(data));
+ data, gfp_flags,
+ type_pf_data_timeout(data));
if (ret < 0) {
write_unlock_bh(&set->lock);
chash_destroy(t, htable_bits);
if (h->elements >= h->maxelem)
/* FIXME: when set is full, we slow down here */
type_pf_chash_expire(h);
-#ifdef IP_SET_HASH_WITH_NETS
- if (h->elements >= h->maxelem || h->nets[d->cidr-1].nets == UINT_MAX)
-#else
if (h->elements >= h->maxelem)
-#endif
return -IPSET_ERR_HASH_FULL;
hash = JHASH2(d, h->initval, h->htable_bits);
return -EAGAIN;
}
found:
- if (type_pf_data_isnull(data)) {
+ if (type_pf_data_isnull(data))
h->elements++;
#ifdef IP_SET_HASH_WITH_NETS
- } else {
- if (--h->nets[data->cidr-1].nets == 0)
- del_cidr(h->nets, HOST_MASK, data->cidr);
- }
- if (h->nets[d->cidr-1].nets++ == 0) {
- add_cidr(h->nets, HOST_MASK, d->cidr);
+ else
+ del_cidr(h, data->cidr, HOST_MASK);
+
+ add_cidr(h, d->cidr, HOST_MASK);
#endif
- }
type_pf_data_copy(data, d);
type_pf_data_timeout_set(data, timeout);
return 0;
struct slist *n;
int i, j = 0;
u32 hash;
- u8 host_mask = set->family == AF_INET ? 32 : 128;
+ u8 host_mask = SET_HOST_MASK(set->family);
retry:
- for (; j < host_mask - 1 && h->nets[j].cidr; j++) {
+ for (; j < host_mask && h->nets[j].cidr; j++) {
type_pf_data_netmask(d, h->nets[j].cidr);
hash = JHASH2(d, h->initval, h->htable_bits);
slist_for_each(n, &h->htable[hash])
struct slist *n;
int i;
u32 hash;
-#ifdef IP_SET_HASH_WITH_NETS
- u8 host_mask = set->family == AF_INET ? 32 : 128;
- if (d->cidr == host_mask)
+#ifdef IP_SET_HASH_WITH_NETS
+ if (d->cidr == SET_HOST_MASK(set->family))
return type_pf_chash_ttest_cidrs(set, d, gfp_flags,
timeout);
#endif
h->gc.function = type_pf_gc;
h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
add_timer(&h->gc);
- pr_debug("gc initialized, run in every %u", IPSET_GC_PERIOD(h->timeout));
+ pr_debug("gc initialized, run in every %u",
+ IPSET_GC_PERIOD(h->timeout));
}
#undef type_pf_data_equal
#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)
*port = src ? uh->source : uh->dest;
break;
}
- default:
- if (*proto == IPSET_IPPROTO_TCPUDP)
+ 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;
}
- if (*proto != IPSET_IPPROTO_TCPUDP)
- *proto = protocol;
+ *proto = protocol;
return true;
}
unsigned int protooff = ip_hdrlen(skb);
int protocol = iph->protocol;
- if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol))
- return false;
-
/* See comments at tcp_match in ip_tables.c */
if (ntohs(iph->frag_off) & IP_OFFSET)
return false;
if (protocol < 0 || fragoff)
return false;
- if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol))
- 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)
{
- u8 proto = IPSET_IPPROTO_TCPUDP;
-
- if (pf == AF_INET)
- return get_ip4_port(skb, src, port, &proto);
- else
- return get_ip6_port(skb, src, port, &proto);
+ 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__ */
#ifndef __IP_SET_HASH_H
#define __IP_SET_HASH_H
-/* Bitmap type specific error codes */
+/* 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,
};
#ifndef _IP_SET_KERNEL_H
#define _IP_SET_KERNEL_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__
#ifdef CONFIG_DEBUG_KERNEL
/* 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,
};
pos = pos->next)
#define slist_for_each_prev(prev, pos, head) \
- for (prev = head, pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \
+ for (prev = head, pos = (head)->next; \
+ pos && ({ prefetch(pos->next); 1; }); \
prev = pos, pos = pos->next)
#define slist_for_each_safe(pos, n, head) \
pos = pos->next)
/**
- * slist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * 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.
pos = pos->next)
/**
- * slist_for_each_entry_from - iterate over a hlist continuing from current point
+ * 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 IPSET_GC_PERIOD(timeout) \
((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1)
-/* Set is defined without timeout support */
+/* 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)
{
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 */
t = timeout * HZ + jiffies;
if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT)
+ /* Bingo! */
t++;
return t;
#else
+/* Hash specific timeout constants and macros for the entries */
+
/* Hash entry is set with no timeout value */
-#define IPSET_ELEM_UNSET 0
+#define IPSET_ELEM_PERMANENT 0
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
- return timeout == IPSET_ELEM_UNSET || time_after(timeout, jiffies);
+ return timeout == IPSET_ELEM_PERMANENT
+ || time_after(timeout, jiffies);
}
static inline bool
ip_set_timeout_expired(unsigned long timeout)
{
- return timeout != IPSET_ELEM_UNSET && time_before(timeout, jiffies);
+ return timeout != IPSET_ELEM_PERMANENT
+ && time_before(timeout, jiffies);
}
static inline unsigned long
unsigned long t;
if (!timeout)
- return IPSET_ELEM_UNSET;
+ return IPSET_ELEM_PERMANENT;
t = timeout * HZ + jiffies;
- if (t == IPSET_ELEM_UNSET)
+ if (t == IPSET_ELEM_PERMANENT)
+ /* Bingo! :-) */
t++;
return t;
static inline u32
ip_set_timeout_get(unsigned long timeout)
{
- return timeout == IPSET_ELEM_UNSET ? 0 : (timeout - jiffies)/HZ;
+ return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
}
-#endif /* IP_SET_BITMAP_TIMEOUT */
+#endif /* ! IP_SET_BITMAP_TIMEOUT */
#endif /* __KERNEL__ */
-#endif /*_IP_SET_TIMEOUT_H */
+#endif /* _IP_SET_TIMEOUT_H */
return type;
}
-/* Find a given set type by name and family together
- * with the supported minimal and maximum revisions.
+/* Find a given set type by name and family.
+ * If we succeeded, the supported minimal and maximum revisions are
+ * filled out.
*/
static bool
find_set_type_minmax(const char *name, u8 family,
read_unlock_bh(&set->lock);
if (ret == -EAGAIN) {
- /* Type requests element to be re-added */
+ /* Type requests element to be completed */
pr_debug("element must be competed, ADD is triggered");
write_lock_bh(&set->lock);
set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags);
/* List/save set data */
-#define DUMP_ALL 0L
-#define DUMP_ONE 1L
-#define DUMP_LAST 2L
+#define DUMP_INIT 0L
+#define DUMP_ALL 1L
+#define DUMP_ONE 2L
+#define DUMP_LAST 3L
static int
ip_set_dump_done(struct netlink_callback *cb)
}
}
+static inline int
+dump_init(struct netlink_callback *cb)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(cb->skb);
+ int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
+ struct nlattr *cda[IPSET_ATTR_CMD_MAX+1];
+ struct nlattr *attr = (void *)nlh + min_len;
+ ip_set_id_t index;
+
+ /* Second pass, so parser can't fail */
+ nla_parse(cda, IPSET_ATTR_CMD_MAX,
+ attr, nlh->nlmsg_len - min_len, ip_set_setname_policy);
+
+ /* cb->args[0] : dump single set/all sets
+ * [1] : set index
+ * [..]: type specific
+ */
+
+ if (!cda[IPSET_ATTR_SETNAME]) {
+ cb->args[0] = DUMP_ALL;
+ return 0;
+ }
+
+ index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME]));
+ if (index == IPSET_INVALID_ID)
+ return -EEXIST;
+
+ cb->args[0] = DUMP_ONE;
+ cb->args[1] = index;
+ return 0;
+}
+
static int
ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
{
unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0;
int ret = 0;
+ if (cb->args[0] == DUMP_INIT) {
+ ret = dump_init(cb);
+ if (ret < 0) {
+ /* We have to create and send the error message
+ * manually :-( */
+ netlink_ack(cb->skb, nlmsg_hdr(cb->skb), ret);
+ return ret;
+ }
+ }
+
if (cb->args[1] >= ip_set_max)
goto out;
NFNL_CB_CONST struct nlmsghdr *nlh,
NFNL_CB_CONST struct nlattr * NFNL_CB_CONST attr[])
{
- ip_set_id_t index;
-
if (unlikely(protocol_failed(attr)))
return -IPSET_ERR_PROTOCOL;
- if (!attr[IPSET_ATTR_SETNAME])
- return netlink_dump_start(ctnl, skb, nlh,
- ip_set_dump_start,
- ip_set_dump_done);
-
- index = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME]));
- if (index == IPSET_INVALID_ID)
- return -EEXIST;
-
- /* cb->args[0] : dump single set/all sets
- * [1] : set index
- * [..]: type specific
- */
- return netlink_dump_init(ctnl, skb, nlh,
- ip_set_dump_start,
- ip_set_dump_done,
- 2, DUMP_ONE, index);
+ return netlink_dump_start(ctnl, skb, nlh,
+ ip_set_dump_start,
+ ip_set_dump_done);
}
/* Add, del and test */
write_unlock_bh(&set->lock);
} while (ret == -EAGAIN
&& set->variant->resize
- && (ret = set->variant->resize(set, GFP_KERNEL, retried++)) == 0);
+ && (ret = set->variant->resize(set, GFP_ATOMIC,
+ retried++)) == 0);
if (!ret || (ret == -IPSET_ERR_EXIST && eexist))
return 0;
/* Try to load in the type module */
load_type_module(typename);
if (!find_set_type_minmax(typename, family, &min, &max)) {
- pr_debug("can't find: %s, family: %u", typename, family);
+ pr_debug("can't find: %s, family: %u",
+ typename, family);
return -EEXIST;
}
}
if (ip_set_max >= IPSET_INVALID_ID)
ip_set_max = IPSET_INVALID_ID - 1;
- ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL);
+ ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max,
+ GFP_KERNEL);
if (!ip_set_list) {
pr_err("ip_set: Unable to create ip_set_list");
return -ENOMEM;
static const struct nla_policy
bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
- [IPSET_ATTR_IP_TO] = { .type = NLA_U32 },
+ [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 },
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- ip = ip_set_get_h32(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ 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 attribute silently */
+ /* Set was defined without timeout support:
+ * don't ignore the attribute silently */
if (tb[IPSET_ATTR_TIMEOUT])
return -IPSET_ERR_TIMEOUT;
return bitmap_ip_test(map, ip_to_id(map, ip));
if (tb[IPSET_ATTR_IP_TO]) {
- ip_to = ip_set_get_h32(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)
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, htonl(map->first_ip));
- NLA_PUT_NET32(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
+ 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,
} else
goto nla_put_failure;
}
- NLA_PUT_NET32(skb, IPSET_ATTR_IP,
- htonl(map->first_ip + id * map->hosts));
+ NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP,
+ htonl(map->first_ip + id * map->hosts));
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, atd);
/* Timeout variant */
struct bitmap_ip_timeout {
- void *members; /* the set members */
+ 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 */
static inline bool
bitmap_ip_timeout_test(const struct bitmap_ip_timeout *map, u32 id)
{
- unsigned long *table = map->members;
-
- return ip_set_timeout_test(table[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)
{
- unsigned long *table = map->members;
-
if (bitmap_ip_timeout_test(map, id))
return -IPSET_ERR_EXIST;
- table[id] = ip_set_timeout_set(timeout);
+ map->members[id] = ip_set_timeout_set(timeout);
return 0;
}
static inline int
bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, u32 id)
{
- unsigned long *table = map->members;
int ret = -IPSET_ERR_EXIST;
if (bitmap_ip_timeout_test(map, id))
ret = 0;
- table[id] = IPSET_ELEM_UNSET;
+ map->members[id] = IPSET_ELEM_UNSET;
return ret;
}
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- ip = ip_set_get_h32(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ 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;
ip_to_id((const struct bitmap_ip *)map, ip));
if (tb[IPSET_ATTR_IP_TO]) {
- ip_to = ip_set_get_h32(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)
{
struct bitmap_ip_timeout *map = set->data;
- memset(map->members, 0, map->memsize);
+ memset(map->members, IPSET_ELEM_UNSET, map->memsize);
}
static int
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, htonl(map->first_ip));
- NLA_PUT_NET32(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
+ 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_TIMEOUT, htonl(map->timeout));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
} else
goto nla_put_failure;
}
- NLA_PUT_NET32(skb, IPSET_ATTR_IP,
- htonl(map->first_ip + id * map->hosts));
+ 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);
static const struct nla_policy
bitmap_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
- [IPSET_ATTR_IP_TO] = { .type = NLA_U32 },
+ [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 },
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;
- if (tb[IPSET_ATTR_IP])
- first_ip = ip_set_get_h32(tb[IPSET_ATTR_IP]);
- else
- 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]) {
- last_ip = ip_set_get_h32(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;
size_t dsize; /* size of element */
};
+/* ADT structure for generic function args */
struct ipmac {
u32 id; /* id in array */
unsigned char *ether; /* ethernet address */
if (!data->ether)
/* Already added without ethernet address */
return -IPSET_ERR_EXIST;
- /* Fill the MAC address and activate the timer */
+ /* Fill the MAC address */
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
break;
} else
goto nla_put_failure;
}
- NLA_PUT_NET32(skb, IPSET_ATTR_IP,
- htonl(map->first_ip + id));
+ 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);
elem->timeout = ip_set_timeout_set(timeout);
break;
case MAC_FILLED:
- if (bitmap_expired(map, data->id))
+ if (!bitmap_expired(map, data->id))
return -IPSET_ERR_EXIST;
/* Fall through */
case MAC_EMPTY:
elem->match = MAC_FILLED;
} else
elem->match = MAC_UNSET;
- /* If MAC is unset yet, we store plain timeout
+ /* 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 */
} else
goto nla_put_failure;
}
- NLA_PUT_NET32(skb, IPSET_ATTR_IP,
- htonl(map->first_ip + id));
+ 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);
static const struct nla_policy
bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
+ [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 },
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- data.id = ip_set_get_h32(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ 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;
data.id -= map->first_ip;
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, htonl(map->first_ip));
- NLA_PUT_NET32(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
+ NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip));
+ NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
static const struct nla_policy
bitmap_ipmac_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
- [IPSET_ATTR_IP_TO] = { .type = NLA_U32 },
+ [IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
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;
- if (tb[IPSET_ATTR_IP])
- first_ip = ip_set_get_h32(tb[IPSET_ATTR_IP]);
- else
- 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]) {
- last_ip = ip_set_get_h32(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;
/* Timeout variant */
struct bitmap_port_timeout {
- void *members; /* the set members */
+ 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 */
static inline bool
bitmap_port_timeout_test(const struct bitmap_port_timeout *map, u16 id)
{
- unsigned long *timeout = map->members;
-
- return ip_set_timeout_test(timeout[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)
{
- unsigned long *table = map->members;
-
if (bitmap_port_timeout_test(map, id))
return -IPSET_ERR_EXIST;
- table[id] = ip_set_timeout_set(timeout);
+ map->members[id] = ip_set_timeout_set(timeout);
return 0;
}
bitmap_port_timeout_del(const struct bitmap_port_timeout *map,
u16 id)
{
- unsigned long *table = map->members;
int ret = -IPSET_ERR_EXIST;
if (bitmap_port_timeout_test(map, id))
ret = 0;
- table[id] = IPSET_ELEM_UNSET;
+ map->members[id] = IPSET_ELEM_UNSET;
return ret;
}
static inline bool
hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data)
{
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
+ NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
return 0;
nla_put_failure:
const struct hash_ip4_telem *tdata =
(const struct hash_ip4_telem *)data;
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
+ NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
static const struct nla_policy
hash_ip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
- [IPSET_ATTR_IP_TO] = { .type = NLA_U32 },
+ [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 },
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip);
+ if (ret)
+ return ret;
ip &= NETMASK(h->netmask);
if (ip == 0)
}
if (adt == IPSET_TEST)
- return adtfn(set, &ip, GFP_KERNEL, timeout);
+ return adtfn(set, &ip, GFP_ATOMIC, timeout);
ip = ntohl(ip);
if (tb[IPSET_ATTR_IP_TO]) {
- ip_to = ip_set_get_h32(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]) {
for (; !before(ip_to, ip); ip += hosts) {
nip = htonl(ip);
- ret = adtfn(set, &nip, GFP_KERNEL, timeout);
+ ret = adtfn(set, &nip, GFP_ATOMIC, timeout);
if (ret && !ip_set_eexist(ret, flags))
return ret;
static inline bool
hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data)
{
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
+ NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
return 0;
nla_put_failure:
const struct hash_ip6_telem *e =
(const struct hash_ip6_telem *)data;
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
+ 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;
static const struct nla_policy
hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_BINARY,
- .len = sizeof(struct in6_addr) },
+ [IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
- union nf_inet_addr *ip;
+ union nf_inet_addr ip;
u32 timeout = h->timeout;
int ret;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- ip = nla_data(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ 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))
+ ip6_netmask(&ip, h->netmask);
+ if (ipv6_addr_any(&ip.in6))
return -IPSET_ERR_HASH_ELEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, ip, GFP_KERNEL, timeout);
+ ret = adtfn(set, &ip, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
- h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
- GFP_KERNEL);
+ h->htable = ip_set_alloc(
+ jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL);
if (!h->htable) {
kfree(h);
return -ENOMEM;
hash_ipport4_data_list(struct sk_buff *skb,
const struct hash_ipport4_elem *data)
{
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
+ NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
const struct hash_ipport4_telem *tdata =
(const struct hash_ipport4_telem *)data;
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
+ NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
return 1;
}
-#define IP_SET_HASH_WITH_PROTO
-
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ip_set_chash.h>
{
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipport4_elem data = { .proto = h->proto };
+ struct hash_ipport4_elem data = { };
- if (!get_ip4_port(skb, flags & IPSET_DIM_ONE_SRC,
+ if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
}
static const struct nla_policy
-hash_ipport4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
+hash_ipport_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipport4_elem data = { .proto = h->proto };
+ struct hash_ipport4_elem data = { };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
- hash_ipport4_adt_policy))
+ hash_ipport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ 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]);
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
- } else if (data.proto == IPSET_IPPROTO_ANY)
+ } else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
- case IPSET_IPPROTO_TCPUDP:
+ case IPPROTO_ICMP:
break;
default:
data.port = 0;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout
- && x->proto == y->proto
&& x->array_size == y->array_size
&& x->chain_limit == y->chain_limit;
}
hash_ipport6_data_list(struct sk_buff *skb,
const struct hash_ipport6_elem *data)
{
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
+ NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
const struct hash_ipport6_telem *e =
(const struct hash_ipport6_telem *)data;
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
+ NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ 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;
{
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipport6_elem data = { .proto = h->proto };
+ struct hash_ipport6_elem data = { };
- if (!get_ip6_port(skb, flags & IPSET_DIM_ONE_SRC,
+ if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
return adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
-static const struct nla_policy
-hash_ipport6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_BINARY,
- .len = sizeof(struct in6_addr) },
- [IPSET_ATTR_PORT] = { .type = NLA_U16 },
- [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
- [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
- [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
-};
-
static int
hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipport6_elem data = { .proto = h->proto };
+ struct hash_ipport6_elem data = { };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
- hash_ipport6_adt_policy))
+ hash_ipport_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
- sizeof(struct in6_addr));
- else
- return -IPSET_ERR_PROTOCOL;
+ 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]);
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
- } else if (data.proto == IPSET_IPPROTO_ANY)
+ } else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
- case IPSET_IPPROTO_TCPUDP:
+ case IPPROTO_ICMPV6:
break;
default:
data.port = 0;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct chash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
- u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
- if (tb[IPSET_ATTR_PROTO]) {
- proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (!proto)
- return -IPSET_ERR_INVALID_PROTO;
- }
-
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
get_random_bytes(&h->initval, sizeof(h->initval));
- h->proto = proto;
h->timeout = IPSET_NO_TIMEOUT;
- h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
- GFP_KERNEL);
+ h->htable = ip_set_alloc(
+ jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL);
if (!h->htable) {
kfree(h);
return -ENOMEM;
hash_ipportip4_data_list(struct sk_buff *skb,
const struct hash_ipportip4_elem *data)
{
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
- NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2);
+ 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);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
const struct hash_ipportip4_telem *tdata =
(const struct hash_ipportip4_telem *)data;
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
- NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2);
+ 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);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
return 1;
}
-#define IP_SET_HASH_WITH_PROTO
-
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ip_set_chash.h>
{
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportip4_elem data = { .proto = h->proto };
+ struct hash_ipportip4_elem data = { };
if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
}
static const struct nla_policy
-hash_ipportip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
- [IPSET_ATTR_IP2] = { .type = NLA_U32 },
+hash_ipportip_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportip4_elem data = { .proto = h->proto };
+ struct hash_ipportip4_elem data = { };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
- hash_ipportip4_adt_policy))
+ hash_ipportip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
+ if (ret)
+ return ret;
- if (tb[IPSET_ATTR_IP2])
- data.ip2 = ip_set_get_n32(tb[IPSET_ATTR_IP2]);
- else
- return -IPSET_ERR_PROTOCOL;
+ 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]);
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
- } else if (data.proto == IPSET_IPPROTO_ANY)
+ } else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
- case IPSET_IPPROTO_TCPUDP:
+ case IPPROTO_ICMP:
break;
default:
data.port = 0;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout
- && x->proto == y->proto
&& x->array_size == y->array_size
&& x->chain_limit == y->chain_limit;
}
hash_ipportip6_data_list(struct sk_buff *skb,
const struct hash_ipportip6_elem *data)
{
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
- NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
+ 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);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
const struct hash_ipportip6_telem *e =
(const struct hash_ipportip6_telem *)data;
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
- NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
+ 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);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ 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;
{
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportip6_elem data = { .proto = h->proto };
+ struct hash_ipportip6_elem data = { };
if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
-static const struct nla_policy
-hash_ipportip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_BINARY,
- .len = sizeof(struct in6_addr) },
- [IPSET_ATTR_IP2] = { .type = NLA_BINARY,
- .len = sizeof(struct in6_addr) },
- [IPSET_ATTR_PORT] = { .type = NLA_U16 },
- [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
- [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
- [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
-};
-
static int
hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1];
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportip6_elem data = { .proto = h->proto };
+ struct hash_ipportip6_elem data = { };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
- hash_ipportip6_adt_policy))
+ hash_ipportip_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
- sizeof(struct in6_addr));
- else
- return -IPSET_ERR_PROTOCOL;
+ ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
+ if (ret)
+ return ret;
- if (tb[IPSET_ATTR_IP2])
- memcpy(&data.ip2, nla_data(tb[IPSET_ATTR_IP2]),
- sizeof(struct in6_addr));
- else
- return -IPSET_ERR_PROTOCOL;
+ 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]);
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
- } else if (data.proto == IPSET_IPPROTO_ANY)
+ } else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
- case IPSET_IPPROTO_TCPUDP:
+ case IPPROTO_ICMPV6:
break;
default:
data.port = 0;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
[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 },
};
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct chash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
- u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
- if (tb[IPSET_ATTR_PROTO]) {
- proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (!proto)
- return -IPSET_ERR_INVALID_PROTO;
- }
-
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
get_random_bytes(&h->initval, sizeof(h->initval));
- h->proto = proto;
h->timeout = IPSET_NO_TIMEOUT;
- h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
- GFP_KERNEL);
+ h->htable = ip_set_alloc(
+ jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL);
if (!h->htable) {
kfree(h);
return -ENOMEM;
hash_ipportnet4_data_list(struct sk_buff *skb,
const struct hash_ipportnet4_elem *data)
{
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
- NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2);
+ 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);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
const struct hash_ipportnet4_telem *tdata =
(const struct hash_ipportnet4_telem *)data;
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
- NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2);
+ 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);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data =
- { .cidr = h->nets[0].cidr || HOST_MASK,
- .proto = h->proto };
+ { .cidr = h->nets[0].cidr || HOST_MASK };
if (data.cidr == 0)
return -EINVAL;
}
static const struct nla_policy
-hash_ipportnet4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
- [IPSET_ATTR_IP2] = { .type = NLA_U32 },
+hash_ipportnet_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
struct chash *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,
- .proto = h->proto };
+ struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
- hash_ipportnet4_adt_policy))
+ hash_ipportnet_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip);
+ if (ret)
+ return ret;
- if (tb[IPSET_ATTR_IP2])
- data.ip2 = ip_set_get_n32(tb[IPSET_ATTR_IP2]);
- else
- return -IPSET_ERR_PROTOCOL;
+ 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 (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
- } else if (data.proto == IPSET_IPPROTO_ANY)
+ } else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
- case IPSET_IPPROTO_TCPUDP:
+ case IPPROTO_ICMP:
break;
default:
data.port = 0;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem
&& x->timeout == y->timeout
- && x->proto == y->proto
&& x->array_size == y->array_size
&& x->chain_limit == y->chain_limit;
}
hash_ipportnet6_data_list(struct sk_buff *skb,
const struct hash_ipportnet6_elem *data)
{
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
- NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
+ 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);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
const struct hash_ipportnet6_telem *e =
(const struct hash_ipportnet6_telem *)data;
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
- NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
+ 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);
- if (data->proto != IPSET_IPPROTO_TCPUDP)
- NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ 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;
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data =
- { .cidr = h->nets[0].cidr || HOST_MASK,
- .proto = h->proto };
+ { .cidr = h->nets[0].cidr || HOST_MASK };
if (data.cidr == 0)
return -EINVAL;
return adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
-static const struct nla_policy
-hash_ipportnet6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_BINARY,
- .len = sizeof(struct in6_addr) },
- [IPSET_ATTR_IP2] = { .type = NLA_BINARY,
- .len = sizeof(struct in6_addr) },
- [IPSET_ATTR_PORT] = { .type = NLA_U16 },
- [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_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
struct chash *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,
- .proto = h->proto };
+ struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
- hash_ipportnet6_adt_policy))
+ hash_ipportnet_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
- sizeof(struct in6_addr));
- else
- return -IPSET_ERR_PROTOCOL;
+ ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip);
+ if (ret)
+ return ret;
- if (tb[IPSET_ATTR_IP2])
- memcpy(&data.ip2, nla_data(tb[IPSET_ATTR_IP2]),
- sizeof(struct in6_addr));
- else
- return -IPSET_ERR_PROTOCOL;
+ 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 (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ if (data.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
- } else if (data.proto == IPSET_IPPROTO_ANY)
+ } else
return -IPSET_ERR_MISSING_PROTO;
switch (data.proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
- case IPSET_IPPROTO_TCPUDP:
+ case IPPROTO_ICMPV6:
break;
default:
data.port = 0;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
[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 },
};
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1];
struct chash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
- u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
- if (tb[IPSET_ATTR_PROTO]) {
- proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
- if (!proto)
- return -IPSET_ERR_INVALID_PROTO;
- }
h = kzalloc(sizeof(*h)
+ sizeof(struct chash_nets)
- * (set->family == AF_INET ? 31 : 127), GFP_KERNEL);
+ * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
get_random_bytes(&h->initval, sizeof(h->initval));
- h->proto = proto;
h->timeout = IPSET_NO_TIMEOUT;
- h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
- GFP_KERNEL);
+ h->htable = ip_set_alloc(
+ jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL);
if (!h->htable) {
kfree(h);
return -ENOMEM;
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == AF_INET
- ? &hash_ipportnet4_tvariant : &hash_ipportnet6_tvariant;
+ ? &hash_ipportnet4_tvariant
+ : &hash_ipportnet6_tvariant;
if (set->family == AF_INET)
hash_ipportnet4_gc_init(set);
static inline bool
hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
{
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
+ NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
return 0;
const struct hash_net4_telem *tdata =
(const struct hash_net4_telem *)data;
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
+ 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)));
}
static const struct nla_policy
-hash_net4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_U32 },
+hash_net_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
- hash_net4_adt_policy))
+ hash_net_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
- else
- return -IPSET_ERR_PROTOCOL;
+ 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]);
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
static inline bool
hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data)
{
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
+ NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
return 0;
const struct hash_net6_telem *e =
(const struct hash_net6_telem *)data;
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
+ 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 adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
-static const struct nla_policy
-hash_net6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
- [IPSET_ATTR_IP] = { .type = NLA_BINARY,
- .len = sizeof(struct in6_addr) },
- [IPSET_ATTR_CIDR] = { .type = NLA_U8 },
- [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
- [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
-};
-
static int
hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len,
enum ipset_adt adt, u32 *lineno, u32 flags)
int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
- hash_net6_adt_policy))
+ hash_net_adt_policy))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- if (tb[IPSET_ATTR_IP])
- memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
- sizeof(struct in6_addr));
- else
- return -IPSET_ERR_PROTOCOL;
+ 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]);
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ret = adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
h = kzalloc(sizeof(*h)
+ sizeof(struct chash_nets)
- * (set->family == AF_INET ? 31 : 127), GFP_KERNEL);
+ * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
- h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
- GFP_KERNEL);
+ h->htable = ip_set_alloc(
+ jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL);
if (!h->htable) {
kfree(h);
return -ENOMEM;
--- /dev/null
+/* 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 <linux/netfilter/ip_set_kernel.h>
+#include <linux/netfilter/ip_set_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/pfxlen.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/ip_set.h>
+#include <linux/netfilter/ip_set_timeout.h>
+#include <linux/netfilter/ip_set_getport.h>
+#include <linux/netfilter/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 <linux/netfilter/ip_set_chash.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)
+{
+ struct chash *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, GFP_ATOMIC, h->timeout);
+}
+
+static const struct nla_policy
+hash_netport_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_PORT] = { .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)
+{
+ struct chash *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 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]);
+ }
+
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
+
+ return ip_set_eexist(ret, flags) ? 0 : ret;
+}
+
+static bool
+hash_netport_same_set(const struct ip_set *a, const struct ip_set *b)
+{
+ struct chash *x = a->data;
+ struct chash *y = b->data;
+
+ /* Resizing changes htable_bits, so we ignore it */
+ return x->maxelem == y->maxelem
+ && x->timeout == y->timeout
+ && x->array_size == y->array_size
+ && x->chain_limit == y->chain_limit;
+}
+
+/* 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 <linux/netfilter/ip_set_chash.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)
+{
+ struct chash *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, GFP_ATOMIC, h->timeout);
+}
+
+static int
+hash_netport6_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
+{
+ struct chash *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 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]);
+ }
+
+ ret = adtfn(set, &data, GFP_ATOMIC, timeout);
+
+ return ip_set_eexist(ret, flags) ? 0 : ret;
+}
+
+/* Create hash:ip type of sets */
+
+static const struct nla_policy
+hash_netport_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
+ [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 chash *h;
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+
+ 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 chash_nets)
+ * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
+ if (!h)
+ return -ENOMEM;
+
+ h->maxelem = maxelem;
+ h->htable_bits = htable_bits(hashsize);
+ h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
+ h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
+ get_random_bytes(&h->initval, sizeof(h->initval));
+ h->timeout = IPSET_NO_TIMEOUT;
+
+ h->htable = ip_set_alloc(
+ jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL);
+ if (!h->htable) {
+ kfree(h);
+ return -ENOMEM;
+ }
+
+ 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->htable_bits),
+ h->htable_bits, h->maxelem, set->data, h->htable);
+
+ return 0;
+}
+
+static struct ip_set_type hash_netport_type = {
+ .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);
}
if (tb[IPSET_ATTR_NAMEREF]) {
- refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), &s);
+ refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
+ &s);
if (refid == IPSET_INVALID_ID) {
ret = -IPSET_ERR_NAMEREF;
goto finish;
continue;
else if (elem->id == id
&& (before == 0
- || (before > 0 && next_id_eq(map, i, refid))))
+ || (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))
include $(top_srcdir)/Make_global.am
-AM_CFLAGS += -fPIC
-LIBS =
+AM_CFLAGS += ${libmnl_CFLAGS}
lib_LTLIBRARIES = libipset.la
libipset_la_LDFLAGS = -version-info $(LIBVERSION)
+libipset_la_LIBADD = ${libmnl_LIBS}
libipset_la_SOURCES = \
data.c \
+ icmp.c \
+ icmpv6.c \
mnl.c \
parse.c \
print.c \
uint64_t ignored;
/* Setname */
char setname[IPSET_MAXNAMELEN];
+ /* Set type */
const struct ipset_type *type;
/* Common CADT options */
uint8_t cidr;
/**
* ipset_data_ignored - test and set ignored bits in the data blob
* @data: data blob
- * @flags: the option flags which is ignored
+ * @flags: the option flag to be ignored
*
* Returns true if the option was not already ignored.
*/
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:
break;
/* Create-specific options, type */
case IPSET_OPT_TYPENAME:
- ipset_strncpy(data->u.create.typename, value, IPSET_MAXNAMELEN);
+ ipset_strncpy(data->u.create.typename, value,
+ IPSET_MAXNAMELEN);
break;
case IPSET_OPT_REVISION:
data->u.create.revision = *(const uint8_t *) value;
assert(data);
assert(opt != IPSET_OPT_NONE);
- if (opt != IPSET_OPT_TYPENAME && !ipset_data_test(data, opt))
+ if (!(opt == IPSET_OPT_TYPENAME || ipset_data_test(data, opt)))
return NULL;
switch (opt) {
}
/**
- * ipset_data_sizeof - calculates the size for the type of data
+ * 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 option kind.
+ * Returns the size required to store the given data type.
*/
size_t
ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
* @data: data blob
*
* Return the value of IPSET_OPT_CIDR stored in the data blob.
- * If it is not set, the the returned value corresponds to
+ * If it is not set, then the returned value corresponds to
* the default one according to the family type or zero.
*/
uint8_t
--- /dev/null
+/* 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 (STRNEQ(icmp_typecodes[i].name, str, strlen(str))) {
+ *typecode = (icmp_typecodes[i].type << 8) | icmp_typecodes[i].code;
+ return 0;
+ }
+
+ return -1;
+}
--- /dev/null
+/* 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 (STRNEQ(icmpv6_typecodes[i].name, str, strlen(str))) {
+ *typecode = (icmpv6_typecodes[i].type << 8) | icmpv6_typecodes[i].code;
+ return 0;
+ }
+
+ return -1;
+}
#define NFNL_SUBSYS_IPSET 6
#endif
+/* Internal data structure for the kernel-userspace communication parameters */
struct ipset_handle {
- struct mnl_socket *h;
- unsigned int seq;
- unsigned int portid;
- mnl_cb_t *cb_ctl;
- void *data;
+ 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 */
};
/* Netlink flags of the commands */
[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)
{
#include <sys/types.h> /* getaddrinfo */
#include <sys/socket.h> /* getaddrinfo, AF_ */
#include <net/ethernet.h> /* ETH_ALEN */
+#include <netinet/in.h> /* IPPROTO_ */
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* IPSET_OPT_* */
+#include <libipset/icmp.h> /* name_to_icmp */
+#include <libipset/icmpv6.h> /* name_to_icmpv6 */
#include <libipset/pfxlen.h> /* prefixlen_netmask_map */
#include <libipset/session.h> /* ipset_err */
#include <libipset/types.h> /* ipset_type_get */
* Parse TCP service names or port numbers
*/
static int
-parse_portname(struct ipset_session *session, const char *str, uint16_t *port)
+parse_portname(struct ipset_session *session, const char *str,
+ uint16_t *port, const char *proto)
{
struct servent *service;
- if ((service = getservbyname(str, "tcp")) != NULL) {
+ if ((service = getservbyname(str, proto)) != NULL) {
*port = ntohs((uint16_t) service->s_port);
return 0;
}
- return syntax_err("cannot parse '%s' as a (TCP) port", str);
+ return syntax_err("cannot parse '%s' as a %s port", str, proto);
}
/**
- * ipset_parse_single_port - parse a single (TCP) port number or name
+ * ipset_parse_single_port - parse a single port number or name
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
+ * @proto: protocol
*
- * Parse string as a single (TCP) port number or name. The parsed port
+ * Parse string as a single port number or name. The parsed port
* number is stored in the data blob of the session.
*
* Returns 0 on success or a negative error code.
*/
int
-ipset_parse_single_port(struct ipset_session *session,
- enum ipset_opt opt, const char *str)
+ipset_parse_port(struct ipset_session *session,
+ enum ipset_opt opt, const char *str,
+ const char *proto)
{
uint16_t port;
int err;
assert(str);
if ((err = string_to_u16(session, str, &port)) == 0
- || (err = parse_portname(session, str, &port)) == 0)
+ || (err = parse_portname(session, str, &port, proto)) == 0)
err = ipset_session_data_set(session, opt, &port);
if (!err)
- /* No error, so reset session messages! */
+ /* No error, so reset false error messages! */
ipset_session_report_reset(session);
return err;
}
/**
- * ipset_parse_port - parse (TCP) port name, number, or range of them
+ * ipset_parse_tcp_port - parse TCP port name, number, or range of them
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
*
- * Parse string as a TCP port name or number or range of them.
+ * Parse string as a TCP port name or number or range of them
* separated by a dash. The parsed port numbers are stored
* in the data blob of the session.
*
* Returns 0 on success or a negative error code.
*/
int
-ipset_parse_port(struct ipset_session *session,
- enum ipset_opt opt, const char *str)
+ipset_parse_tcp_port(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
{
char *a, *saved, *tmp;
int err = 0;
if (a != NULL) {
/* port-port */
*a++ = '\0';
- err = ipset_parse_single_port(session, IPSET_OPT_PORT_TO, a);
+ err = ipset_parse_port(session, IPSET_OPT_PORT_TO, a, "TCP");
if (err)
goto error;
}
- err = ipset_parse_single_port(session, opt, tmp);
+ err = ipset_parse_port(session, opt, tmp, "TCP");
error:
free(saved);
return err;
}
+/**
+ * ipset_parse_single_tcp_port - parse TCP port name or number
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a single TCP port name or number.
+ * The parsed port number is stored
+ * in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_single_tcp_port(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ assert(session);
+ assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
+ assert(str);
+
+ return ipset_parse_port(session, opt, str, "TCP");
+}
+
/**
* ipset_parse_proto - parse protocol name
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
*
- * Parse string as a protocol name. "any" is supported
- * as a special protocol name for ipset itself.
- * The parsed protocol are stored in the data blob of the session.
+ * Parse string as a protocol name.
+ * The parsed protocol is stored in the data blob of the session.
*
* Returns 0 on success or a negative error code.
*/
ipset_parse_proto(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
+ struct protoent *protoent;
uint8_t proto = 0;
assert(session);
assert(opt == IPSET_OPT_PROTO);
assert(str);
- if (STREQ(str, "any"))
- proto = IPSET_IPPROTO_ANY;
- else {
- struct protoent *protoent = getprotobyname(str);
- if (protoent == NULL)
- return syntax_err("cannot parse '%s' as a protocol name", str);
- proto = protoent->p_proto;
- }
- if (!proto || proto == IPSET_IPPROTO_TCPUDP)
- return syntax_err("invalid protocol '%s'", str);
+ protoent = getprotobyname(strcasecmp(str, "icmpv6") == 0
+ ? "ipv6-icmp" : str);
+ if (protoent == NULL)
+ return syntax_err("cannot parse '%s' "
+ "as a protocol name", str);
+ proto = protoent->p_proto;
+ if (!proto)
+ return syntax_err("Unsupported protocol '%s'", str);
return ipset_session_data_set(session, opt, &proto);
}
+/* Parse ICMP and ICMPv6 type/code */
+static int
+parse_icmp_typecode(struct ipset_session *session,
+ enum ipset_opt opt, const char *str,
+ const char *family)
+{
+ uint16_t typecode;
+ uint8_t type, code;
+ char *a, *saved, *tmp;
+ int err;
+
+ saved = tmp = strdup(str);
+ if (tmp == NULL)
+ return ipset_err(session,
+ "Cannot allocate memory to duplicate %s.",
+ str);
+ a = cidr_separator(tmp);
+ if (a == NULL) {
+ free(saved);
+ return ipset_err(session,
+ "Cannot parse %s as an %s type/code.", str, family);
+ }
+ *a++ = '\0';
+ if ((err = string_to_u8(session, a, &type)) != 0
+ || (err = string_to_u8(session, tmp, &code)) != 0)
+ goto error;
+
+ typecode = (type << 8) | code;
+ err = ipset_session_data_set(session, opt, &typecode);
+
+error:
+ free(saved);
+ return err;
+}
+
+/**
+ * ipset_parse_icmp - parse an ICMP name or type/code
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an ICMP name or type/code numbers.
+ * The parsed ICMP type/code is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_icmp(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ uint16_t typecode;
+
+ assert(session);
+ assert(opt == IPSET_OPT_PORT);
+ assert(str);
+
+ if (name_to_icmp(str, &typecode) < 0)
+ return parse_icmp_typecode(session, opt, str, "ICMP");
+
+ return ipset_session_data_set(session, opt, &typecode);
+}
+
+/**
+ * ipset_parse_icmpv6 - parse an ICMPv6 name or type/code
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an ICMPv6 name or type/code numbers.
+ * The parsed ICMPv6 type/code is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_icmpv6(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ uint16_t typecode;
+
+ assert(session);
+ assert(opt == IPSET_OPT_PORT);
+ assert(str);
+
+ if (name_to_icmpv6(str, &typecode) < 0)
+ return parse_icmp_typecode(session, opt, str, "ICMPv6");
+
+ return ipset_session_data_set(session, opt, &typecode);
+}
+
/**
* ipset_parse_proto_port - parse (optional) protocol and a single port
* @session: session structure
ipset_parse_proto_port(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
+ struct ipset_data *data;
char *a, *saved, *tmp;
+ const char *proto;
+ uint8_t p = IPPROTO_TCP;
int err = 0;
assert(session);
assert(opt == IPSET_OPT_PORT);
assert(str);
+ data = ipset_session_data(session);
saved = tmp = strdup(str);
if (tmp == NULL)
return ipset_err(session,
a = proto_separator(tmp);
if (a != NULL) {
+ uint8_t family = ipset_data_family(data);
+
/* proto:port */
*a++ = '\0';
err = ipset_parse_proto(session, IPSET_OPT_PROTO, tmp);
if (err)
goto error;
- tmp = a;
+
+ p = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
+ switch (p) {
+ case IPPROTO_TCP:
+ proto = tmp;
+ tmp = a;
+ goto parse_port;
+ case IPPROTO_UDP:
+ proto = tmp;
+ tmp = a;
+ goto parse_port;
+ case IPPROTO_ICMP:
+ if (family != AF_INET) {
+ syntax_err("Protocol ICMP can be used with family INET only");
+ goto error;
+ }
+ err = ipset_parse_icmp(session, opt, a);
+ break;
+ case IPPROTO_ICMPV6:
+ if (family != AF_INET6) {
+ syntax_err("Protocol ICMPv6 can be used with family INET6 only");
+ goto error;
+ }
+ err = ipset_parse_icmpv6(session, opt, a);
+ break;
+ default:
+ if (!STREQ(a, "0")) {
+ syntax_err("Protocol %s can be used with pseudo port value 0 only.");
+ goto error;
+ }
+ ipset_data_flags_set(data, IPSET_FLAG(opt));
+ }
+ goto error;
+ } else {
+ proto = "TCP";
+ err = ipset_data_set(data, IPSET_OPT_PROTO, &p);
+ if (err)
+ goto error;
}
- err = ipset_parse_single_port(session, opt, tmp);
+parse_port:
+ err = ipset_parse_port(session, opt, tmp, proto);
error:
free(saved);
data = ipset_session_data(session);
if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FAMILY)))
- syntax_err("protocol family may not be specified multiple times");
+ syntax_err("protocol family may not be specified "
+ "multiple times");
if (STREQ(str, "inet") || STREQ(str, "ipv4") || STREQ(str, "-4"))
family = AF_INET;
}
/*
- * Parse IPv4/IPv6 addresses, networks and ranges
+ * Parse IPv4/IPv6 addresses, networks and ranges.
* We resolve hostnames but just the first IP address is used.
*/
static struct addrinfo *
-get_addrinfo(struct ipset_session *session, const char *str, uint8_t family)
+call_getaddrinfo(struct ipset_session *session, const char *str,
+ uint8_t family)
{
struct addrinfo hints;
struct addrinfo *res;
return res;
}
-#define GET_ADDRINFO(family, IP, f, n) \
-static int \
-get_addrinfo##f(struct ipset_session *session, \
- const char *str, \
- struct addrinfo **info, \
- struct in##n##_addr **inaddr) \
-{ \
- struct addrinfo *i; \
- struct sockaddr_in##n saddr; \
- int found; \
- \
- if ((*info = get_addrinfo(session, str, family)) == NULL) { \
- syntax_err("cannot parse %s: " IP " resolving failed", \
- str); \
- return EINVAL; \
- } \
- \
- for (i = *info, found = 0; i != NULL; i = i->ai_next) { \
- if (i->ai_family != family \
- || i->ai_addrlen != sizeof(saddr)) \
- continue; \
- if (found == 0) { \
- /* Workaround: can't cast on Sparc */ \
- memcpy(&saddr, i->ai_addr, sizeof(saddr)); \
- *inaddr = &saddr.sin##n##_addr; \
- } else if (found == 1) { \
- ipset_warn(session, \
- "%s resolves to multiple addresses: " \
- "using only the first one returned by the resolver", \
- str); \
- } \
- found++; \
- } \
- if (found == 0) \
- return syntax_err("cannot parse %s: " \
- IP "address could not be resolved", \
- str); \
- return 0; \
+static int
+get_addrinfo(struct ipset_session *session,
+ enum ipset_opt opt,
+ const char *str,
+ struct addrinfo **info,
+ uint8_t family)
+{
+ struct addrinfo *i;
+ size_t addrlen = family == AF_INET ? sizeof(struct sockaddr_in)
+ : sizeof(struct sockaddr_in6);
+ int found, err = 0;
+
+ if ((*info = call_getaddrinfo(session, str, family)) == NULL) {
+ syntax_err("cannot parse %s: resolving to %s address failed",
+ str, family == AF_INET ? "IPv4" : "IPv6");
+ return EINVAL;
+ }
+
+ for (i = *info, found = 0; i != NULL; i = i->ai_next) {
+ if (i->ai_family != family || i->ai_addrlen != addrlen)
+ continue;
+ if (found == 0) {
+ if (family == AF_INET) {
+ err = ipset_session_data_set(session, opt,
+ &((const struct sockaddr_in *)i->ai_addr)->sin_addr);
+ } else {
+ err = ipset_session_data_set(session, opt,
+ &((const struct sockaddr_in6 *)i->ai_addr)->sin6_addr);
+ }
+ } else if (found == 1) {
+ ipset_warn(session,
+ "%s resolves to multiple addresses: "
+ "using only the first one returned "
+ "by the resolver",
+ str);
+ }
+ found++;
+ }
+ if (found == 0)
+ return syntax_err("cannot parse %s: "
+ "%s address could not be resolved",
+ str, family == AF_INET ? "IPv4" : "IPv6");
+ return err;
}
-#define PARSE_IP(mask, f, n) \
-static int \
-parse_ipv##f(struct ipset_session *session, \
- enum ipset_opt opt, const char *str) \
-{ \
- uint8_t m = mask; \
- int aerr = EINVAL, err = 0, range = 0; \
- char *saved = strdup(str); \
- char *a, *tmp = saved; \
- struct addrinfo *info; \
- struct in##n##_addr *inaddr; \
- struct ipset_data *data = ipset_session_data(session); \
- enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR \
- : IPSET_OPT_CIDR2; \
- \
- if (tmp == NULL) \
- return ipset_err(session, \
- "Cannot allocate memory to duplicate %s.",\
- str); \
- if ((a = cidr_separator(tmp)) != NULL) { \
- /* IP/mask */ \
- *a++ = '\0'; \
- \
- if ((err = string_to_cidr(session, a, 0, m, &m)) != 0 \
- || (err = ipset_data_set(data, copt, &m)) != 0) \
- goto out; \
- } else if ((a = range_separator(tmp)) != NULL) { \
- /* IP-IP */ \
- *a++ = '\0'; \
- D("range %s", a); \
- range++; \
- } \
- if ((aerr = get_addrinfo##f(session, tmp, &info, &inaddr)) != 0 \
- || (err = ipset_data_set(data, opt, inaddr)) != 0 \
- || !range) \
- goto out; \
- freeaddrinfo(info); \
- if ((aerr = get_addrinfo##f(session, a, &info, &inaddr)) == 0) \
- err = ipset_data_set(data, IPSET_OPT_IP_TO, inaddr); \
- \
-out: \
- if (aerr != EINVAL) \
- /* getaddrinfo not failed */ \
- freeaddrinfo(info); \
- else if (aerr) \
- err = -1; \
- free(saved); \
- return err; \
-}
+static int
+parse_ipaddr(struct ipset_session *session,
+ enum ipset_opt opt, const char *str,
+ uint8_t family)
+{
+ uint8_t m = family == AF_INET ? 32 : 128;
+ int aerr = EINVAL, err = 0, range = 0;
+ char *saved = strdup(str);
+ char *a, *tmp = saved;
+ struct addrinfo *info;
+ enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR
+ : IPSET_OPT_CIDR2;
-GET_ADDRINFO(AF_INET, "IPv4", 4, )
-PARSE_IP(32, 4, )
+ if (tmp == NULL)
+ return ipset_err(session,
+ "Cannot allocate memory to duplicate %s.",
+ str);
+ if ((a = cidr_separator(tmp)) != NULL) {
+ /* IP/mask */
+ *a++ = '\0';
-GET_ADDRINFO(AF_INET6, "IPv6", 6, 6)
-PARSE_IP(128, 6, 6)
+ if ((err = string_to_cidr(session, a, 0, m, &m)) != 0
+ || (err = ipset_session_data_set(session, copt, &m)) != 0)
+ goto out;
+ } else if ((a = range_separator(tmp)) != NULL) {
+ /* IP-IP */
+ *a++ = '\0';
+ D("range %s", a);
+ range++;
+ }
+ if ((aerr = get_addrinfo(session, opt, tmp, &info, family)) != 0
+ || !range)
+ goto out;
+ freeaddrinfo(info);
+ aerr = get_addrinfo(session, IPSET_OPT_IP_TO, a, &info, family);
+
+out:
+ if (aerr != EINVAL)
+ /* getaddrinfo not failed */
+ freeaddrinfo(info);
+ else if (aerr)
+ err = -1;
+ free(saved);
+ return err;
+}
enum ipaddr_type {
IPADDR_ANY,
parse_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str, enum ipaddr_type addrtype)
{
- int err = 0;
struct ipset_data *data = ipset_session_data(session);
uint8_t family = ipset_data_family(data);
break;
}
- if (family == AF_INET)
- err = parse_ipv4(session, opt, str);
- else
- err = parse_ipv6(session, opt, str);
-
- return err;
+ return parse_ipaddr(session, opt, str, family);
}
/**
#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_ */
proto = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
assert(proto);
- if (proto == IPSET_IPPROTO_ANY)
- return snprintf(buf, len, "any");
protoent = getprotobynumber(proto);
if (protoent)
return snprintf(buf, len, "%s", protoent->p_name);
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
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;
- strcat(buf, ":");
- SNPRINTF_FAILURE(1, len, offset);
+ 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;
+ }
}
- return ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
+ size = ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
+ SNPRINTF_FAILURE(size, len, offset);
+
+ return offset;
}
#define print_second(data) \
const struct ipset_attr_policy create_attrs[] = {
[IPSET_ATTR_IP] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP,
},
[IPSET_ATTR_IP_TO] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP_TO,
},
[IPSET_ATTR_CIDR] = {
const struct ipset_attr_policy adt_attrs[] = {
[IPSET_ATTR_IP] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP,
},
[IPSET_ATTR_IP_TO] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP_TO,
},
[IPSET_ATTR_CIDR] = {
.len = IPSET_MAXNAMELEN,
},
[IPSET_ATTR_IP2] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP2,
},
[IPSET_ATTR_CIDR2] = {
},
};
+const struct ipset_attr_policy ipaddr_attrs[] = {
+ [IPSET_ATTR_IPADDR_IPV4] = {
+ .type = MNL_TYPE_U32,
+ },
+ [IPSET_ATTR_IPADDR_IPV6] = {
+ .type = MNL_TYPE_BINARY,
+ .len = sizeof(union nf_inet_addr),
+ },
+};
+
+static int
+generic_data_attr_cb(const struct nlattr *attr, void *data,
+ int attr_max, const struct ipset_attr_policy *policy)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ D("attr type: %u, len %u", type, attr->nla_len);
+ if (mnl_attr_type_valid(attr, attr_max) < 0) {
+ D("attr type: %u INVALID", type);
+ return MNL_CB_ERROR;
+ }
+ if (mnl_attr_validate(attr, policy[type].type) < 0) {
+ D("attr type: %u POLICY, attrlen %u", type,
+ mnl_attr_get_payload_len(attr));
+ return MNL_CB_ERROR;
+ }
+ if (policy[type].type == MNL_TYPE_NUL_STRING
+ && mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN)
+ return MNL_CB_ERROR;
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+create_attr_cb(const struct nlattr *attr, void *data)
+{
+ return generic_data_attr_cb(attr, data,
+ IPSET_ATTR_CREATE_MAX, create_attrs);
+}
+
+static int
+adt_attr_cb(const struct nlattr *attr, void *data)
+{
+ return generic_data_attr_cb(attr, data,
+ IPSET_ATTR_ADT_MAX, adt_attrs);
+}
+
+static int
+ipaddr_attr_cb(const struct nlattr *attr, void *data)
+{
+ return generic_data_attr_cb(attr, data,
+ IPSET_ATTR_IPADDR_MAX, ipaddr_attrs);
+}
+
#define FAILURE(format, args...) \
{ ipset_err(session, format , ## args); return MNL_CB_ERROR; }
attr = &attrs[type];
d = mnl_attr_get_payload(nla[type]);
- if (attr->type == MNL_TYPE_BINARY && !attr->len) {
+ if (attr->type == MNL_TYPE_NESTED && attr->opt) {
+ /* IP addresses */
+ struct nlattr *ipattr[IPSET_ATTR_IPADDR_MAX+1] = {};
uint8_t family = ipset_data_family(data);
+ int atype;
+ D("attr type %u", type);
+ if (mnl_attr_parse_nested(nla[type],
+ ipaddr_attr_cb, ipattr) < 0)
+ FAILURE("Broken kernel message, cannot validate "
+ "IP address attribute!");
/* Validate by hand */
switch (family) {
case AF_INET:
- if (nla[type]->nla_len < sizeof(uint32_t))
+ atype = IPSET_ATTR_IPADDR_IPV4;
+ if (!ipattr[atype])
+ FAILURE("Broken kernel message: IPv4 address "
+ "expected but not received!");
+ if (ipattr[atype]->nla_len < sizeof(uint32_t))
FAILURE("Broken kernel message: "
- "cannot validate IPv4 address attribute!");
+ "cannot validate IPv4 "
+ "address attribute!");
break;
case AF_INET6:
- if (nla[type]->nla_len < sizeof(struct in6_addr))
+ atype = IPSET_ATTR_IPADDR_IPV6;
+ if (!ipattr[atype])
+ FAILURE("Broken kernel message: IPv6 address "
+ "expected but not received!");
+ if (ipattr[atype]->nla_len < sizeof(struct in6_addr))
FAILURE("Broken kernel message: "
- "cannot validate IPv6 address attribute!");
+ "cannot validate IPv6 "
+ "address attribute!");
break;
default:
FAILURE("Broken kernel message: "
"IP address attribute but "
"family is unspecified!");
}
+ d = mnl_attr_get_payload(ipattr[atype]);
} else if (nla[type]->nla_type & NLA_F_NET_BYTEORDER) {
switch (attr->type) {
case MNL_TYPE_U32: {
for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->opt; arg++) {
if (!arg->print
|| !ipset_data_test(data, arg->opt)
- || (arg->opt == IPSET_OPT_FAMILY && family == type->family))
+ || (arg->opt == IPSET_OPT_FAMILY
+ && family == type->family))
continue;
switch (session->mode) {
case IPSET_LIST_SAVE:
return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
}
-static int
-generic_data_attr_cb(const struct nlattr *attr, void *data,
- int attr_max, const struct ipset_attr_policy *policy)
-{
- const struct nlattr **tb = (const struct nlattr **)data;
- int type = mnl_attr_get_type(attr);
-
- D("attr type: %u, len %u", type, attr->nla_len);
- if (mnl_attr_type_valid(attr, attr_max) < 0) {
- D("attr type: %u INVALID", type);
- return MNL_CB_ERROR;
- }
- if (mnl_attr_validate(attr, policy[type].type) < 0) {
- D("attr type: %u POLICY, attrlen %u", type,
- mnl_attr_get_payload_len(attr));
- return MNL_CB_ERROR;
- }
- if (policy[type].type == MNL_TYPE_NUL_STRING
- && mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN)
- return MNL_CB_ERROR;
- tb[type] = attr;
- return MNL_CB_OK;
-}
-
-static int
-create_attr_cb(const struct nlattr *attr, void *data)
-{
- return generic_data_attr_cb(attr, data,
- IPSET_ATTR_CREATE_MAX, create_attrs);
-}
-
-static int
-adt_attr_cb(const struct nlattr *attr, void *data)
-{
- return generic_data_attr_cb(attr, data,
- IPSET_ATTR_ADT_MAX, adt_attrs);
-}
-
static int
callback_list(struct ipset_session *session, struct nlattr *nla[],
enum ipset_cmd cmd)
attr_len(const struct ipset_attr_policy *attr, uint8_t family, uint16_t *flags)
{
switch (attr->type) {
- case MNL_TYPE_BINARY:
+ case MNL_TYPE_NESTED:
if (attr->len)
return attr->len;
alen = attr_len(attr, family, &flags);
switch (attr->type) {
+ case MNL_TYPE_NESTED: {
+ /* IP addresses */
+ struct nlattr *nested = mnl_attr_nest_start(nlh, type);
+ int atype = family == AF_INET ? IPSET_ATTR_IPADDR_IPV4
+ : IPSET_ATTR_IPADDR_IPV6;
+
+ D("family: %s", family == AF_INET ? "INET" :
+ family == AF_INET6 ? "INET6" : "UNSPEC");
+ mnl_attr_put(nlh, atype | flags, alen, d);
+ mnl_attr_nest_end(nlh, nested);
+
+ return 0;
+ }
case MNL_TYPE_U32: {
uint32_t value = htonl(*(const uint32_t *)d);
ADDATTR(nlh, data, IPSET_ATTR_TYPENAME, AF_INET, cmd_attrs);
ADDATTR_RAW(nlh, &type->revision,
IPSET_ATTR_REVISION, cmd_attrs);
- D("family: %u", ipset_data_family(data));
+ D("family: %u, type family %u",
+ ipset_data_family(data), type->family);
ADDATTR(nlh, data, IPSET_ATTR_FAMILY, AF_INET, cmd_attrs);
/* Type-specific create attributes */
}
}
type = ipset_data_get(data, IPSET_OPT_TYPE);
+ D("family: %u, type family %u",
+ ipset_data_family(data), type->family);
open_nested(session, nlh, IPSET_ATTR_DATA);
- addattr_adt(nlh, data, type->family);
+ addattr_adt(nlh, data, ipset_data_family(data));
ADDATTR_RAW(nlh, &session->lineno,
IPSET_ATTR_LINENO, cmd_attrs);
close_nested(session, nlh);
"Invalid test command: missing settype");
type = ipset_data_get(data, IPSET_OPT_TYPE);
+ D("family: %u, type family %u",
+ ipset_data_family(data), type->family);
ADDATTR_SETNAME(nlh, data);
open_nested(session, nlh, IPSET_ATTR_DATA);
- addattr_adt(nlh, data, type->family);
+ addattr_adt(nlh, data, ipset_data_family(data));
close_nested(session, nlh);
break;
}
#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_* */
continue;
if (!(IPSET_FLAG(opt) & type->full[IPSET_ADD]))
continue;
- max += ipset_data_sizeof(opt, family);
+ max += MNL_ALIGN(ipset_data_sizeof(opt, family))
+ + MNL_ATTR_HDRLEN;
+ switch (opt) {
+ case IPSET_OPT_IP:
+ case IPSET_OPT_IP_TO:
+ case IPSET_OPT_IP2:
+ /* Nested attributes */
+ max += MNL_ATTR_HDRLEN;
+ break;
+ default:
+ break;
+ }
}
type->maxsize[sizeid] = max;
}
/**
* ipset_type_add - add (register) a userspace set type
- * @type: set type structure
+ * @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.
#ifdef __KERNEL__
-diff --git a/include/linux/netlink.h b/include/linux/netlink.h
-index ab5d312..ef8b229 100644
---- a/include/linux/netlink.h
-+++ b/include/linux/netlink.h
-@@ -263,11 +263,14 @@ __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags)
- #define NLMSG_PUT(skb, pid, seq, type, len) \
- NLMSG_NEW(skb, pid, seq, type, len, 0)
-
--extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
-- const struct nlmsghdr *nlh,
-- int (*dump)(struct sk_buff *skb, struct netlink_callback*),
-- int (*done)(struct netlink_callback*));
--
-+extern int netlink_dump_init(struct sock *ssk, struct sk_buff *skb,
-+ const struct nlmsghdr *nlh,
-+ int (*dump)(struct sk_buff *skb, struct netlink_callback*),
-+ int (*done)(struct netlink_callback*),
-+ unsigned char init, ...);
-+
-+#define netlink_dump_start(ssk, skb, nlh, dump, done) \
-+ netlink_dump_init(ssk, skb, nlh, dump, done, 0)
-
- #define NL_NONROOT_RECV 0x1
- #define NL_NONROOT_SEND 0x2
-diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
-index 19e9800..7d85d45 100644
---- a/net/netlink/af_netlink.c
-+++ b/net/netlink/af_netlink.c
-@@ -1714,15 +1714,18 @@ errout:
- return err;
- }
-
--int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
-- const struct nlmsghdr *nlh,
-- int (*dump)(struct sk_buff *skb,
-- struct netlink_callback *),
-- int (*done)(struct netlink_callback *))
-+int netlink_dump_init(struct sock *ssk, struct sk_buff *skb,
-+ const struct nlmsghdr *nlh,
-+ int (*dump)(struct sk_buff *skb,
-+ struct netlink_callback *),
-+ int (*done)(struct netlink_callback *),
-+ unsigned char init, ...)
- {
- struct netlink_callback *cb;
- struct sock *sk;
- struct netlink_sock *nlk;
-+ va_list args;
-+ unsigned char i;
-
- cb = kzalloc(sizeof(*cb), GFP_KERNEL);
- if (cb == NULL)
-@@ -1733,6 +1736,10 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
- cb->nlh = nlh;
- atomic_inc(&skb->users);
- cb->skb = skb;
-+ va_start(args, init);
-+ for (i = 0; i < init; i++)
-+ cb->args[i] = va_arg(args, long);
-+ va_end(args);
-
- sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).pid);
- if (sk == NULL) {
-@@ -1759,7 +1766,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
- */
- return -EINTR;
- }
--EXPORT_SYMBOL(netlink_dump_start);
-+EXPORT_SYMBOL(netlink_dump_init);
-
- void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
- {
+++ /dev/null
-diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
-index 9f00da2..9f51ff6 100644
---- a/include/linux/netfilter/nfnetlink.h
-+++ b/include/linux/netfilter/nfnetlink.h
-@@ -47,7 +47,8 @@ struct nfgenmsg {
- #define NFNL_SUBSYS_QUEUE 3
- #define NFNL_SUBSYS_ULOG 4
- #define NFNL_SUBSYS_OSF 5
--#define NFNL_SUBSYS_COUNT 6
-+#define NFNL_SUBSYS_IPSET 6
-+#define NFNL_SUBSYS_COUNT 7
-
- #ifdef __KERNEL__
-
-diff --git a/include/linux/netlink.h b/include/linux/netlink.h
-index ab5d312..ef8b229 100644
---- a/include/linux/netlink.h
-+++ b/include/linux/netlink.h
-@@ -263,11 +263,14 @@ __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags)
- #define NLMSG_PUT(skb, pid, seq, type, len) \
- NLMSG_NEW(skb, pid, seq, type, len, 0)
-
--extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
-- struct nlmsghdr *nlh,
-- int (*dump)(struct sk_buff *skb, struct netlink_callback*),
-- int (*done)(struct netlink_callback*));
--
-+extern int netlink_dump_init(struct sock *ssk, struct sk_buff *skb,
-+ struct nlmsghdr *nlh,
-+ int (*dump)(struct sk_buff *skb, struct netlink_callback*),
-+ int (*done)(struct netlink_callback*),
-+ unsigned char init, ...);
-+
-+#define netlink_dump_start(ssk, skb, nlh, dump, done) \
-+ netlink_dump_init(ssk, skb, nlh, dump, done, 0)
-
- #define NL_NONROOT_RECV 0x1
- #define NL_NONROOT_SEND 0x2
-diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
-index 19e9800..7d85d45 100644
---- a/net/netlink/af_netlink.c
-+++ b/net/netlink/af_netlink.c
-@@ -1714,15 +1714,18 @@ errout:
- return err;
- }
-
--int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
-- struct nlmsghdr *nlh,
-- int (*dump)(struct sk_buff *skb,
-- struct netlink_callback *),
-- int (*done)(struct netlink_callback *))
-+int netlink_dump_init(struct sock *ssk, struct sk_buff *skb,
-+ struct nlmsghdr *nlh,
-+ int (*dump)(struct sk_buff *skb,
-+ struct netlink_callback *),
-+ int (*done)(struct netlink_callback *),
-+ unsigned char init, ...)
- {
- struct netlink_callback *cb;
- struct sock *sk;
- struct netlink_sock *nlk;
-+ va_list args;
-+ unsigned char i;
-
- cb = kzalloc(sizeof(*cb), GFP_KERNEL);
- if (cb == NULL)
-@@ -1733,6 +1736,10 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
- cb->nlh = nlh;
- atomic_inc(&skb->users);
- cb->skb = skb;
-+ va_start(args, init);
-+ for (i = 0; i < init; i++)
-+ cb->args[i] = va_arg(args, long);
-+ va_end(args);
-
- sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).pid);
- if (sk == NULL) {
-@@ -1759,7 +1766,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
- */
- return -EINTR;
- }
--EXPORT_SYMBOL(netlink_dump_start);
-+EXPORT_SYMBOL(netlink_dump_init);
-
- void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
- {
ipset_hash_ipportip.c \
ipset_hash_ipportnet.c \
ipset_hash_net.c \
+ ipset_hash_netport.c \
ipset_list_set.c \
ui.c
ipset_LDADD = ../lib/libipset.la
{ 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." },
+ "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 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,
{ 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." },
+ "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." },
+ "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 an error code
+ * ipset_errcode - interpret a kernel error code
* @session: session structure
* @errcode: errcode
*
* Find the error code and print the appropriate
- * error message.
+ * error message into the error buffer.
*
* Returns -1.
*/
strerror(errcode));
else
return ipset_err(session,
- "Undecoded error %u received from kernel", errcode);
+ "Undecoded error %u received from kernel",
+ errcode);
}
.\" 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" "Jun 11, 2010" "Jozsef Kadlecsik" ""
+.TH "IPSET" "8" "Oct 15, 2010" "Jozsef Kadlecsik" ""
.SH "NAME"
ipset \(em administration tool for IP sets
.SH "SYNOPSIS"
\fB\-exist\fR
option is specified,
\fBipset\fR
-ignores the error otherwise raised when the the same set (setname and create parameters
+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 ]
\fB\-exist\fR
option is specified,
\fBipset\fR
-ignores if the entry does not added (expired) to the set.
+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
(default) or IPv4 network addresses. A \fBbitmap:ip\fR type of set can store up
to 65536 entries.
.PP
-\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ]
+\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 := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR }
+\fIADD\-ENTRY\fR := { \fIipaddr\fR | \fIfromip\fR\-\fItoip\fR | \fIipaddr\fR/\fIcidr\fR }
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR }
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR | \fIfromip\fR\-\fItoip\fR | \fIipaddr\fR/\fIcidr\fR }
.PP
\fITEST\-ENTRY\fR := \fIipaddr\fR
.PP
Mandatory \fBcreate\fR options:
.TP
-\fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR
+\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.
.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 value must 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 cidr value,
+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
.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 \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR [ \fBtimeout\fR \fIvalue\fR ]
+\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfromip\fP\-\fItoip\fR|\fIip\fR/\fIcidr\fR [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR]
+\fIADD\-ENTRY\fR := \fIipaddr\fR[,\fImacaddr\fR]
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR]
+\fIDEL\-ENTRY\fR := \fIipaddr\fR[,\fImacaddr\fR]
.PP
-\fITEST\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR]
+\fITEST\-ENTRY\fR := \fIipaddr\fR[,\fImacaddr\fR]
.PP
Mandatory options to use when creating a \fBbitmap:ip,mac\fR type of set:
.TP
-\fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR
+\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.
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 \fIfrom\-port\fP\-\fIto\-port [ \fBtimeout\fR \fIvalue\fR ]
+\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfromport\fP\-\fItoport [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := { \fIport\fR | \fIfrom\-port\fR\-\fIto\-port\fR }
+\fIADD\-ENTRY\fR := { \fIport\fR | \fIfromport\fR\-\fItoport\fR }
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := { \fIport\fR | \fIfrom\-port\fR\-\fIto\-port\fR }
+\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 \fIfrom\-port\fP\-\fIto\-port\fR
+\fBrange\fP \fIfromport\fP\-\fItoport\fR
Create the set from the specified inclusive port range.
.PP
Examples:
.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 value must 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 cidr, can be found in the set.
+calculated from the prefix, can be found in the set.
.PP
Examples:
.IP
\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 parameter is not specified,
-then the host cidr value is assumed. When adding/deleting entries, overlapping elements
-are not checked.
+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 an IP address will be in a \fBhash:net\fR type of set if it belongs to any of the netblocks added to the set.
The matching always start from the smallest size of netblock (most specific
-cidr) to the largest ones (least specific cidr). When adding/deleting IP
+prefix) to the largest ones (least specific prefix). When adding/deleting IP
addresses 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.
+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 \fIcidr\fR
+The lookup time grows linearly with the number of the different prefix
values added to the set.
.PP
Examples:
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.
+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
-You can imagine a setlist type of set as an ordered union of
-the set elements.
+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.
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_tree_ip0;
extern struct ipset_type ipset_list_set0;
enum exittype {
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]);
+ 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;
case IPSET_MANDATORY_ARG:
if (i + 1 > *argc)
return exit_error(PARAMETER_PROBLEM,
- "Missing mandatory argument of option `%s'",
+ "Missing mandatory argument "
+ "of option `%s'",
arg->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
return;
if (!arg) {
exit_error(OTHER_PROBLEM,
- "There are missing mandatory flags but can't check them. "
+ "There are missing mandatory flags "
+ "but can't check them. "
"It's a bug, please report the problem.");
return;
}
case IPSET_MANDATORY_ARG:
if (i + 1 > argc)
return exit_error(PARAMETER_PROBLEM,
- "Missing mandatory argument to option %s",
+ "Missing mandatory argument "
+ "to option %s",
opt->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
|| command->cmd == IPSET_CMD_VERSION
|| command->cmd == IPSET_CMD_HELP))
return exit_error(PARAMETER_PROBLEM,
- "Command `%s' is invalid in restore mode.",
+ "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");
+ if (interactive
+ && command->cmd == IPSET_CMD_RESTORE) {
+ printf("Restore command ignored "
+ "in interactive mode\n");
return 0;
}
case IPSET_MANDATORY_ARG2:
if (i + 1 > argc)
return exit_error(PARAMETER_PROBLEM,
- "Missing mandatory argument to command %s",
+ "Missing mandatory argument "
+ "to command %s",
command->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
if (command->has_arg == IPSET_MANDATORY_ARG2) {
if (i + 1 > argc)
return exit_error(PARAMETER_PROBLEM,
- "Missing second mandatory argument to command %s",
+ "Missing second mandatory "
+ "argument to command %s",
command->name[0]);
arg1 = argv[i];
/* Shift off second arg */
}
if (argc > 1)
return exit_error(PARAMETER_PROBLEM,
- "No command specified: unknown argument %s", argv[1]);
+ "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);
"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",
+ printf("\nType %s supports INET "
+ "and INET6.\n",
type->name);
else
- printf("\nType %s supports family %s only.\n",
+ printf("\nType %s supports family "
+ "%s only.\n",
type->name,
- type->family == AF_INET ? "INET" : "INET6");
+ type->family == AF_INET
+ ? "INET" : "INET6");
} else {
printf("\nSupported set types:\n");
type = ipset_types();
case IPSET_CMD_SAVE:
/* Args: [setname] */
if (arg0) {
- ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
+ ret = ipset_parse_setname(session,
+ IPSET_SETNAME, arg0);
if (ret < 0)
return handle_error();
}
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);
" [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";
+"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",
" [matchunset] [timeout VALUE]\n"
"add SETNAME IP[,MAC] [timeout VALUE]\n"
"del SETNAME IP[,MAC]\n"
-"test 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",
static const struct ipset_arg bitmap_port_create_args[] = {
{ .name = { "range", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT,
- .parse = ipset_parse_port, .print = ipset_print_port,
+ .parse = ipset_parse_tcp_port, .print = ipset_print_port,
},
{ .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
/* Backward compatibility */
{ .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT,
- .parse = ipset_parse_single_port,
+ .parse = ipset_parse_single_tcp_port,
},
{ .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT_TO,
- .parse = ipset_parse_single_port,
+ .parse = ipset_parse_single_tcp_port,
},
{ },
};
" [timeout VALUE]\n"
"add SETNAME PORT|FROM-TO [timeout VALUE]\n"
"del SETNAME PORT|FROM-TO\n"
-"test SETNAME PORT\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",
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
- .parse = ipset_parse_port,
+ .parse = ipset_parse_tcp_port,
.print = ipset_print_port,
.opt = IPSET_OPT_PORT
},
" [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";
+"test SETNAME IP\n\n"
+"where depending on the INET family\n"
+" IP, FROM and TO are IPv4 or IPv6 addresses (or hostnames),\n"
+" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n";
struct ipset_type ipset_hash_ip0 = {
.name = "hash:ip",
#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 */
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "proto", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO,
- .parse = ipset_parse_proto, .print = ipset_print_proto,
- },
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
static const char hash_ipport_usage[] =
"create SETNAME hash:ip,port\n"
-" [family inet|inet6] [proto PROTO]\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";
+"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";
struct ipset_type ipset_hash_ipport0 = {
.name = "hash:ip,port",
.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_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
},
.usage = hash_ipport_usage,
+ .usagefn = ipset_port_usage,
};
#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 */
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "proto", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO,
- .parse = ipset_parse_proto, .print = ipset_print_proto,
- },
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
static const char hash_ipportip_usage[] =
"create SETNAME hash:ip,port,ip\n"
-" [family inet|inet6] [proto PROTO]\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";
+"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 are valid IPv4 or IPv6 addresses (or hostnames),\n";
struct ipset_type ipset_hash_ipportip0 = {
.name = "hash:ip,port,ip",
[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_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
},
.usage = hash_ipportip_usage,
+ .usagefn = ipset_port_usage,
};
#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 */
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "proto", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO,
- .parse = ipset_parse_proto, .print = ipset_print_proto,
- },
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
static const char hash_ipportnet_usage[] =
"create SETNAME hash:ip,port,net\n"
-" [family inet|inet6] [proto PROTO]\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";
+"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";
struct ipset_type ipset_hash_ipportnet0 = {
.name = "hash:ip,port,net",
[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_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
},
.usage = hash_ipportnet_usage,
+ .usagefn = ipset_port_usage,
};
" [timeout VALUE]\n"
"add SETNAME IP[/CIDR] [timeout VALUE]\n"
"del SETNAME IP[/CIDR]\n"
-"test 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",
--- /dev/null
+/* 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";
+
+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_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_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,
+};
" [size VALUE] [timeout VALUE]\n"
"add SETNAME NAME [before|after NAME] [timeout VALUE]\n"
"del SETNAME NAME\n"
-"test SETNAME NAME\n";
+"test SETNAME NAME\n\n"
+"where NAME are existing set names.\n";
struct ipset_type ipset_list_set0 = {
.name = "list:set",
#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 */
if (len > strlen(name[0]) || !len)
return false;
- else if (memcmp(arg, name[0], len) == 0)
+ else if (strcmp(arg, name[0]) == 0)
return true;
else if (len != 1)
return false;
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);
+}
Size in memory: 8720
References: 0
Members:
-2.0.0.0,5,1.1.1.1 timeout x
-2.0.0.1,5,1.1.1.1 timeout x
-2.1.0.0,128,2.2.2.2 timeout x
-2.1.0.1,128,2.2.2.2 timeout x
+2.0.0.0,tcp:5,1.1.1.1 timeout x
+2.0.0.1,tcp:5,1.1.1.1 timeout x
+2.1.0.0,tcp:128,2.2.2.2 timeout x
+2.1.0.1,tcp:128,2.2.2.2 timeout x
0 ipset flush test
# Delete test set
0 ipset destroy test
-# Create a set with default TCP protocol
-0 ipset create test hash:ip,port proto tcp
+# Create a set
+0 ipset create test hash:ip,port
# Add element without specifying protocol
0 ipset add test 2.0.0.1,80
# Add "same" element but with UDP protocol
Size in memory: 8592
References: 0
Members:
-2.0.0.0,5 timeout x
-2.0.0.1,5 timeout x
-2.1.0.0,128 timeout x
-2.1.0.1,128 timeout x
+2.0.0.0,tcp:5 timeout x
+2.0.0.1,tcp:5 timeout x
+2.1.0.0,tcp:128 timeout x
+2.1.0.1,tcp:128 timeout x
Name: test
Type: hash:ip,port
-Header: family inet hashsize 1024 maxelem 65536 proto tcp
-Size in memory: 8424
+Header: family inet hashsize 1024 maxelem 65536
+Size in memory: 8432
References: 0
Members:
2.0.0.1,ospf:0
0 ipset -F test
# IP: Delete test set
0 ipset -X test
+# IP: Stress test resizing
+0 ./resize.sh
# Network: Create a set with timeout
0 ipset -N test iphash --hashsize 128 --netmask 24 timeout 6
# Network: Add zero valued element
Size in memory: 9104
References: 0
Members:
-2:1::1,128,2:2:2::2 timeout x
-2:1::,128,2:2:2::2 timeout x
-2::1,5,1:1:1::1 timeout x
-2::,5,1:1:1::1 timeout x
+2:1::1,tcp:128,2:2:2::2 timeout x
+2:1::,tcp:128,2:2:2::2 timeout x
+2::1,tcp:5,1:1:1::1 timeout x
+2::,tcp:5,1:1:1::1 timeout x
Size in memory: 8848
References: 0
Members:
-2:1::1,128 timeout x
-2:1::,128 timeout x
-2::1,5 timeout x
-2::,5 timeout x
+2:1::1,tcp:128 timeout x
+2:1::,tcp:128 timeout x
+2::1,tcp:5 timeout x
+2::,tcp:5 timeout x
--- /dev/null
+# Create a set with timeout
+0 ipset create test hash:net,port hashsize 128 timeout 6
+# Add zero valued element
+1 ipset add test 0.0.0.0/0,0
+# Test zero valued element
+1 ipset test test 0.0.0.0/0,0
+# Delete zero valued element
+1 ipset del test 0.0.0.0/0,0
+# Try to add /0
+1 ipset add test 1.1.1.1/0,0
+# Try to add /32
+0 ipset add test 1.1.1.1/32,tcp:5
+# Add almost zero valued element
+0 ipset add test 0.0.0.0/1,tcp:8
+# Test almost zero valued element
+0 ipset test test 0.0.0.0/1,tcp:8
+# Test almost zero valued element with UDP
+1 ipset test test 0.0.0.0/1,udp:8
+# Delete almost zero valued element
+0 ipset del test 0.0.0.0/1,tcp:8
+# Test deleted element
+1 ipset test test 0.0.0.0/1,tcp:8
+# Delete element not added to the set
+1 ipset del test 0.0.0.0/1,tcp:8
+# Add first random network
+0 ipset add test 2.0.0.1/24,icmp:ping
+# Add second random network
+0 ipset add test 192.168.68.69/27,tcp:8
+# Test first random value
+0 ipset test test 2.0.0.255,icmp:ping
+# Test second random value
+0 ipset test test 192.168.68.95,tcp:8
+# Test value not added to the set
+1 ipset test test 2.0.1.0,icmp:ping
+# Try to add IP address
+0 ipset add test 2.0.0.1,icmp:ping timeout 3
+# List set
+0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
+# Check listing
+0 diff -I 'Size in memory.*' .foo hash:net,port.t.list0
+# Sleep 6s so that element can time out
+0 sleep 6
+# IP: List set
+0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0
+# IP: Check listing
+0 diff -I 'Size in memory.*' .foo hash:net,port.t.list1 && rm .foo
+# Flush test set
+0 ipset flush test
+# Delete test set
+0 ipset destroy test
+# eof
--- /dev/null
+Name: test
+Type: hash:net,port
+Header: family inet hashsize 128 maxelem 65536 timeout x
+Size in memory: 2072
+References: 0
+Members:
+1.1.1.1,tcp:5 timeout x
+192.168.68.64/27,tcp:8 timeout x
+2.0.0.0/24,icmp:echo-request timeout x
+2.0.0.1,icmp:echo-request timeout x
+
--- /dev/null
+Name: test
+Type: hash:net,port
+Header: family inet hashsize 128 maxelem 65536 timeout 6
+Size in memory: 2000
+References: 0
+Members:
+
--- /dev/null
+# Create a set with timeout
+0 ipset create test hash:net,port family inet6 hashsize 128 timeout 6
+# Add zero valued element
+1 ipset add test ::/0,tcp:8
+# Test zero valued element
+1 ipset test test ::/0,tcp:8
+# Delete zero valued element
+1 ipset del test ::/0,tcp:8
+# Try to add /0
+1 ipset add test 1:1:1::1/0,tcp:8
+# Try to add /128
+0 ipset add test 1:1:1::1/128,tcp:8 timeout 0
+# Add almost zero valued element
+0 ipset add test 0:0:0::0/1,tcp:8
+# Test almost zero valued element
+0 ipset test test 0:0:0::0/1,tcp:8
+# Test almost zero valued element with UDP
+1 ipset test test 0:0:0::0/1,udp:8
+# Delete almost zero valued element
+0 ipset del test 0:0:0::0/1,tcp:8
+# Test deleted element
+1 ipset test test 0:0:0::0/1,tcp:8
+# Delete element not added to the set
+1 ipset del test 0:0:0::0/1,tcp:8
+# Add first random network
+0 ipset add test 2:0:0::1/24,tcp:8
+# Add second random network
+0 ipset add test 192:168:68::69/27,icmpv6:ping
+# Test first random value
+0 ipset test test 2:0:0::255,tcp:8
+# Test second random value
+0 ipset test test 192:168:68::95,icmpv6:ping
+# Test value not added to the set
+1 ipset test test 3:0:0::1,tcp:8
+# Try to add IP address
+0 ipset add test 3:0:0::1,tcp:8
+# List set
+0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
+# Check listing
+0 diff -I 'Size in memory.*' .foo hash:net6,port.t.list0 && rm .foo
+# Sleep 6s so that element can time out
+0 sleep 6
+# IP: List set
+0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0
+# IP: Check listing
+0 diff -I 'Size in memory.*' .foo hash:net6,port.t.list1 && rm .foo
+# Flush test set
+0 ipset flush test
+# Delete test set
+0 ipset destroy test
+# eof
--- /dev/null
+Name: test
+Type: hash:net,port
+Header: family inet6 hashsize 128 maxelem 65536 timeout x
+Size in memory: 2328
+References: 0
+Members:
+1:1:1::1,tcp:8 timeout x
+192:160::/27,ipv6-icmp:echo-request timeout x
+2::/24,tcp:8 timeout x
+3::1,tcp:8 timeout x
+
--- /dev/null
+Name: test
+Type: hash:net,port
+Header: family inet6 hashsize 128 maxelem 65536 timeout 6
+Size in memory: 2328
+References: 0
+Members:
+1:1:1::1,tcp:8 timeout 0
+
1 ipset del test ::/0
# Try to add /0
1 ipset add test 1:1:1::1/0
-# Try to add /32
+# Try to add /128
0 ipset add test 1:1:1::1/128
# Add almost zero valued element
0 ipset add test 0:0:0::0/1
Size in memory: 8464
References: 0
Members:
-2.0.0.0,5
-2.0.0.1,5
-2.1.0.0,128
-2.1.0.1,128
+2.0.0.0,tcp:5
+2.0.0.1,tcp:5
+2.1.0.0,tcp:128
+2.1.0.1,tcp:128
Size in memory: 8464
References: 0
Members:
-1.255.255.255,5
-2.0.0.0,5
-2.0.255.255,128
-2.1.0.0,128
+1.255.255.255,tcp:5
+2.0.0.0,tcp:5
+2.0.255.255,tcp:128
+2.1.0.0,tcp:128
Size in memory: 8528
References: 0
Members:
-2.0.0.0,5,1.1.1.1
-2.0.0.1,5,1.1.1.1
-2.1.0.0,128,2.2.2.2
-2.1.0.1,128,2.2.2.2
+2.0.0.0,tcp:5,1.1.1.1
+2.0.0.1,tcp:5,1.1.1.1
+2.1.0.0,tcp:128,2.2.2.2
+2.1.0.1,tcp:128,2.2.2.2
Size in memory: 8416
References: 0
Members:
-2.0.0.0,5,1.1.1.1
-2.0.255.255,128,2.2.2.2
+2.0.0.0,tcp:5,1.1.1.1
+2.0.255.255,tcp:128,2.2.2.2
Size in memory: 8776
References: 0
Members:
-2.0.0.0,5,1.1.1.0/24
-2.0.0.1,5,1.1.1.0/24
-2.1.0.0,128,2.0.0.0/12
-2.1.0.1,128,2.0.0.0/12
+2.0.0.0,tcp:5,1.1.1.0/24
+2.0.0.1,tcp:5,1.1.1.0/24
+2.1.0.0,tcp:128,2.0.0.0/12
+2.1.0.1,tcp:128,2.0.0.0/12
Size in memory: 8776
References: 0
Members:
-1.255.255.255,5,1.1.1.0/24
-2.0.0.0,5,1.1.1.0/24
-2.0.255.255,128,2.0.0.0/12
-2.1.0.0,128,2.0.0.0/12
+1.255.255.255,tcp:5,1.1.1.0/24
+2.0.0.0,tcp:5,1.1.1.0/24
+2.0.255.255,tcp:128,2.0.0.0/12
+2.1.0.0,tcp:128,2.0.0.0/12
../src/ipset a ip1 $IP1 2>/dev/null
../src/ipset n ip2 hash:ip $family 2>/dev/null
../src/ipset a ip2 $IP2 2>/dev/null
- ../src/ipset n ipport hash:ip,port $family proto any 2>/dev/null
+ ../src/ipset n ipport hash:ip,port $family 2>/dev/null
../src/ipset n list list:set 2>/dev/null
../src/ipset a list ipport 2>/dev/null
../src/ipset a list ip1 2>/dev/null
--- /dev/null
+#!/bin/bash
+
+set -e
+
+../src/ipset n resize-test hash:ip hashsize 64
+for x in `seq 1 20`; do
+ for y in `seq 1 255`; do
+ ../src/ipset a resize-test 192.168.$x.$y
+ done
+done
+../src/ipset x resize-test
#!/bin/bash
+# set -x
+
tests="init"
tests="$tests ipmap bitmap:ip"
tests="$tests macipmap portmap"
tests="$tests iphash hash:ip hash:ip6"
tests="$tests ipporthash hash:ip,port hash:ip6,port"
tests="$tests ipportiphash hash:ip,port,ip hash:ip6,port,ip6"
-tests="$tests nethash hash:net hash:net6"
+tests="$tests nethash hash:net hash:net6 hash:net,port hash:net6,port"
tests="$tests setlist"
tests="$tests iptree iptreemap"
add=match_target6
fi
line="`dmesg | tail -1 | cut -d " " -f 2-`"
- if [ ! -e /var/log/kern.log -o -z "`grep \"$line\" /var/log/kern.log`" ]; then
+ if [ ! -e /var/log/kern.log -o -z "`grep -F \"$line\" /var/log/kern.log`" ]; then
echo "The destination for kernel log is not /var/log/kern.log, skipping $1 match and target tests"
return
fi