Included in this package
========================
- ipset 4.5
-- ipset 5.4.1
+- ipset 6.6a-genl
- xt_ACCOUNT 1.16, libxt_ACCOUNT 1.3
IPSET_OPT_NAMEREF,
IPSET_OPT_IP2,
IPSET_OPT_CIDR2,
+ IPSET_OPT_IP2_TO,
IPSET_OPT_PROTO,
/* Swap/rename to */
IPSET_OPT_SETNAME2,
*/
/* The protocol version */
-#define IPSET_PROTOCOL 6
+#define IPSET_PROTOCOL 0x60
/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2,
+ IPSET_ATTR_IP2_TO,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
+ IPSET_FLAG_BIT_LIST_SETNAME = 1,
+ IPSET_FLAG_LIST_SETNAME = (1 << IPSET_FLAG_BIT_LIST_SETNAME),
+ IPSET_FLAG_BIT_LIST_HEADER = 2,
+ IPSET_FLAG_LIST_HEADER = (1 << IPSET_FLAG_BIT_LIST_HEADER),
};
/* Flags at CADT attribute level */
IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO,
+ /* Range not supported */
+ IPSET_ERR_HASH_RANGE_UNSUPPORTED,
+ /* Invalid range */
+ IPSET_ERR_HASH_RANGE,
};
#endif /* __IP_SET_HASH_H */
enum ipset_opt opt, const char *str);
extern int ipset_parse_ip4_single6(struct ipset_session *session,
enum ipset_opt opt, const char *str);
+extern int ipset_parse_ip4_net6(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
extern int ipset_parse_name(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_before(struct ipset_session *session,
IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE),
IPSET_ENV_BIT_EXIST = 3,
IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST),
+ IPSET_ENV_BIT_LIST_SETNAME = 4,
+ IPSET_ENV_LIST_SETNAME = (1 << IPSET_ENV_BIT_LIST_SETNAME),
+ IPSET_ENV_BIT_LIST_HEADER = 5,
+ IPSET_ENV_LIST_HEADER = (1 << IPSET_ENV_BIT_LIST_HEADER),
};
extern int ipset_envopt_parse(struct ipset_session *session,
extern struct ipset_session * ipset_session_init(ipset_outfn outfn);
extern int ipset_session_fini(struct ipset_session *session);
+extern void ipset_debug_msg(const char *dir, void *buffer, int len);
+
#endif /* LIBIPSET_SESSION_H */
#include <linux/netlink.h>
/* The protocol version */
-#define IPSET_PROTOCOL 6
+#define IPSET_PROTOCOL 0x60
/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2,
+ IPSET_ATTR_IP2_TO,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
+ IPSET_FLAG_BIT_LIST_SETNAME = 1,
+ IPSET_FLAG_LIST_SETNAME = (1 << IPSET_FLAG_BIT_LIST_SETNAME),
+ IPSET_FLAG_BIT_LIST_HEADER = 2,
+ IPSET_FLAG_LIST_HEADER = (1 << IPSET_FLAG_BIT_LIST_HEADER),
};
/* Flags at CADT attribute level */
#include <linux/ipv6.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
+#include <linux/netfilter/x_tables.h>
#include <linux/vmalloc.h>
#include <net/netlink.h>
typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
u32 timeout, u32 flags);
+/* Kernel API function options */
+struct ip_set_adt_opt {
+ u8 family; /* Actual protocol family */
+ u8 dim; /* Dimension of match/target */
+ u8 flags; /* Direction and negation flags */
+ u32 cmdflags; /* Command-like flags */
+ u32 timeout; /* Timeout value */
+};
+
/* Set type, variant-specific part */
struct ip_set_type_variant {
/* Kernelspace: test/add/del entries
* zero for no match/success to add/delete
* positive for matching element */
int (*kadt)(struct ip_set *set, const struct sk_buff * skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt);
/* Userspace: test/add/del entries
* returns negative error code,
* zero for no match/success to add/delete
* positive for matching element */
int (*uadt)(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags);
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
/* Low level add/del/test functions */
ipset_adtfn adt[IPSET_ADT_MAX];
u8 dimension;
/* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */
u8 family;
- /* Type revision */
- u8 revision;
+ /* Type revisions */
+ u8 revision_min, revision_max;
/* Create set */
int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags);
const struct ip_set_type_variant *variant;
/* The actual INET family of the set */
u8 family;
+ /* The type revision */
+ u8 revision;
/* The type specific data */
void *data;
};
extern void ip_set_nfnl_put(ip_set_id_t index);
/* API for iptables set match, and SET target */
+
extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb,
- u8 family, u8 dim, u8 flags);
+ const struct xt_action_param *par,
+ const struct ip_set_adt_opt *opt);
extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb,
- u8 family, u8 dim, u8 flags);
+ const struct xt_action_param *par,
+ const struct ip_set_adt_opt *opt);
extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb,
- u8 family, u8 dim, u8 flags);
+ const struct xt_action_param *par,
+ const struct ip_set_adt_opt *opt);
/* Utility functions */
extern void * ip_set_alloc(size_t size);
#include "jhash.h"
#include "ip_set_timeout.h"
+#define CONCAT(a, b, c) a##b##c
+#define TOKEN(a, b, c) CONCAT(a, b, c)
+
+#define type_pf_next TOKEN(TYPE, PF, _elem)
+
/* Hashing which uses arrays to resolve clashing. The hash table is resized
* (doubled) when searching becomes too long.
* Internally jhash is used with the assumption that the size of the
u32 initval; /* random jhash init value */
u32 timeout; /* timeout value, if enabled */
struct timer_list gc; /* garbage collection when timeout enabled */
+ struct type_pf_next next; /* temporary storage for uadd */
#ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask; /* netmask value for subnets to store */
#endif
#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask)
#define type_pf_data_list TOKEN(TYPE, PF, _data_list)
#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist)
+#define type_pf_data_next TOKEN(TYPE, PF, _data_next)
#define type_pf_elem TOKEN(TYPE, PF, _elem)
#define type_pf_telem TOKEN(TYPE, PF, _telem)
return 0;
}
+static void
+type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d);
+
/* Add an element to a hash and update the internal counters when succeeded,
* otherwise report the proper error code. */
static int
}
ret = type_pf_elem_add(n, value);
- if (ret != 0)
+ if (ret != 0) {
+ if (ret == -EAGAIN)
+ type_pf_data_next(h, d);
goto out;
+ }
#ifdef IP_SET_HASH_WITH_NETS
add_cidr(h, d->cidr, HOST_MASK);
static int
type_pf_kadt(struct ip_set *set, const struct sk_buff * skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt);
static int
type_pf_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags);
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
static const struct ip_set_type_variant type_pf_variant = {
.kadt = type_pf_kadt,
goto out;
}
ret = type_pf_elem_tadd(n, d, timeout);
- if (ret != 0)
+ if (ret != 0) {
+ if (ret == -EAGAIN)
+ type_pf_data_next(h, d);
goto out;
+ }
#ifdef IP_SET_HASH_WITH_NETS
add_cidr(h, d->cidr, HOST_MASK);
struct htable *t = h->table;
const struct type_pf_elem *d = value;
struct hbucket *n;
- int i, ret = 0;
+ int i;
struct type_pf_elem *data;
u32 key;
if (!type_pf_data_equal(data, d))
continue;
if (type_pf_data_expired(data))
- ret = -IPSET_ERR_EXIST;
+ return -IPSET_ERR_EXIST;
if (i != n->pos - 1)
/* Not last one */
type_pf_data_copy(data, ahash_tdata(n, n->pos - 1));
static int
bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct bitmap_ip *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip;
- ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
+ ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
ip = ip_to_id(map, ip);
- return adtfn(set, &ip, map->timeout, flags);
+ return adtfn(set, &ip, opt_timeout(opt, map), opt->cmdflags);
}
static int
bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct bitmap_ip *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
- ip &= ip_set_hostmask(cidr);
- ip_to = ip | ~ip_set_hostmask(cidr);
+ ip_set_mask_from_to(ip, ip_to, cidr);
} else
ip_to = ip;
if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR;
- last_ip = first_ip | ~ip_set_hostmask(cidr);
+ ip_set_mask_from_to(first_ip, last_ip, cidr);
} else
return -IPSET_ERR_PROTOCOL;
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_INET,
- .revision = 0,
+ .revision_min = 0,
+ .revision_max = 0,
.create = bitmap_ip_create,
.create_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
static int
bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data;
/* MAC can be src only */
- if (!(flags & IPSET_DIM_TWO_SRC))
+ if (!(opt->flags & IPSET_DIM_TWO_SRC))
return 0;
- data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
+ data.id = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
data.id -= map->first_ip;
data.ether = eth_hdr(skb)->h_source;
- return adtfn(set, &data, map->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, map), opt->cmdflags);
}
static int
bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR;
- last_ip = first_ip | ~ip_set_hostmask(cidr);
+ ip_set_mask_from_to(first_ip, last_ip, cidr);
} else
return -IPSET_ERR_PROTOCOL;
.features = IPSET_TYPE_IP | IPSET_TYPE_MAC,
.dimension = IPSET_DIM_TWO,
.family = AF_INET,
- .revision = 0,
+ .revision_min = 0,
+ .revision_max = 0,
.create = bitmap_ipmac_create,
.create_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
static int
bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct bitmap_port *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
__be16 __port;
u16 port = 0;
- if (!ip_set_get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &__port))
+ if (!ip_set_get_ip_port(skb, opt->family,
+ opt->flags & IPSET_DIM_ONE_SRC, &__port))
return -EINVAL;
port = ntohs(__port);
port -= map->first_port;
- return adtfn(set, &port, map->timeout, flags);
+ return adtfn(set, &port, opt_timeout(opt, map), opt->cmdflags);
}
static int
bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct bitmap_port *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
.features = IPSET_TYPE_PORT,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
- .revision = 0,
+ .revision_min = 0,
+ .revision_max = 0,
.create = bitmap_port_create,
.create_policy = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
+++ /dev/null
-#ifndef _IP_SET_CHASH_H
-#define _IP_SET_CHASH_H
-
-#include "jhash.h"
-#include "slist.h"
-#include "ip_set_timeout.h"
-
-/* 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 */
-#define CHASH_DEFAULT_ARRAY_SIZE 4
-/* 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 */
-};
-
-struct htable {
- u8 htable_bits; /* size of hash table == 2^htable_bits */
- struct slist htable[0]; /* hashtable of single linked lists */
-};
-
-struct chash {
- struct htable *table; /* the hash table */
- u32 maxelem; /* max elements in the hash */
- u32 elements; /* current element (vs timeout) */
- u32 initval; /* random jhash init value */
- u32 timeout; /* timeout value, if enabled */
- struct timer_list gc; /* garbage collection when timeout enabled */
- u8 array_size; /* number of elements in an array */
- u8 chain_limit; /* max number of arrays */
-#ifdef IP_SET_HASH_WITH_NETMASK
- u8 netmask; /* netmask value for subnets to store */
-#endif
-#ifdef IP_SET_HASH_WITH_NETS
- 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)
-{
- /* Assume that hashsize == 2^htable_bits */
- u8 bits = fls(hashsize - 1);
- if (jhash_size(bits) != hashsize)
- /* Round up to the first 2^n value */
- bits = fls(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 *h, u8 cidr, u8 host_mask)
-{
- u8 i;
-
- ++h->nets[cidr-1].nets;
-
- pr_debug("add_cidr added %u: %u", cidr, h->nets[cidr-1].nets);
-
- 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 (h->nets[i].cidr < cidr)
- swap(h->nets[i].cidr, cidr);
- }
- if (i < host_mask)
- h->nets[i].cidr = cidr;
-}
-
-static inline void
-del_cidr(struct chash *h, u8 cidr, u8 host_mask)
-{
- u8 i;
-
- --h->nets[cidr-1].nets;
-
- pr_debug("del_cidr deleted %u: %u", cidr, h->nets[cidr-1].nets);
-
- 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;
- }
- h->nets[i - 1].cidr = 0;
-}
-#endif
-
-/* Destroy the hashtable part of the set */
-static void
-chash_destroy(struct htable *ht)
-{
- struct slist *n, *tmp;
- u32 i;
-
- for (i = 0; i < jhash_size(ht->htable_bits); i++)
- slist_for_each_safe(n, tmp, &ht->htable[i])
- /* FIXME: use slab cache */
- kfree(n);
-
- ip_set_free(ht);
-}
-
-/* Calculate the actual memory size of the set data */
-static size_t
-chash_memsize(const struct chash *h, size_t dsize, u8 host_mask)
-{
- struct slist *n;
- u32 i;
- struct htable *ht = h->table;
- size_t memsize = sizeof(*h)
-#ifdef IP_SET_HASH_WITH_NETS
- + sizeof(struct chash_nets) * host_mask
-#endif
- + jhash_size(ht->htable_bits) * sizeof(struct slist);
-
- for (i = 0; i < jhash_size(ht->htable_bits); i++)
- slist_for_each(n, &ht->htable[i])
- memsize += sizeof(struct slist)
- + h->array_size * dsize;
-
- return memsize;
-}
-
-/* Flush a hash type of set: destroy all elements */
-static void
-ip_set_hash_flush(struct ip_set *set)
-{
- struct chash *h = set->data;
- struct htable *ht = h->table;
- struct slist *n, *tmp;
- u32 i;
-
- for (i = 0; i < jhash_size(ht->htable_bits); i++) {
- slist_for_each_safe(n, tmp, &ht->htable[i])
- /* FIXME: slab cache */
- kfree(n);
- ht->htable[i].next = NULL;
- }
-#ifdef IP_SET_HASH_WITH_NETS
- memset(h->nets, 0, sizeof(struct chash_nets)
- * 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)
-{
- struct chash *h = set->data;
-
- if (with_timeout(h->timeout))
- del_timer_sync(&h->gc);
-
- chash_destroy(h->table);
- kfree(h);
-
- set->data = NULL;
-}
-
-#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)
-#define type_pf_data_isnull TOKEN(TYPE, PF, _data_isnull)
-#define type_pf_data_copy TOKEN(TYPE, PF, _data_copy)
-#define type_pf_data_swap TOKEN(TYPE, PF, _data_swap)
-#define type_pf_data_zero_out TOKEN(TYPE, PF, _data_zero_out)
-#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask)
-#define type_pf_data_list TOKEN(TYPE, PF, _data_list)
-#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist)
-
-#define type_pf_elem TOKEN(TYPE, PF, _elem)
-#define type_pf_telem TOKEN(TYPE, PF, _telem)
-#define type_pf_data_timeout TOKEN(TYPE, PF, _data_timeout)
-#define type_pf_data_expired TOKEN(TYPE, PF, _data_expired)
-#define type_pf_data_swap_timeout TOKEN(TYPE, PF, _data_swap_timeout)
-#define type_pf_data_timeout_set TOKEN(TYPE, PF, _data_timeout_set)
-
-#define type_pf_chash_readd TOKEN(TYPE, PF, _chash_readd)
-#define type_pf_chash_del_elem TOKEN(TYPE, PF, _chash_del_elem)
-#define type_pf_chash_add TOKEN(TYPE, PF, _chash_add)
-#define type_pf_chash_del TOKEN(TYPE, PF, _chash_del)
-#define type_pf_chash_test_cidrs TOKEN(TYPE, PF, _chash_test_cidrs)
-#define type_pf_chash_test TOKEN(TYPE, PF, _chash_test)
-
-#define type_pf_chash_treadd TOKEN(TYPE, PF, _chash_treadd)
-#define type_pf_chash_del_telem TOKEN(TYPE, PF, _chash_del_telem)
-#define type_pf_chash_expire TOKEN(TYPE, PF, _chash_expire)
-#define type_pf_chash_tadd TOKEN(TYPE, PF, _chash_tadd)
-#define type_pf_chash_tdel TOKEN(TYPE, PF, _chash_tdel)
-#define type_pf_chash_ttest_cidrs TOKEN(TYPE, PF, _chash_ttest_cidrs)
-#define type_pf_chash_ttest TOKEN(TYPE, PF, _chash_ttest)
-
-#define type_pf_resize TOKEN(TYPE, PF, _resize)
-#define type_pf_tresize TOKEN(TYPE, PF, _tresize)
-#define type_pf_flush ip_set_hash_flush
-#define type_pf_destroy ip_set_hash_destroy
-#define type_pf_head TOKEN(TYPE, PF, _head)
-#define type_pf_list TOKEN(TYPE, PF, _list)
-#define type_pf_tlist TOKEN(TYPE, PF, _tlist)
-#define type_pf_same_set TOKEN(TYPE, PF, _same_set)
-#define type_pf_kadt TOKEN(TYPE, PF, _kadt)
-#define type_pf_uadt TOKEN(TYPE, PF, _uadt)
-#define type_pf_gc TOKEN(TYPE, PF, _gc)
-#define type_pf_gc_init TOKEN(TYPE, PF, _gc_init)
-#define type_pf_variant TOKEN(TYPE, PF, _variant)
-#define type_pf_tvariant TOKEN(TYPE, PF, _tvariant)
-
-/* 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 htable *ht,
- const struct type_pf_elem *value,
- gfp_t gfp_flags)
-{
- struct slist *n, *prev;
- struct type_pf_elem *data;
- void *tmp;
- int i = 0, j = 0;
- u32 hash = JHASH2(value, h->initval, ht->htable_bits);
-
- slist_for_each_prev(prev, n, &ht->htable[hash]) {
- for (i = 0; i < h->array_size; i++) {
- data = chash_data(n, i);
- if (type_pf_data_isnull(data)) {
- tmp = n;
- goto found;
- }
- }
- j++;
- }
- if (j < h->chain_limit) {
- tmp = kzalloc(h->array_size * sizeof(struct type_pf_elem)
- + sizeof(struct slist), gfp_flags);
- if (!tmp)
- return -ENOMEM;
- prev->next = (struct slist *) tmp;
- data = chash_data(tmp, 0);
- } else {
- /* Trigger rehashing */
- return -EAGAIN;
- }
-found:
- type_pf_data_copy(data, value);
- 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; /* Index in array */
-
- if (n->next != NULL) {
- for (prev = n, tmp = n->next;
- tmp->next != NULL;
- prev = tmp, tmp = tmp->next)
- /* Find last array */;
- j = 0;
- } else {
- /* Already at last array */
- tmp = n;
- j = i;
- }
- /* Find last non-empty element */
- for (; j < h->array_size - 1; j++)
- if (type_pf_data_isnull(chash_data(tmp, j + 1)))
- break;
-
- if (!(tmp == n && i == j))
- type_pf_data_swap(data, chash_data(tmp, j));
-
-#ifdef IP_SET_HASH_WITH_NETS
- del_cidr(h, data->cidr, HOST_MASK);
-#endif
- if (j == 0) {
- prev->next = NULL;
- kfree(tmp);
- } else
- type_pf_data_zero_out(chash_data(tmp, j));
-
- 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)
-{
- struct chash *h = set->data;
- struct htable *ht, *orig = h->table;
- u8 htable_bits = orig->htable_bits;
- struct slist *n;
- const struct type_pf_elem *data;
- u32 i, j;
- int ret;
-
-retry:
- ret = i = 0;
- htable_bits++;
- if (!htable_bits)
- /* In case we have plenty of memory :-) */
- return -IPSET_ERR_HASH_FULL;
- ht = ip_set_alloc(sizeof(*ht)
- + jhash_size(htable_bits) * sizeof(struct slist),
- GFP_KERNEL);
- if (!ht)
- return -ENOMEM;
- ht->htable_bits = htable_bits;
-
- read_lock_bh(&set->lock);
-next_slot:
- for (; i < jhash_size(orig->htable_bits); i++) {
- slist_for_each(n, &orig->htable[i]) {
- for (j = 0; j < h->array_size; j++) {
- data = chash_data(n, j);
- if (type_pf_data_isnull(data)) {
- i++;
- goto next_slot;
- }
- ret = type_pf_chash_readd(h, ht,
- data, gfp_flags);
- if (ret < 0) {
- read_unlock_bh(&set->lock);
- chash_destroy(ht);
- if (ret == -EAGAIN)
- goto retry;
- return ret;
- }
- }
- }
- }
-
- h->table = ht;
- read_unlock_bh(&set->lock);
-
- /* Give time to other users of the set */
- synchronize_net();
-
- chash_destroy(orig);
-
- 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)
-{
- struct chash *h = set->data;
- const struct type_pf_elem *d = value;
- struct slist *n, *prev;
- struct htable *ht = h->table;
- struct type_pf_elem *data;
- void *tmp;
- int i = 0, j = 0;
- u32 hash;
-
- if (h->elements >= h->maxelem)
- return -IPSET_ERR_HASH_FULL;
-
- hash = JHASH2(value, h->initval, ht->htable_bits);
- slist_for_each_prev(prev, n, &ht->htable[hash]) {
- for (i = 0; i < h->array_size; i++) {
- data = chash_data(n, i);
- if (type_pf_data_isnull(data)) {
- tmp = n;
- goto found;
- }
- if (type_pf_data_equal(data, d))
- return -IPSET_ERR_EXIST;
- }
- j++;
- }
- if (j < h->chain_limit) {
- tmp = kzalloc(h->array_size * sizeof(struct type_pf_elem)
- + sizeof(struct slist), gfp_flags);
- if (!tmp)
- return -ENOMEM;
- prev->next = (struct slist *) tmp;
- data = chash_data(tmp, 0);
- } else {
- /* Rehashing */
- return -EAGAIN;
- }
-found:
- type_pf_data_copy(data, d);
-#ifdef IP_SET_HASH_WITH_NETS
- 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)
-{
- struct chash *h = set->data;
- const struct type_pf_elem *d = value;
- struct htable *ht = h->table;
- struct slist *n, *prev;
- int i;
- struct type_pf_elem *data;
- u32 hash = JHASH2(value, h->initval, ht->htable_bits);
-
- slist_for_each_prev(prev, n, &ht->htable[hash])
- for (i = 0; i < h->array_size; i++) {
- data = chash_data(n, i);
- if (type_pf_data_isnull(data))
- return -IPSET_ERR_EXIST;
- if (type_pf_data_equal(data, d)) {
- type_pf_chash_del_elem(h, prev, n, i);
- return 0;
- }
- }
-
- return -IPSET_ERR_EXIST;
-}
-
-#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,
- gfp_t gfp_flags, u32 timeout)
-{
- struct chash *h = set->data;
- struct htable *ht = h->table;
- struct slist *n;
- const struct type_pf_elem *data;
- int i, j = 0;
- u32 hash;
- u8 host_mask = SET_HOST_MASK(set->family);
-
-retry:
- pr_debug("test by nets");
- for (; j < host_mask && h->nets[j].cidr; j++) {
- type_pf_data_netmask(d, h->nets[j].cidr);
- hash = JHASH2(d, h->initval, ht->htable_bits);
- slist_for_each(n, &ht->htable[hash])
- for (i = 0; i < h->array_size; i++) {
- data = chash_data(n, i);
- if (type_pf_data_isnull(data)) {
- j++;
- goto retry;
- }
- if (type_pf_data_equal(data, d))
- return 1;
- }
- }
- return 0;
-}
-#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)
-{
- struct chash *h = set->data;
- struct htable *ht = h->table;
- struct type_pf_elem *d = value;
- struct slist *n;
- const struct type_pf_elem *data;
- int i;
- u32 hash;
-
-#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
-
- hash = JHASH2(d, h->initval, ht->htable_bits);
- slist_for_each(n, &ht->htable[hash])
- for (i = 0; i < h->array_size; i++) {
- data = chash_data(n, i);
- if (type_pf_data_isnull(data))
- return 0;
- if (type_pf_data_equal(data, d))
- return 1;
- }
- 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)
-{
- const struct chash *h = set->data;
- struct nlattr *nested;
- size_t memsize;
-
- read_lock_bh(&set->lock);
- memsize = chash_memsize(h, with_timeout(h->timeout)
- ? sizeof(struct type_pf_telem)
- : sizeof(struct type_pf_elem),
- set->family == AF_INET ? 32 : 128);
- read_unlock_bh(&set->lock);
-
- nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
- if (!nested)
- goto nla_put_failure;
- NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE,
- htonl(jhash_size(h->table->htable_bits)));
- NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem));
-#ifdef IP_SET_HASH_WITH_NETMASK
- if (h->netmask != HOST_MASK)
- NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask);
-#endif
- NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
- htonl(atomic_read(&set->ref) - 1));
- NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize));
- if (with_timeout(h->timeout))
- NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout));
- ipset_nest_end(skb, nested);
-
- return 0;
-nla_put_failure:
- 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)
-{
- const struct chash *h = set->data;
- const struct htable *ht = h->table;
- struct nlattr *atd, *nested;
- struct slist *n;
- const struct type_pf_elem *data;
- u32 first = cb->args[2];
- /* We assume that one hash bucket fills into one page */
- void *incomplete;
- int i;
-
- atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
- if (!atd)
- return -EFAULT;
- pr_debug("list hash set %s", set->name);
- for (; cb->args[2] < jhash_size(ht->htable_bits); cb->args[2]++) {
- incomplete = skb_tail_pointer(skb);
- slist_for_each(n, &ht->htable[cb->args[2]]) {
- for (i = 0; i < h->array_size; i++) {
- data = chash_data(n, i);
- if (type_pf_data_isnull(data))
- break;
- pr_debug("list hash %lu slist %p i %u",
- cb->args[2], n, i);
- nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
- if (!nested) {
- if (cb->args[2] == first) {
- nla_nest_cancel(skb, atd);
- return -EFAULT;
- } else
- goto nla_put_failure;
- }
- if (type_pf_data_list(skb, data))
- goto nla_put_failure;
- ipset_nest_end(skb, nested);
- }
- }
- }
- ipset_nest_end(skb, atd);
- /* Set listing finished */
- cb->args[2] = 0;
-
- return 0;
-
-nla_put_failure:
- nlmsg_trim(skb, incomplete);
- ipset_nest_end(skb, atd);
- if (unlikely(first == cb->args[2])) {
- pr_warn("Can't list set %s: one bucket does not fit into "
- "a message. Please report it!\n", set->name);
- cb->args[2] = 0;
- }
- return 0;
-}
-
-static int
-type_pf_kadt(struct ip_set *set, const struct sk_buff * skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
-static int
-type_pf_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, u32 *lineno, u32 flags);
-
-static const struct ip_set_type_variant type_pf_variant = {
- .kadt = type_pf_kadt,
- .uadt = type_pf_uadt,
- .adt = {
- [IPSET_ADD] = type_pf_chash_add,
- [IPSET_DEL] = type_pf_chash_del,
- [IPSET_TEST] = type_pf_chash_test,
- },
- .destroy = type_pf_destroy,
- .flush = type_pf_flush,
- .head = type_pf_head,
- .list = type_pf_list,
- .resize = type_pf_resize,
- .same_set = type_pf_same_set,
-};
-
-/* Flavour with timeout support */
-
-#define chash_tdata(n, i) \
-(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)
-{
- const struct type_pf_telem *tdata =
- (const struct type_pf_telem *) data;
-
- return tdata->timeout;
-}
-
-static inline bool
-type_pf_data_expired(const struct type_pf_elem *data)
-{
- const struct type_pf_telem *tdata =
- (const struct type_pf_telem *) data;
-
- return ip_set_timeout_expired(tdata->timeout);
-}
-
-static inline void
-type_pf_data_swap_timeout(struct type_pf_elem *src,
- struct type_pf_elem *dst)
-{
- struct type_pf_telem *x = (struct type_pf_telem *) src;
- struct type_pf_telem *y = (struct type_pf_telem *) dst;
-
- swap(x->timeout, y->timeout);
-}
-
-static inline void
-type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout)
-{
- struct type_pf_telem *tdata = (struct type_pf_telem *) data;
-
- tdata->timeout = ip_set_timeout_set(timeout);
-}
-
-static int
-type_pf_chash_treadd(struct chash *h, struct htable *ht,
- const struct type_pf_elem *value,
- gfp_t gfp_flags, u32 timeout)
-{
- struct slist *n, *prev;
- struct type_pf_elem *data;
- void *tmp;
- int i = 0, j = 0;
- u32 hash = JHASH2(value, h->initval, ht->htable_bits);
-
- slist_for_each_prev(prev, n, &ht->htable[hash]) {
- for (i = 0; i < h->array_size; i++) {
- data = chash_tdata(n, i);
- if (type_pf_data_isnull(data)) {
- tmp = n;
- goto found;
- }
- }
- j++;
- }
- if (j < h->chain_limit) {
- tmp = kzalloc(h->array_size * sizeof(struct type_pf_telem)
- + sizeof(struct slist), gfp_flags);
- if (!tmp)
- return -ENOMEM;
- prev->next = (struct slist *) tmp;
- data = chash_tdata(tmp, 0);
- } else {
- /* Trigger rehashing */
- return -EAGAIN;
- }
-found:
- type_pf_data_copy(data, value);
- type_pf_data_timeout_set(data, timeout);
- return 0;
-}
-
-static void
-type_pf_chash_del_telem(struct chash *h, struct slist *prev,
- struct slist *n, int i)
-{
- struct type_pf_elem *d, *data = chash_tdata(n, i);
- struct slist *tmp;
- int j; /* Index in array */
-
- pr_debug("del %u", i);
- if (n->next != NULL) {
- for (prev = n, tmp = n->next;
- tmp->next != NULL;
- prev = tmp, tmp = tmp->next)
- /* Find last array */;
- j = 0;
- } else {
- /* Already at last array */
- tmp = n;
- j = i;
- }
- /* Find last non-empty element */
- for (; j < h->array_size - 1; j++)
- if (type_pf_data_isnull(chash_tdata(tmp, j + 1)))
- break;
-
- d = chash_tdata(tmp, j);
- if (!(tmp == n && i == j)) {
- type_pf_data_swap(data, d);
- type_pf_data_swap_timeout(data, d);
- }
-#ifdef IP_SET_HASH_WITH_NETS
- del_cidr(h, data->cidr, HOST_MASK);
-#endif
- if (j == 0) {
- prev->next = NULL;
- kfree(tmp);
- } else
- type_pf_data_zero_out(d);
-
- h->elements--;
-}
-
-/* Delete expired elements from the hashtable */
-static void
-type_pf_chash_expire(struct chash *h)
-{
- struct htable *ht = h->table;
- struct slist *n, *prev;
- struct type_pf_elem *data;
- u32 i;
- int j;
-
- for (i = 0; i < jhash_size(ht->htable_bits); i++)
- slist_for_each_prev(prev, n, &ht->htable[i])
- for (j = 0; j < h->array_size; j++) {
- data = chash_tdata(n, j);
- if (type_pf_data_isnull(data))
- break;
- if (type_pf_data_expired(data)) {
- pr_debug("expire %u/%u", i, j);
- type_pf_chash_del_telem(h, prev, n, j);
- }
- }
-}
-
-static int
-type_pf_tresize(struct ip_set *set, gfp_t gfp_flags, bool retried)
-{
- struct chash *h = set->data;
- struct htable *ht, *orig = h->table;
- u8 htable_bits = orig->htable_bits;
- struct slist *n;
- const struct type_pf_elem *data;
- u32 i, j;
- int ret;
-
- /* Try to cleanup once */
- if (!retried) {
- i = h->elements;
- write_lock_bh(&set->lock);
- type_pf_chash_expire(set->data);
- write_unlock_bh(&set->lock);
- if (h->elements < i)
- return 0;
- }
-
-retry:
- ret = i = 0;
- htable_bits++;
- if (!htable_bits)
- /* In case we have plenty of memory :-) */
- return -IPSET_ERR_HASH_FULL;
- ht = ip_set_alloc(sizeof(*ht)
- + jhash_size(htable_bits) * sizeof(struct slist),
- GFP_KERNEL);
- if (!ht)
- return -ENOMEM;
- ht->htable_bits = htable_bits;
-
- read_lock_bh(&set->lock);
-next_slot:
- for (; i < jhash_size(orig->htable_bits); i++) {
- slist_for_each(n, &orig->htable[i]) {
- for (j = 0; j < h->array_size; j++) {
- data = chash_tdata(n, j);
- if (type_pf_data_isnull(data)) {
- i++;
- goto next_slot;
- }
- ret = type_pf_chash_treadd(h, ht,
- data, gfp_flags,
- type_pf_data_timeout(data));
- if (ret < 0) {
- read_unlock_bh(&set->lock);
- chash_destroy(ht);
- if (ret == -EAGAIN)
- goto retry;
- return ret;
- }
- }
- }
- }
-
- h->table = ht;
- read_unlock_bh(&set->lock);
-
- /* Give time to other users of the set */
- synchronize_net();
-
- chash_destroy(orig);
-
- return 0;
-}
-
-static int
-type_pf_chash_tadd(struct ip_set *set, void *value,
- gfp_t gfp_flags, u32 timeout)
-{
- struct chash *h = set->data;
- const struct type_pf_elem *d = value;
- struct slist *n, *prev;
- struct htable *ht = h->table;
- struct type_pf_elem *data;
- void *tmp;
- int i = 0, j = 0;
- u32 hash;
-
- if (h->elements >= h->maxelem)
- /* FIXME: when set is full, we slow down here */
- type_pf_chash_expire(h);
- if (h->elements >= h->maxelem)
- return -IPSET_ERR_HASH_FULL;
-
- hash = JHASH2(d, h->initval, ht->htable_bits);
- slist_for_each_prev(prev, n, &ht->htable[hash]) {
- for (i = 0; i < h->array_size; i++) {
- data = chash_tdata(n, i);
- if (type_pf_data_isnull(data)
- || type_pf_data_expired(data)) {
- tmp = n;
- goto found;
- }
- if (type_pf_data_equal(data, d))
- return -IPSET_ERR_EXIST;
- }
- j++;
- }
- if (j < h->chain_limit) {
- tmp = kzalloc(h->array_size * sizeof(struct type_pf_telem)
- + sizeof(struct slist), gfp_flags);
- if (!tmp)
- return -ENOMEM;
- prev->next = (struct slist *) tmp;
- data = chash_tdata(tmp, 0);
- } else {
- /* Rehashing */
- return -EAGAIN;
- }
-found:
- if (type_pf_data_isnull(data))
- h->elements++;
-#ifdef IP_SET_HASH_WITH_NETS
- 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;
-}
-
-static int
-type_pf_chash_tdel(struct ip_set *set, void *value,
- gfp_t gfp_flags, u32 timeout)
-{
- struct chash *h = set->data;
- struct htable *ht = h->table;
- const struct type_pf_elem *d = value;
- struct slist *n, *prev;
- int i, ret = 0;
- struct type_pf_elem *data;
- u32 hash = JHASH2(value, h->initval, ht->htable_bits);
-
- slist_for_each_prev(prev, n, &ht->htable[hash])
- for (i = 0; i < h->array_size; i++) {
- data = chash_tdata(n, i);
- if (type_pf_data_isnull(data))
- return -IPSET_ERR_EXIST;
- if (type_pf_data_equal(data, d)) {
- if (type_pf_data_expired(data))
- ret = -IPSET_ERR_EXIST;
- type_pf_chash_del_telem(h, prev, n, i);
- return ret;
- }
- }
-
- return -IPSET_ERR_EXIST;
-}
-
-#ifdef IP_SET_HASH_WITH_NETS
-static inline int
-type_pf_chash_ttest_cidrs(struct ip_set *set,
- struct type_pf_elem *d,
- gfp_t gfp_flags, u32 timeout)
-{
- struct chash *h = set->data;
- struct htable *ht = h->table;
- struct type_pf_elem *data;
- struct slist *n;
- int i, j = 0;
- u32 hash;
- u8 host_mask = SET_HOST_MASK(set->family);
-
-retry:
- for (; j < host_mask && h->nets[j].cidr; j++) {
- type_pf_data_netmask(d, h->nets[j].cidr);
- hash = JHASH2(d, h->initval, ht->htable_bits);
- slist_for_each(n, &ht->htable[hash])
- for (i = 0; i < h->array_size; i++) {
- data = chash_tdata(n, i);
- if (type_pf_data_isnull(data)) {
- j++;
- goto retry;
- }
- if (type_pf_data_equal(data, d))
- return !type_pf_data_expired(data);
- }
- }
- return 0;
-}
-#endif
-
-static inline int
-type_pf_chash_ttest(struct ip_set *set, void *value,
- gfp_t gfp_flags, u32 timeout)
-{
- struct chash *h = set->data;
- struct htable *ht = h->table;
- struct type_pf_elem *data, *d = value;
- struct slist *n;
- int i;
- u32 hash;
-
-#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
- hash = JHASH2(d, h->initval, ht->htable_bits);
- slist_for_each(n, &ht->htable[hash])
- for (i = 0; i < h->array_size; i++) {
- data = chash_tdata(n, i);
- if (type_pf_data_isnull(data))
- return 0;
- if (type_pf_data_equal(data, d))
- return !type_pf_data_expired(data);
- }
- return 0;
-}
-
-static int
-type_pf_tlist(struct ip_set *set,
- struct sk_buff *skb, struct netlink_callback *cb)
-{
- const struct chash *h = set->data;
- const struct htable *ht = h->table;
- struct nlattr *atd, *nested;
- struct slist *n;
- const struct type_pf_elem *data;
- u32 first = cb->args[2];
- /* We assume that one hash bucket fills into one page */
- void *incomplete;
- int i;
-
- atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
- if (!atd)
- return -EFAULT;
- for (; cb->args[2] < jhash_size(ht->htable_bits); cb->args[2]++) {
- incomplete = skb_tail_pointer(skb);
- slist_for_each(n, &ht->htable[cb->args[2]]) {
- for (i = 0; i < h->array_size; i++) {
- data = chash_tdata(n, i);
- pr_debug("list %p %u", n, i);
- if (type_pf_data_isnull(data))
- break;
- if (type_pf_data_expired(data))
- continue;
- pr_debug("do list %p %u", n, i);
- nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
- if (!nested) {
- if (cb->args[2] == first) {
- nla_nest_cancel(skb, atd);
- return -EFAULT;
- } else
- goto nla_put_failure;
- }
- if (type_pf_data_tlist(skb, data))
- goto nla_put_failure;
- ipset_nest_end(skb, nested);
- }
- }
- }
- ipset_nest_end(skb, atd);
- /* Set listing finished */
- cb->args[2] = 0;
-
- return 0;
-
-nla_put_failure:
- nlmsg_trim(skb, incomplete);
- ipset_nest_end(skb, atd);
- if (unlikely(first == cb->args[2])) {
- pr_warn("Can't list set %s: one bucket does not fit into "
- "a message. Please report it!\n", set->name);
- cb->args[2] = 0;
- }
- return 0;
-}
-
-static const struct ip_set_type_variant type_pf_tvariant = {
- .kadt = type_pf_kadt,
- .uadt = type_pf_uadt,
- .adt = {
- [IPSET_ADD] = type_pf_chash_tadd,
- [IPSET_DEL] = type_pf_chash_tdel,
- [IPSET_TEST] = type_pf_chash_ttest,
- },
- .destroy = type_pf_destroy,
- .flush = type_pf_flush,
- .head = type_pf_head,
- .list = type_pf_tlist,
- .resize = type_pf_tresize,
- .same_set = type_pf_same_set,
-};
-
-static void
-type_pf_gc(unsigned long ul_set)
-{
- struct ip_set *set = (struct ip_set *) ul_set;
- struct chash *h = set->data;
-
- pr_debug("called");
- write_lock_bh(&set->lock);
- type_pf_chash_expire(h);
- write_unlock_bh(&set->lock);
-
- h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
- add_timer(&h->gc);
-}
-
-static inline void
-type_pf_gc_init(struct ip_set *set)
-{
- struct chash *h = set->data;
-
- init_timer(&h->gc);
- h->gc.data = (unsigned long) set;
- h->gc.function = type_pf_gc;
- h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
- add_timer(&h->gc);
- pr_debug("gc initialized, run in every %u",
- IPSET_GC_PERIOD(h->timeout));
-}
-
-#undef type_pf_data_equal
-#undef type_pf_data_isnull
-#undef type_pf_data_copy
-#undef type_pf_data_swap
-#undef type_pf_data_zero_out
-#undef type_pf_data_list
-#undef type_pf_data_tlist
-
-#undef type_pf_elem
-#undef type_pf_telem
-#undef type_pf_data_timeout
-#undef type_pf_data_expired
-#undef type_pf_data_swap_timeout
-#undef type_pf_data_netmask
-#undef type_pf_data_timeout_set
-
-#undef type_pf_chash_readd
-#undef type_pf_chash_del_elem
-#undef type_pf_chash_add
-#undef type_pf_chash_del
-#undef type_pf_chash_test_cidrs
-#undef type_pf_chash_test
-
-#undef type_pf_chash_treadd
-#undef type_pf_chash_del_telem
-#undef type_pf_chash_expire
-#undef type_pf_chash_tadd
-#undef type_pf_chash_tdel
-#undef type_pf_chash_ttest_cidrs
-#undef type_pf_chash_ttest
-
-#undef type_pf_resize
-#undef type_pf_tresize
-#undef type_pf_flush
-#undef type_pf_destroy
-#undef type_pf_head
-#undef type_pf_list
-#undef type_pf_tlist
-#undef type_pf_same_set
-#undef type_pf_kadt
-#undef type_pf_uadt
-#undef type_pf_gc
-#undef type_pf_gc_init
-#undef type_pf_variant
-#undef type_pf_tvariant
#include <net/netlink.h>
#include <linux/netfilter.h>
+#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/nfnetlink.h>
#include "ip_set.h"
#include <net/genetlink.h>
list_for_each_entry_rcu(type, &ip_set_type_list, list)
if (STREQ(type->name, name) &&
(type->family == family || type->family == AF_UNSPEC) &&
- type->revision == revision)
+ revision >= type->revision_min &&
+ revision <= type->revision_max)
return type;
return NULL;
}
if (STREQ(type->name, name) &&
(type->family == family || type->family == AF_UNSPEC)) {
found = true;
- if (type->revision < *min)
- *min = type->revision;
- if (type->revision > *max)
- *max = type->revision;
+ if (type->revision_min < *min)
+ *min = type->revision_min;
+ if (type->revision_max > *max)
+ *max = type->revision_max;
}
rcu_read_unlock();
if (found)
int ret = 0;
if (type->protocol != IPSET_PROTOCOL) {
- pr_warning("ip_set type %s, family %s, revision %u uses "
+ pr_warning("ip_set type %s, family %s, revision %u:%u uses "
"wrong protocol version %u (want %u)\n",
type->name, family_name(type->family),
- type->revision, type->protocol, IPSET_PROTOCOL);
+ type->revision_min, type->revision_max,
+ type->protocol, IPSET_PROTOCOL);
return -EINVAL;
}
ip_set_type_lock();
- if (find_set_type(type->name, type->family, type->revision)) {
+ if (find_set_type(type->name, type->family, type->revision_min)) {
/* Duplicate! */
- pr_warning("ip_set type %s, family %s, revision %u "
+ pr_warning("ip_set type %s, family %s with revision min %u "
"already registered!\n", type->name,
- family_name(type->family), type->revision);
+ family_name(type->family), type->revision_min);
ret = -EINVAL;
goto unlock;
}
list_add_rcu(&type->list, &ip_set_type_list);
- pr_debug("type %s, family %s, revision %u registered.\n",
- type->name, family_name(type->family), type->revision);
+ pr_debug("type %s, family %s, revision %u:%u registered.\n",
+ type->name, family_name(type->family),
+ type->revision_min, type->revision_max);
unlock:
ip_set_type_unlock();
return ret;
ip_set_type_unregister(struct ip_set_type *type)
{
ip_set_type_lock();
- if (!find_set_type(type->name, type->family, type->revision)) {
- pr_warning("ip_set type %s, family %s, revision %u "
+ if (!find_set_type(type->name, type->family, type->revision_min)) {
+ pr_warning("ip_set type %s, family %s with revision min %u "
"not registered\n", type->name,
- family_name(type->family), type->revision);
+ family_name(type->family), type->revision_min);
goto unlock;
}
list_del_rcu(&type->list);
- pr_debug("type %s, family %s, revision %u unregistered.\n",
- type->name, family_name(type->family), type->revision);
+ pr_debug("type %s, family %s with revision min %u unregistered.\n",
+ type->name, family_name(type->family), type->revision_min);
unlock:
ip_set_type_unlock();
int
ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
- u8 family, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ const struct ip_set_adt_opt *opt)
{
struct ip_set *set = ip_set_list[index];
int ret = 0;
BUG_ON(set == NULL);
pr_debug("set %s, index %u\n", set->name, index);
- if (dim < set->type->dimension ||
- !(family == set->family || set->family == AF_UNSPEC))
+ if (opt->dim < set->type->dimension ||
+ !(opt->family == set->family || set->family == AF_UNSPEC))
return 0;
read_lock_bh(&set->lock);
- ret = set->variant->kadt(set, skb, IPSET_TEST, family, dim, flags);
+ ret = set->variant->kadt(set, skb, par, IPSET_TEST, opt);
read_unlock_bh(&set->lock);
if (ret == -EAGAIN) {
/* Type requests element to be completed */
pr_debug("element must be competed, ADD is triggered\n");
write_lock_bh(&set->lock);
- set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags);
+ set->variant->kadt(set, skb, par, IPSET_ADD, opt);
write_unlock_bh(&set->lock);
ret = 1;
}
int
ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
- u8 family, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ const struct ip_set_adt_opt *opt)
{
struct ip_set *set = ip_set_list[index];
int ret;
BUG_ON(set == NULL);
pr_debug("set %s, index %u\n", set->name, index);
- if (dim < set->type->dimension ||
- !(family == set->family || set->family == AF_UNSPEC))
+ if (opt->dim < set->type->dimension ||
+ !(opt->family == set->family || set->family == AF_UNSPEC))
return 0;
write_lock_bh(&set->lock);
- ret = set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags);
+ ret = set->variant->kadt(set, skb, par, IPSET_ADD, opt);
write_unlock_bh(&set->lock);
return ret;
int
ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
- u8 family, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ const struct ip_set_adt_opt *opt)
{
struct ip_set *set = ip_set_list[index];
int ret = 0;
BUG_ON(set == NULL);
pr_debug("set %s, index %u\n", set->name, index);
- if (dim < set->type->dimension ||
- !(family == set->family || set->family == AF_UNSPEC))
+ if (opt->dim < set->type->dimension ||
+ !(opt->family == set->family || set->family == AF_UNSPEC))
return 0;
write_lock_bh(&set->lock);
- ret = set->variant->kadt(set, skb, IPSET_DEL, family, dim, flags);
+ ret = set->variant->kadt(set, skb, par, IPSET_DEL, opt);
write_unlock_bh(&set->lock);
return ret;
rwlock_init(&set->lock);
strlcpy(set->name, name, IPSET_MAXNAMELEN);
set->family = family;
+ set->revision = revision;
/*
* Next, check that we know the type, and take
(flags & IPSET_FLAG_EXIST) &&
STREQ(set->type->name, clash->type->name) &&
set->type->family == clash->type->family &&
- set->type->revision == clash->type->revision &&
+ set->type->revision_min == clash->type->revision_min &&
+ set->type->revision_max == clash->type->revision_max &&
set->variant->same_set(set, clash))
ret = 0;
goto cleanup;
if (!attr[IPSET_ATTR_SETNAME]) {
for (i = 0; i < ip_set_max; i++) {
if (ip_set_list[i] != NULL && ip_set_list[i]->ref) {
- ret = IPSET_ERR_BUSY;
+ ret = -IPSET_ERR_BUSY;
goto out;
}
}
ip_set_id_t i;
if (unlikely(protocol_failed(attr)))
- return -EPROTO;
+ return -IPSET_ERR_PROTOCOL;
if (!attr[IPSET_ATTR_SETNAME]) {
for (i = 0; i < ip_set_max; i++)
/* List/save set data */
-#define DUMP_INIT 0L
-#define DUMP_ALL 1L
-#define DUMP_ONE 2L
-#define DUMP_LAST 3L
+#define DUMP_INIT 0
+#define DUMP_ALL 1
+#define DUMP_ONE 2
+#define DUMP_LAST 3
+
+#define DUMP_TYPE(arg) (((u32)(arg)) & 0x0000FFFF)
+#define DUMP_FLAGS(arg) (((u32)(arg)) >> 16)
static int
ip_set_dump_done(struct netlink_callback *cb)
int min_len = NLMSG_SPACE(sizeof(struct genlmsghdr));
struct nlattr *cda[IPSET_ATTR_CMD_MAX+1];
struct nlattr *attr = (void *)nlh + min_len;
+ u32 dump_type;
ip_set_id_t index;
/* Second pass, so parser can't fail */
* [..]: type specific
*/
- if (!cda[IPSET_ATTR_SETNAME]) {
- cb->args[0] = DUMP_ALL;
- return 0;
- }
+ if (cda[IPSET_ATTR_SETNAME]) {
+ index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME]));
+ if (index == IPSET_INVALID_ID)
+ return -ENOENT;
- index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME]));
- if (index == IPSET_INVALID_ID)
- return -ENOENT;
+ dump_type = DUMP_ONE;
+ cb->args[1] = index;
+ } else
+ dump_type = DUMP_ALL;
+
+ if (cda[IPSET_ATTR_FLAGS]) {
+ u32 f = ip_set_get_h32(cda[IPSET_ATTR_FLAGS]);
+ dump_type |= (f << 16);
+ }
+ cb->args[0] = dump_type;
- cb->args[0] = DUMP_ONE;
- cb->args[1] = index;
return 0;
}
struct ip_set *set = NULL;
struct genlmsg_buf *nlh = NULL;
unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0;
+ u32 dump_type, dump_flags;
int ret = 0;
- if (cb->args[0] == DUMP_INIT) {
+ if (!cb->args[0]) {
ret = dump_init(cb);
if (ret < 0) {
struct nlmsghdr *nlh = nlmsg_hdr(cb->skb);
if (cb->args[1] >= ip_set_max)
goto out;
- pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]);
- max = cb->args[0] == DUMP_ONE ? cb->args[1] + 1 : ip_set_max;
+ dump_type = DUMP_TYPE(cb->args[0]);
+ dump_flags = DUMP_FLAGS(cb->args[0]);
+ max = dump_type == DUMP_ONE ? cb->args[1] + 1 : ip_set_max;
+dump_last:
+ pr_debug("args[0]: %u %u args[1]: %ld\n",
+ dump_type, dump_flags, cb->args[1]);
for (; cb->args[1] < max; cb->args[1]++) {
index = (ip_set_id_t) cb->args[1];
set = ip_set_list[index];
if (set == NULL) {
- if (cb->args[0] == DUMP_ONE) {
+ if (dump_type == DUMP_ONE) {
ret = -ENOENT;
goto out;
}
/* When dumping all sets, we must dump "sorted"
* so that lists (unions of sets) are dumped last.
*/
- if (cb->args[0] != DUMP_ONE &&
- !((cb->args[0] == DUMP_ALL) ^
- (set->type->features & IPSET_DUMP_LAST)))
+ if (dump_type != DUMP_ONE &&
+ ((dump_type == DUMP_ALL) ==
+ !!(set->type->features & IPSET_DUMP_LAST)))
continue;
pr_debug("List set: %s\n", set->name);
if (!cb->args[2]) {
}
NLA_PUT_U8(skb, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL);
NLA_PUT_STRING(skb, IPSET_ATTR_SETNAME, set->name);
+ if (dump_flags & IPSET_FLAG_LIST_SETNAME)
+ goto next_set;
switch (cb->args[2]) {
case 0:
/* Core header data */
NLA_PUT_U8(skb, IPSET_ATTR_FAMILY,
set->family);
NLA_PUT_U8(skb, IPSET_ATTR_REVISION,
- set->type->revision);
+ set->revision);
ret = set->variant->head(set, skb);
if (ret < 0)
goto release_refcount;
+ if (dump_flags & IPSET_FLAG_LIST_HEADER)
+ goto next_set;
/* Fall through and add elements */
default:
read_lock_bh(&set->lock);
ret = set->variant->list(set, skb, cb);
read_unlock_bh(&set->lock);
- if (!cb->args[2]) {
+ if (!cb->args[2])
/* Set is done, proceed with next one */
- if (cb->args[0] == DUMP_ONE)
- cb->args[1] = IPSET_INVALID_ID;
- else
- cb->args[1]++;
- }
+ goto next_set;
goto release_refcount;
}
}
+ /* If we dump all sets, continue with dumping last ones */
+ if (dump_type == DUMP_ALL) {
+ dump_type = DUMP_LAST;
+ cb->args[0] = dump_type | (dump_flags << 16);
+ cb->args[1] = 0;
+ goto dump_last;
+ }
goto out;
nla_put_failure:
ret = -EFAULT;
+next_set:
+ if (dump_type == DUMP_ONE)
+ cb->args[1] = IPSET_INVALID_ID;
+ else
+ cb->args[1]++;
release_refcount:
/* If there was an error or set is done, release set */
if (ret || !cb->args[2]) {
pr_debug("release set %s\n", ip_set_list[index]->name);
ip_set_put_byindex(index);
}
-
- /* If we dump all sets, continue with dumping last ones */
- if (cb->args[0] == DUMP_ALL && cb->args[1] >= max && !cb->args[2])
- cb->args[0] = DUMP_LAST;
-
out:
if (nlh) {
genlmsg_end(skb, nlh);
struct nlattr *tb[], enum ipset_adt adt,
u32 flags, bool use_lineno)
{
- int ret, retried = 0;
+ int ret;
u32 lineno = 0;
- bool eexist = flags & IPSET_FLAG_EXIST;
+ bool eexist = flags & IPSET_FLAG_EXIST, retried = false;
do {
write_lock_bh(&set->lock);
- ret = set->variant->uadt(set, tb, adt, &lineno, flags);
+ ret = set->variant->uadt(set, tb, adt, &lineno, flags, retried);
write_unlock_bh(&set->lock);
+ retried = true;
} while (ret == -EAGAIN &&
set->variant->resize &&
- (ret = set->variant->resize(set, retried++)) == 0);
+ (ret = set->variant->resize(set, retried)) == 0);
if (!ret || (ret == -IPSET_ERR_EXIST && eexist))
return 0;
return -IPSET_ERR_PROTOCOL;
read_lock_bh(&set->lock);
- ret = set->variant->uadt(set, tb, IPSET_TEST, NULL, 0);
+ ret = set->variant->uadt(set, tb, IPSET_TEST, NULL, 0, 0);
read_unlock_bh(&set->lock);
/* Userspace can't trigger element to be re-added */
if (ret == -EAGAIN)
NLA_PUT_STRING(skb2, IPSET_ATTR_SETNAME, set->name);
NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, set->type->name);
NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->family);
- NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->type->revision);
+ NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->revision);
genlmsg_end(skb2, nlh2);
ret = genlmsg_unicast(genl_info_net(info), skb2, NETLINK_CB(skb).pid);
IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO,
+ /* Range not supported */
+ IPSET_ERR_HASH_RANGE_UNSUPPORTED,
+ /* Invalid range */
+ IPSET_ERR_HASH_RANGE,
};
#ifdef __KERNEL__
#define HOST_MASK 32
#include "ip_set_ahash.h"
+static inline void
+hash_ip4_data_next(struct ip_set_hash *h, const struct hash_ip4_elem *d)
+{
+ h->next.ip = ntohl(d->ip);
+}
+
static int
hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
__be32 ip;
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip);
+ ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
ip &= ip_set_netmask(h->netmask);
if (ip == 0)
return -EINVAL;
- return adtfn(set, &ip, h->timeout, flags);
+ return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
- ip &= ip_set_hostmask(cidr);
- ip_to = ip | ~ip_set_hostmask(cidr);
+ ip_set_mask_from_to(ip, ip_to, cidr);
} else
ip_to = ip;
hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
+ if (retried)
+ ip = h->next.ip;
for (; !before(ip_to, ip); ip += hosts) {
nip = htonl(ip);
if (nip == 0)
#define HOST_MASK 128
#include "ip_set_ahash.h"
+static inline void
+hash_ip6_data_next(struct ip_set_hash *h, const struct hash_ip6_elem *d)
+{
+}
+
static int
hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip;
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6);
+ ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip.in6);
ip6_netmask(&ip, h->netmask);
if (ipv6_addr_any(&ip.in6))
return -EINVAL;
- return adtfn(set, &ip, h->timeout, flags);
+ return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags);
}
static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
static int
hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
- .revision = 0,
+ .revision_min = 0,
+ .revision_max = 0,
.create = hash_ip_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
#define HOST_MASK 32
#include "ip_set_ahash.h"
+static inline void
+hash_ipport4_data_next(struct ip_set_hash *h,
+ const struct hash_ipport4_elem *d)
+{
+ h->next.ip = ntohl(d->ip);
+ h->next.port = ntohs(d->port);
+}
+
static int
hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
- if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
+ if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
+ ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
- u32 ip, ip_to, p, port, port_to;
+ u32 ip, ip_to, p = 0, port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
- ip &= ip_set_hostmask(cidr);
- ip_to = ip | ~ip_set_hostmask(cidr);
+ ip_set_mask_from_to(ip, ip_to, cidr);
} else
ip_to = ip;
swap(port, port_to);
}
- for (; !before(ip_to, ip); ip++)
- for (p = port; p <= port_to; p++) {
+ if (retried)
+ ip = h->next.ip;
+ for (; !before(ip_to, ip); ip++) {
+ p = retried && ip == h->next.ip ? h->next.port : port;
+ for (; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout, flags);
else
ret = 0;
}
+ }
return ret;
}
#define HOST_MASK 128
#include "ip_set_ahash.h"
+static inline void
+hash_ipport6_data_next(struct ip_set_hash *h,
+ const struct hash_ipport6_elem *d)
+{
+ h->next.port = ntohs(d->port);
+}
+
static int
hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { };
- if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
+ if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
if (port > port_to)
swap(port, port_to);
+ if (retried)
+ port = h->next.port;
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout, flags);
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC,
- .revision = 1,
+ .revision_min = 0,
+ .revision_max = 1, /* SCTP and UDPLITE support added */
.create = hash_ipport_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
#define HOST_MASK 32
#include "ip_set_ahash.h"
+static inline void
+hash_ipportip4_data_next(struct ip_set_hash *h,
+ const struct hash_ipportip4_elem *d)
+{
+ h->next.ip = ntohl(d->ip);
+ h->next.port = ntohs(d->port);
+}
+
static int
hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
- if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
+ if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
- ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
+ ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
+ ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
- u32 ip, ip_to, p, port, port_to;
+ u32 ip, ip_to, p = 0, port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
- ip &= ip_set_hostmask(cidr);
- ip_to = ip | ~ip_set_hostmask(cidr);
+ ip_set_mask_from_to(ip, ip_to, cidr);
} else
ip_to = ip;
swap(port, port_to);
}
- for (; !before(ip_to, ip); ip++)
- for (p = port; p <= port_to; p++) {
+ if (retried)
+ ip = h->next.ip;
+ for (; !before(ip_to, ip); ip++) {
+ p = retried && ip == h->next.ip ? h->next.port : port;
+ for (; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout, flags);
else
ret = 0;
}
+ }
return ret;
}
#define HOST_MASK 128
#include "ip_set_ahash.h"
+static inline void
+hash_ipportip6_data_next(struct ip_set_hash *h,
+ const struct hash_ipportip6_elem *d)
+{
+ h->next.port = ntohs(d->port);
+}
+
static int
hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { };
- if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
+ if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
- ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
+ ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
if (port > port_to)
swap(port, port_to);
+ if (retried)
+ port = h->next.port;
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout, flags);
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC,
- .revision = 1,
+ .revision_min = 0,
+ .revision_max = 1, /* SCTP and UDPLITE support added */
.create = hash_ipportip_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
#define HOST_MASK 32
#include "ip_set_ahash.h"
+static inline void
+hash_ipportnet4_data_next(struct ip_set_hash *h,
+ const struct hash_ipportnet4_elem *d)
+{
+ h->next.ip = ntohl(d->ip);
+ h->next.port = ntohs(d->port);
+ h->next.ip2 = ntohl(d->ip2);
+}
+
static int
hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportnet4_elem data =
- { .cidr = h->nets[0].cidr || HOST_MASK };
+ struct hash_ipportnet4_elem data = {
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
- if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
+ if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
- ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
+ ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
+ ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
data.ip2 &= ip_set_netmask(data.cidr);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
- u32 ip, ip_to, p, port, port_to;
+ u32 ip, ip_to, p = 0, port, port_to;
+ u32 ip2_from = 0, ip2_to, ip2_last, ip2;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
- ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2);
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from);
if (ret)
return ret;
- if (tb[IPSET_ATTR_CIDR2])
+ if (tb[IPSET_ATTR_CIDR2]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
-
- if (!data.cidr)
- return -IPSET_ERR_INVALID_CIDR;
-
- data.ip2 &= ip_set_netmask(data.cidr);
+ if (!data.cidr)
+ return -IPSET_ERR_INVALID_CIDR;
+ }
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
+ with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
if (adt == IPSET_TEST ||
- !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
- tb[IPSET_ATTR_PORT_TO])) {
+ !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
+ tb[IPSET_ATTR_IP2_TO])) {
+ data.ip = htonl(ip);
+ data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
- ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
- ip &= ip_set_hostmask(cidr);
- ip_to = ip | ~ip_set_hostmask(cidr);
- } else
- ip_to = ip;
+ ip_set_mask_from_to(ip, ip_to, cidr);
+ }
port_to = port = ntohs(data.port);
- if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
+ if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
}
+ if (tb[IPSET_ATTR_IP2_TO]) {
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
+ if (ret)
+ return ret;
+ if (ip2_from > ip2_to)
+ swap(ip2_from, ip2_to);
+ if (ip2_from + UINT_MAX == ip2_to)
+ return -IPSET_ERR_HASH_RANGE;
+ } else {
+ ip_set_mask_from_to(ip2_from, ip2_to, data.cidr);
+ }
- for (; !before(ip_to, ip); ip++)
- for (p = port; p <= port_to; p++) {
- data.ip = htonl(ip);
+ if (retried)
+ ip = h->next.ip;
+ for (; !before(ip_to, ip); ip++) {
+ data.ip = htonl(ip);
+ p = retried && ip == h->next.ip ? h->next.port : port;
+ for (; p <= port_to; p++) {
data.port = htons(p);
- ret = adtfn(set, &data, timeout, flags);
-
- if (ret && !ip_set_eexist(ret, flags))
- return ret;
- else
- ret = 0;
+ ip2 = retried && ip == h->next.ip && p == h->next.port
+ ? h->next.ip2 : ip2_from;
+ while (!after(ip2, ip2_to)) {
+ data.ip2 = htonl(ip2);
+ ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
+ &data.cidr);
+ ret = adtfn(set, &data, timeout, flags);
+
+ if (ret && !ip_set_eexist(ret, flags))
+ return ret;
+ else
+ ret = 0;
+ ip2 = ip2_last + 1;
+ }
}
+ }
return ret;
}
#define HOST_MASK 128
#include "ip_set_ahash.h"
+static inline void
+hash_ipportnet6_data_next(struct ip_set_hash *h,
+ const struct hash_ipportnet6_elem *d)
+{
+ h->next.port = ntohs(d->port);
+}
+
static int
hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportnet6_elem data =
- { .cidr = h->nets[0].cidr || HOST_MASK };
+ struct hash_ipportnet6_elem data = {
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
- if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
+ if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
- ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
+ ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
ip6_netmask(&data.ip2, data.cidr);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
+ if (unlikely(tb[IPSET_ATTR_IP_TO]))
+ return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
if (port > port_to)
swap(port, port_to);
+ if (retried)
+ port = h->next.port;
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout, flags);
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC,
- .revision = 1,
+ .revision_min = 0,
+ /* 1 SCTP and UDPLITE support added */
+ .revision_max = 2, /* Range as input support for IPv4 added */
.create = hash_ipportnet_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
#define HOST_MASK 32
#include "ip_set_ahash.h"
+static inline void
+hash_net4_data_next(struct ip_set_hash *h,
+ const struct hash_net4_elem *d)
+{
+ h->next.ip = ntohl(d->ip);
+}
+
static int
hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_net4_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
+ struct hash_net4_elem data = {
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
+ ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
+ u32 ip = 0, ip_to, last;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
- if (tb[IPSET_ATTR_CIDR])
+ if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-
- if (!data.cidr)
- return -IPSET_ERR_INVALID_CIDR;
-
- data.ip &= ip_set_netmask(data.cidr);
+ if (!data.cidr)
+ return -IPSET_ERR_INVALID_CIDR;
+ }
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
+
+ if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
+ data.ip = htonl(ip & ip_set_hostmask(data.cidr));
+ ret = adtfn(set, &data, timeout, flags);
+ return ip_set_eexist(ret, flags) ? 0 : ret;
+ }
- ret = adtfn(set, &data, timeout, flags);
-
- return ip_set_eexist(ret, flags) ? 0 : ret;
+ ip_to = ip;
+ if (tb[IPSET_ATTR_IP_TO]) {
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+ if (ret)
+ return ret;
+ if (ip_to < ip)
+ swap(ip, ip_to);
+ if (ip + UINT_MAX == ip_to)
+ return -IPSET_ERR_HASH_RANGE;
+ }
+ if (retried)
+ ip = h->next.ip;
+ while (!after(ip, ip_to)) {
+ data.ip = htonl(ip);
+ last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
+ ret = adtfn(set, &data, timeout, flags);
+ if (ret && !ip_set_eexist(ret, flags))
+ return ret;
+ else
+ ret = 0;
+ ip = last + 1;
+ }
+ return ret;
}
static bool
#define HOST_MASK 128
#include "ip_set_ahash.h"
+static inline void
+hash_net6_data_next(struct ip_set_hash *h,
+ const struct hash_net6_elem *d)
+{
+}
+
static int
hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_net6_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
+ struct hash_net6_elem data = {
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
+ if (unlikely(tb[IPSET_ATTR_IP_TO]))
+ return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
- .revision = 0,
+ .revision_min = 0,
+ .revision_max = 1, /* Range as input support for IPv4 added */
.create = hash_net_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
#define HOST_MASK 32
#include "ip_set_ahash.h"
+static inline void
+hash_netport4_data_next(struct ip_set_hash *h,
+ const struct hash_netport4_elem *d)
+{
+ h->next.ip = ntohl(d->ip);
+ h->next.port = ntohs(d->port);
+}
+
static int
hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = {
- .cidr = h->nets[0].cidr || HOST_MASK };
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
- if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
+ if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
+ ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = { .cidr = HOST_MASK };
- u32 port, port_to;
+ u32 port, port_to, p = 0, ip = 0, ip_to, last;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
if (ret)
return ret;
- if (tb[IPSET_ATTR_CIDR])
+ if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
- if (!data.cidr)
- return -IPSET_ERR_INVALID_CIDR;
- data.ip &= ip_set_netmask(data.cidr);
+ if (!data.cidr)
+ return -IPSET_ERR_INVALID_CIDR;
+ }
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
+ with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
+ if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
+ data.ip = htonl(ip & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
- port = ntohs(data.port);
- port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
- if (port > port_to)
- swap(port, port_to);
-
- for (; port <= port_to; port++) {
- data.port = htons(port);
- ret = adtfn(set, &data, timeout, flags);
-
- if (ret && !ip_set_eexist(ret, flags))
+ port = port_to = ntohs(data.port);
+ if (tb[IPSET_ATTR_PORT_TO]) {
+ port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
+ if (port_to < port)
+ swap(port, port_to);
+ }
+ if (tb[IPSET_ATTR_IP_TO]) {
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+ if (ret)
return ret;
- else
- ret = 0;
+ if (ip_to < ip)
+ swap(ip, ip_to);
+ if (ip + UINT_MAX == ip_to)
+ return -IPSET_ERR_HASH_RANGE;
+ } else {
+ ip_set_mask_from_to(ip, ip_to, data.cidr);
+ }
+
+ if (retried)
+ ip = h->next.ip;
+ while (!after(ip, ip_to)) {
+ data.ip = htonl(ip);
+ last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
+ p = retried && ip == h->next.ip ? h->next.port : port;
+ for (; p <= port_to; p++) {
+ data.port = htons(p);
+ ret = adtfn(set, &data, timeout, flags);
+
+ if (ret && !ip_set_eexist(ret, flags))
+ return ret;
+ else
+ ret = 0;
+ }
+ ip = last + 1;
}
return ret;
}
#define HOST_MASK 128
#include "ip_set_ahash.h"
+static inline void
+hash_netport6_data_next(struct ip_set_hash *h,
+ const struct hash_netport6_elem *d)
+{
+ h->next.port = ntohs(d->port);
+}
+
static int
hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = {
- .cidr = h->nets[0].cidr || HOST_MASK };
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
- if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
+ if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
- return adtfn(set, &data, h->timeout, flags);
+ return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
static int
hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
+ if (unlikely(tb[IPSET_ATTR_IP_TO]))
+ return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
if (port > port_to)
swap(port, port_to);
+ if (retried)
+ port = h->next.port;
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout, flags);
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC,
- .revision = 1,
+ .revision_min = 0,
+ /* 1 SCTP and UDPLITE support added */
+ .revision_max = 2, /* Range as input support for IPv4 added */
.create = hash_netport_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
static int
list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+ const struct xt_action_param *par,
+ enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
struct list_set *map = set->data;
struct set_elem *elem;
continue;
switch (adt) {
case IPSET_TEST:
- ret = ip_set_test(elem->id, skb, pf, dim, flags);
+ ret = ip_set_test(elem->id, skb, par, opt);
if (ret > 0)
return ret;
break;
case IPSET_ADD:
- ret = ip_set_add(elem->id, skb, pf, dim, flags);
+ ret = ip_set_add(elem->id, skb, par, opt);
if (ret == 0)
return ret;
break;
case IPSET_DEL:
- ret = ip_set_del(elem->id, skb, pf, dim, flags);
+ ret = ip_set_del(elem->id, skb, par, opt);
if (ret == 0)
return ret;
break;
static int
list_set_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct list_set *map = set->data;
bool with_timeout = with_timeout(map->timeout);
.features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
- .revision = 0,
+ .revision_min = 0,
+ .revision_max = 0,
.create = list_set_create,
.create_policy = {
[IPSET_ATTR_SIZE] = { .type = NLA_U32 },
#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT)
+#define opt_timeout(opt, map) \
+ (with_timeout((opt)->timeout) ? (opt)->timeout : (map)->timeout)
+
static inline unsigned int
ip_set_timeout_uget(struct nlattr *tb)
{
{
return timeout != IPSET_ELEM_UNSET &&
(timeout == IPSET_ELEM_PERMANENT ||
- time_after(timeout, jiffies));
+ time_is_after_jiffies(timeout));
}
static inline bool
{
return timeout != IPSET_ELEM_UNSET &&
timeout != IPSET_ELEM_PERMANENT &&
- time_before(timeout, jiffies);
+ time_is_before_jiffies(timeout);
}
static inline unsigned long
if (!timeout)
return IPSET_ELEM_PERMANENT;
- t = timeout * HZ + jiffies;
+ t = msecs_to_jiffies(timeout * 1000) + jiffies;
if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT)
/* Bingo! */
t++;
static inline u32
ip_set_timeout_get(unsigned long timeout)
{
- return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
+ return timeout == IPSET_ELEM_PERMANENT ? 0 :
+ jiffies_to_msecs(timeout - jiffies)/1000;
}
#else
ip_set_timeout_test(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT ||
- time_after(timeout, jiffies);
+ time_is_after_jiffies(timeout);
}
static inline bool
ip_set_timeout_expired(unsigned long timeout)
{
return timeout != IPSET_ELEM_PERMANENT &&
- time_before(timeout, jiffies);
+ time_is_before_jiffies(timeout);
}
static inline unsigned long
if (!timeout)
return IPSET_ELEM_PERMANENT;
- t = timeout * HZ + jiffies;
+ t = msecs_to_jiffies(timeout * 1000) + jiffies;
if (t == IPSET_ELEM_PERMANENT)
/* Bingo! :-) */
t++;
static inline u32
ip_set_timeout_get(unsigned long timeout)
{
- return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
+ return timeout == IPSET_ELEM_PERMANENT ? 0 :
+ jiffies_to_msecs(timeout - jiffies)/1000;
}
#endif /* ! IP_SET_BITMAP_TIMEOUT */
/* ADT/LIST/SAVE */
struct {
union nf_inet_addr ip2;
+ union nf_inet_addr ip2_to;
uint8_t cidr2;
uint8_t proto;
char ether[ETH_ALEN];
return -1;
copy_addr(data->family, &data->adt.ip2, value);
break;
+ case IPSET_OPT_IP2_TO:
+ if (!(data->family == AF_INET || data->family == AF_INET6))
+ return -1;
+ copy_addr(data->family, &data->adt.ip2_to, value);
+ break;
case IPSET_OPT_CIDR2:
data->adt.cidr2 = *(const uint8_t *) value;
break;
return data->adt.nameref;
case IPSET_OPT_IP2:
return &data->adt.ip2;
+ case IPSET_OPT_IP2_TO:
+ return &data->adt.ip2_to;
case IPSET_OPT_CIDR2:
return &data->adt.cidr2;
case IPSET_OPT_PROTO:
case IPSET_OPT_IP:
case IPSET_OPT_IP_TO:
case IPSET_OPT_IP2:
+ case IPSET_OPT_IP2_TO:
return family == AF_INET ? sizeof(uint32_t)
: sizeof(struct in6_addr);
case IPSET_OPT_PORT:
--- /dev/null
+/* Copyright 2011 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <arpa/inet.h> /* inet_ntop */
+#include <libmnl/libmnl.h> /* libmnl backend */
+
+struct ipset_attrname {
+ const char *name;
+};
+
+static const struct ipset_attrname cmdattr2name[] = {
+ [IPSET_ATTR_PROTOCOL] = { .name = "PROTOCOL" },
+ [IPSET_ATTR_SETNAME] = { .name = "SETNAME" },
+ [IPSET_ATTR_TYPENAME] = { .name = "TYPENAME" },
+ [IPSET_ATTR_REVISION] = { .name = "REVISION" },
+ [IPSET_ATTR_FAMILY] = { .name = "FAMILY" },
+ [IPSET_ATTR_FLAGS] = { .name = "FLAGS" },
+ [IPSET_ATTR_DATA] = { .name = "DATA" },
+ [IPSET_ATTR_ADT] = { .name = "ADT" },
+ [IPSET_ATTR_LINENO] = { .name = "LINENO" },
+ [IPSET_ATTR_PROTOCOL_MIN] = { .name = "PROTO_MIN" },
+};
+
+static const struct ipset_attrname createattr2name[] = {
+ [IPSET_ATTR_IP] = { .name = "IP" },
+ [IPSET_ATTR_IP_TO] = { .name = "IP_TO" },
+ [IPSET_ATTR_CIDR] = { .name = "CIDR" },
+ [IPSET_ATTR_PORT] = { .name = "PORT" },
+ [IPSET_ATTR_PORT_TO] = { .name = "PORT_TO" },
+ [IPSET_ATTR_TIMEOUT] = { .name = "TIMEOUT" },
+ [IPSET_ATTR_PROTO] = { .name = "PROTO" },
+ [IPSET_ATTR_CADT_FLAGS] = { .name = "CADT_FLAGS" },
+ [IPSET_ATTR_CADT_LINENO] = { .name = "CADT_LINENO" },
+ [IPSET_ATTR_GC] = { .name = "GC" },
+ [IPSET_ATTR_HASHSIZE] = { .name = "HASHSIZE" },
+ [IPSET_ATTR_MAXELEM] = { .name = "MAXELEM" },
+ [IPSET_ATTR_NETMASK] = { .name = "NETMASK" },
+ [IPSET_ATTR_PROBES] = { .name = "PROBES" },
+ [IPSET_ATTR_RESIZE] = { .name = "RESIZE" },
+ [IPSET_ATTR_SIZE] = { .name = "SIZE" },
+ [IPSET_ATTR_ELEMENTS] = { .name = "ELEMENTS" },
+ [IPSET_ATTR_REFERENCES] = { .name = "REFERENCES" },
+ [IPSET_ATTR_MEMSIZE] = { .name = "MEMSIZE" },
+};
+
+static const struct ipset_attrname adtattr2name[] = {
+ [IPSET_ATTR_IP] = { .name = "IP" },
+ [IPSET_ATTR_IP_TO] = { .name = "IP_TO" },
+ [IPSET_ATTR_CIDR] = { .name = "CIDR" },
+ [IPSET_ATTR_PORT] = { .name = "PORT" },
+ [IPSET_ATTR_PORT_TO] = { .name = "PORT_TO" },
+ [IPSET_ATTR_TIMEOUT] = { .name = "TIMEOUT" },
+ [IPSET_ATTR_PROTO] = { .name = "PROTO" },
+ [IPSET_ATTR_CADT_FLAGS] = { .name = "CADT_FLAGS" },
+ [IPSET_ATTR_CADT_LINENO] = { .name = "CADT_LINENO" },
+ [IPSET_ATTR_ETHER] = { .name = "ETHER" },
+ [IPSET_ATTR_NAME] = { .name = "NAME" },
+ [IPSET_ATTR_NAMEREF] = { .name = "NAMEREF" },
+ [IPSET_ATTR_IP2] = { .name = "IP2" },
+ [IPSET_ATTR_CIDR2] = { .name = "CIDR2" },
+ [IPSET_ATTR_IP2_TO] = { .name = "IP2_TO" },
+};
+
+static void
+debug_cadt_attrs(int max, const struct ipset_attr_policy *policy,
+ const struct ipset_attrname attr2name[],
+ struct nlattr *nla[])
+{
+ uint32_t v;
+ int i;
+
+ fprintf(stderr,"\t\t%s attributes:\n", policy == create_attrs ? "CREATE" : "ADT");
+ for (i = IPSET_ATTR_UNSPEC + 1; i <= max; i++) {
+ if (!nla[i])
+ continue;
+ switch (policy[i].type) {
+ case MNL_TYPE_U8:
+ v = * (uint8_t *) mnl_attr_get_payload(nla[i]);
+ fprintf(stderr, "\t\t%s: %u\n",
+ attr2name[i].name, v);
+ break;
+ case MNL_TYPE_U16:
+ v = * (uint16_t *) mnl_attr_get_payload(nla[i]);
+ fprintf(stderr, "\t\t%s: %u\n",
+ attr2name[i].name, ntohs(v));
+ break;
+ case MNL_TYPE_U32:
+ v = * (uint32_t *) mnl_attr_get_payload(nla[i]);
+ fprintf(stderr, "\t\t%s: %u\n",
+ attr2name[i].name, ntohl(v));
+ break;
+ case MNL_TYPE_NUL_STRING:
+ fprintf(stderr, "\t\t%s: %s\n",
+ attr2name[i].name,
+ (const char *) mnl_attr_get_payload(nla[i]));
+ break;
+ case MNL_TYPE_NESTED: {
+ struct nlattr *ipattr[IPSET_ATTR_IPADDR_MAX+1] = {};
+ char addr[INET6_ADDRSTRLEN];
+ void *d;
+
+ if (mnl_attr_parse_nested(nla[i], ipaddr_attr_cb, ipattr) < 0) {
+ fprintf(stderr, "\t\tIPADDR: cannot validate and parse attributes\n");
+ continue;
+ }
+ if (ipattr[IPSET_ATTR_IPADDR_IPV4]) {
+ d = mnl_attr_get_payload(ipattr[IPSET_ATTR_IPADDR_IPV4]);
+
+ inet_ntop(AF_INET, d, addr, INET6_ADDRSTRLEN);
+ fprintf(stderr, "\t\t%s: %s\n",
+ attr2name[i].name, addr);
+ } else if (ipattr[IPSET_ATTR_IPADDR_IPV6]) {
+ d = mnl_attr_get_payload(ipattr[IPSET_ATTR_IPADDR_IPV6]);
+
+ inet_ntop(AF_INET6, d, addr, INET6_ADDRSTRLEN);
+ fprintf(stderr, "\t\t%s: %s\n",
+ attr2name[i].name, addr);
+ }
+ break;
+ }
+ default:
+ fprintf(stderr, "\t\t%s: unresolved!\n",
+ attr2name[i].name);
+ }
+ }
+}
+
+static void
+debug_cmd_attrs(int cmd, struct nlattr *nla[])
+{
+ struct nlattr *adt[IPSET_ATTR_ADT_MAX+1] = {};
+ struct nlattr *cattr[IPSET_ATTR_CREATE_MAX+1] = {};
+ uint32_t v;
+ int i;
+
+ fprintf(stderr,"\tCommand attributes:\n");
+ for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CMD_MAX; i++) {
+ if (!nla[i])
+ continue;
+ switch (cmd_attrs[i].type) {
+ case MNL_TYPE_U8:
+ v = * (uint8_t *) mnl_attr_get_payload(nla[i]);
+ fprintf(stderr, "\t%s: %u\n",
+ cmdattr2name[i].name, v);
+ break;
+ case MNL_TYPE_U16:
+ v = * (uint16_t *) mnl_attr_get_payload(nla[i]);
+ fprintf(stderr, "\t%s: %u\n",
+ cmdattr2name[i].name, ntohs(v));
+ break;
+ case MNL_TYPE_U32:
+ v = * (uint32_t *) mnl_attr_get_payload(nla[i]);
+ fprintf(stderr, "\t%s: %u\n",
+ cmdattr2name[i].name, ntohl(v));
+ break;
+ case MNL_TYPE_NUL_STRING:
+ fprintf(stderr, "\t%s: %s\n",
+ cmdattr2name[i].name,
+ (const char *) mnl_attr_get_payload(nla[i]));
+ break;
+ case MNL_TYPE_NESTED:
+ if (i == IPSET_ATTR_DATA) {
+ switch (cmd) {
+ case IPSET_CMD_ADD:
+ case IPSET_CMD_DEL:
+ case IPSET_CMD_TEST:
+ if (mnl_attr_parse_nested(nla[i], adt_attr_cb, adt) < 0) {
+ fprintf(stderr, "\tADT: cannot validate and parse attributes\n");
+ continue;
+ }
+ debug_cadt_attrs(IPSET_ATTR_ADT_MAX,
+ adt_attrs,
+ adtattr2name,
+ adt);
+ break;
+ default:
+ if (mnl_attr_parse_nested(nla[i], create_attr_cb, cattr) < 0) {
+ fprintf(stderr, "\tCREATE: cannot validate and parse attributes\n");
+ continue;
+ }
+ debug_cadt_attrs(IPSET_ATTR_CREATE_MAX,
+ create_attrs,
+ createattr2name,
+ cattr);
+ }
+ } else {
+ struct nlattr *tb;
+ mnl_attr_for_each_nested(tb, nla[i]) {
+ memset(adt, 0, sizeof(adt));
+ if (mnl_attr_parse_nested(tb, adt_attr_cb, adt) < 0) {
+ fprintf(stderr, "\tADT: cannot validate and parse attributes\n");
+ continue;
+ }
+ debug_cadt_attrs(IPSET_ATTR_ADT_MAX,
+ adt_attrs,
+ adtattr2name,
+ adt);
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "\t%s: unresolved!\n",
+ cmdattr2name[i].name);
+ }
+ }
+}
+
+void
+ipset_debug_msg(const char *dir, void *buffer, int len)
+{
+ const struct nlmsghdr *nlh = buffer;
+ struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
+ int cmd, nfmsglen = MNL_ALIGN(sizeof(struct nfgenmsg));
+
+ debug = 0;
+ while (mnl_nlmsg_ok(nlh, len)) {
+ switch (nlh->nlmsg_type) {
+ case NLMSG_NOOP:
+ case NLMSG_DONE:
+ case NLMSG_OVERRUN:
+ fprintf(stderr, "Message header: %s msg %s\n"
+ "\tlen %d\n"
+ "\tseq %u\n",
+ dir,
+ nlh->nlmsg_type == NLMSG_NOOP ? "NOOP" :
+ nlh->nlmsg_type == NLMSG_DONE ? "DONE" : "OVERRUN",
+ len, nlh->nlmsg_seq);
+ goto next_msg;
+ case NLMSG_ERROR: {
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+ fprintf(stderr, "Message header: %s msg ERROR\n"
+ "\tlen %d\n"
+ "\terrcode %d\n"
+ "\tseq %u\n",
+ dir, len, err->error, nlh->nlmsg_seq);
+ goto next_msg;
+ }
+ default:
+ ;
+ }
+ cmd = ipset_get_nlmsg_type(nlh);
+ fprintf(stderr, "Message header: %s cmd %s (%d)\n"
+ "\tlen %d\n"
+ "\tflag %s\n"
+ "\tseq %u\n",
+ dir,
+ cmd <= IPSET_CMD_NONE ? "NONE!" :
+ cmd >= IPSET_CMD_MAX ? "MAX!" : cmd2name[cmd], cmd,
+ len,
+ !(nlh->nlmsg_flags & NLM_F_EXCL) ? "EXIST" : "none",
+ nlh->nlmsg_seq);
+ if (cmd <= IPSET_CMD_NONE || cmd >= IPSET_CMD_MAX)
+ goto next_msg;
+ memset(nla, 0, sizeof(nla));
+ if (mnl_attr_parse(nlh, nfmsglen, cmd_attr_cb, nla) < MNL_CB_STOP) {
+ fprintf(stderr, "\tcannot validate and parse attributes\n");
+ goto next_msg;
+ }
+ debug_cmd_attrs(cmd, nla);
+next_msg:
+ nlh = mnl_nlmsg_next(nlh, &len);
+ }
+ debug = 1;
+}
nlh->nlmsg_flags = NLM_F_REQUEST;
if (cmdflags[cmd-1] & NLM_F_ACK)
nlh->nlmsg_flags |= NLM_F_ACK;
- nlh->nlmsg_seq = ++handle->seq;
ghdr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
ghdr->cmd = cmd;
assert(handle);
assert(buffer);
+ nlh->nlmsg_seq = ++handle->seq;
+#ifdef IPSET_DEBUG
+ ipset_debug_msg("sent", nlh, nlh->nlmsg_len);
+#endif
if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0)
return -ECOMM;
- D("message sent");
ret = mnl_socket_recvfrom(handle->h, buffer, len);
- D("message received, ret: %d", ret);
+#ifdef IPSET_DEBUG
+ ipset_debug_msg("received", buffer, ret);
+#endif
while (ret > 0) {
ret = mnl_cb_run2(buffer, ret,
handle->seq, handle->portid,
handle->cb_ctl[NLMSG_MIN_TYPE],
handle->data,
handle->cb_ctl, NLMSG_MIN_TYPE);
- D("nfln_cb_run2, ret: %d", ret);
+ D("nfln_cb_run2, ret: %d, errno %d", ret, errno);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(handle->h, buffer, len);
char *saved = strdup(str);
char *a, *tmp = saved;
struct addrinfo *info;
- enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR
- : IPSET_OPT_CIDR2;
+ enum ipset_opt copt, opt2;
+
+ if (opt == IPSET_OPT_IP) {
+ copt = IPSET_OPT_CIDR;
+ opt2 = IPSET_OPT_IP_TO;
+ } else {
+ copt = IPSET_OPT_CIDR2;
+ opt2 = IPSET_OPT_IP2_TO;
+ }
if (tmp == NULL)
return ipset_err(session,
|| !range)
goto out;
freeaddrinfo(info);
- aerr = get_addrinfo(session, IPSET_OPT_IP_TO, a, &info, family);
+ aerr = get_addrinfo(session, opt2, a, &info, family);
out:
if (aerr != EINVAL)
}
+/**
+ * ipset_parse_ip4_net6 - parse IPv4|IPv6 address or address/cidr pattern
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address or address/cidr pattern. For IPv4,
+ * address range is valid too.
+ * If family is not set yet in the data blob, INET is assumed.
+ * The values are stored in the data blob of the session.
+ *
+ * FIXME: if the hostname resolves to multiple addresses,
+ * the first one is used only.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_ip4_net6(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ struct ipset_data *data;
+ uint8_t family;
+
+ assert(session);
+ assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+ assert(str);
+
+ data = ipset_session_data(session);
+ family = ipset_data_family(data);
+
+ if (family == AF_UNSPEC) {
+ family = AF_INET;
+ ipset_data_set(data, IPSET_OPT_FAMILY, &family);
+ }
+
+ return family == AF_INET ? parse_ip(session, opt, str, IPADDR_ANY)
+ : ipset_parse_ipnet(session, opt, str);
+
+}
+
/**
* ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout
* @session: session structure
/* Command state */
enum ipset_cmd cmd; /* Current command */
uint32_t lineno; /* Current lineno in restore mode */
+ uint32_t printed_set; /* Printed sets so far */
char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */
const struct ipset_type *saved_type; /* Saved type */
struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */
case IPSET_ENV_QUIET:
case IPSET_ENV_RESOLVE:
case IPSET_ENV_EXIST:
+ case IPSET_ENV_LIST_SETNAME:
+ case IPSET_ENV_LIST_HEADER:
session->envopts |= opt;
return 0;
default:
.type = MNL_TYPE_U8,
.opt = IPSET_OPT_CIDR2,
},
+ [IPSET_ATTR_IP2_TO] = {
+ .type = MNL_TYPE_NESTED,
+ .opt = IPSET_OPT_IP2_TO,
+ },
};
static const struct ipset_attr_policy ipaddr_attrs[] = {
},
};
+#ifdef IPSET_DEBUG
+static int debug = 1;
+#endif
+
static int
generic_data_attr_cb(const struct nlattr *attr, void *data,
int attr_max, const struct ipset_attr_policy *policy)
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
- D("attr type: %u, len %u", type, attr->nla_len);
+ IF_D(debug, "attr type: %u, len %u", type, attr->nla_len);
if (mnl_attr_type_valid(attr, attr_max) < 0) {
- D("attr type: %u INVALID", type);
+ IF_D(debug, "attr type: %u INVALID", type);
return MNL_CB_ERROR;
}
if (mnl_attr_validate(attr, policy[type].type) < 0) {
- D("attr type: %u POLICY, attrlen %u", type,
- mnl_attr_get_payload_len(attr));
+ IF_D(debug, "attr type: %u POLICY, attrlen %u", type,
+ mnl_attr_get_payload_len(attr));
return MNL_CB_ERROR;
}
if (policy[type].type == MNL_TYPE_NUL_STRING
type->name);
break;
case IPSET_LIST_PLAIN:
- safe_snprintf(session, "Name: %s\n"
+ safe_snprintf(session, "%sName: %s\n"
"Type: %s\nHeader: ",
+ session->printed_set ? "\n" : "",
ipset_data_setname(data),
type->name);
break;
safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
safe_snprintf(session, "\nReferences: ");
safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
- safe_snprintf(session, "\nMembers:\n");
+ safe_snprintf(session,
+ session->envopts & IPSET_ENV_LIST_HEADER ?
+ "\n" : "\nMembers:\n");
break;
case IPSET_LIST_XML:
- safe_snprintf(session, "</elements>\n <memsize>");
+ safe_snprintf(session, " <memsize>");
safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
safe_snprintf(session, "</memsize>\n <references>");
safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
- safe_snprintf(session, "</references>\n </header>\n <members>\n");
+ safe_snprintf(session,
+ session->envopts & IPSET_ENV_LIST_HEADER ?
+ "</references>\n </header>\n" :
+ "</references>\n </header>\n <members>\n");
break;
default:
break;
}
+ session->printed_set++;
return MNL_CB_OK;
}
? "NONE" : session->saved_setname);
switch (session->mode) {
case IPSET_LIST_XML:
- if (session->saved_setname[0] == '\0')
- safe_snprintf(session, "\n");
- else
+ if (session->envopts & IPSET_ENV_LIST_SETNAME)
+ break;
+ if (session->envopts & IPSET_ENV_LIST_HEADER) {
+ if (session->saved_setname[0] != '\0')
+ safe_snprintf(session, "</ipset>\n");
+ break;
+ }
+ if (session->saved_setname[0] != '\0')
safe_snprintf(session, " </members>\n</ipset>\n");
break;
- case IPSET_LIST_SAVE:
- /* No empty lines between the sets */
- break;
default:
- safe_snprintf(session, "\n");
break;
}
return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
{
struct ipset_data *data = session->data;
- if (setjmp(printf_failure))
+ if (setjmp(printf_failure)) {
+ session->saved_setname[0] = '\0';
+ session->printed_set = 0;
return MNL_CB_ERROR;
+ }
if (!nla[IPSET_ATTR_SETNAME])
FAILURE("Broken %s kernel message: missing setname!",
ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs);
D("setname %s", ipset_data_setname(data));
+ if (session->envopts & IPSET_ENV_LIST_SETNAME &&
+ session->mode != IPSET_LIST_SAVE) {
+ if (session->mode == IPSET_LIST_XML)
+ safe_snprintf(session, "<ipset name=\"%s\"/>\n",
+ ipset_data_setname(data));
+ else
+ safe_snprintf(session, "%s\n",
+ ipset_data_setname(data));
+ return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK;
+ }
+
if (STREQ(ipset_data_setname(data), session->saved_setname)) {
/* Header part already seen */
if (ipset_data_test(data, IPSET_OPT_TYPE)
return 1;
switch (attr->type) {
+ case MNL_TYPE_NUL_STRING:
+ alen = strlen((const char *)d) + 1;
+ break;
case MNL_TYPE_U32: {
uint32_t value = htonl(*(const uint32_t *)d);
static int
build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
{
- char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned));
+ char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned)) = {};
struct nlmsghdr *nlh = (void *)buffer;
struct ipset_data *data = session->data;
int len = PRIVATE_MSG_BUFLEN, ret;
IPSET_ATTR_REVISION, cmd_attrs);
D("family: %u, type family %u",
ipset_data_family(data), type->family);
- ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY, AF_INET, cmd_attrs);
+ if (ipset_data_test(data, IPSET_OPT_FAMILY))
+ ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY,
+ AF_INET, cmd_attrs);
+ else
+ /* bitmap:port and list:set types */
+ mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, AF_UNSPEC);
/* Type-specific create attributes */
D("call open_nested");
}
case IPSET_CMD_DESTROY:
case IPSET_CMD_FLUSH:
- case IPSET_CMD_LIST:
case IPSET_CMD_SAVE:
if (ipset_data_test(data, IPSET_SETNAME))
ADDATTR_SETNAME(session, nlh, data);
break;
+ case IPSET_CMD_LIST: {
+ uint32_t flags = 0;
+
+ if (session->envopts & IPSET_ENV_LIST_SETNAME)
+ flags |= IPSET_FLAG_LIST_SETNAME;
+ if (session->envopts & IPSET_ENV_LIST_HEADER)
+ flags |= IPSET_FLAG_LIST_HEADER;
+ if (ipset_data_test(data, IPSET_SETNAME))
+ ADDATTR_SETNAME(session, nlh, data);
+ if (flags && session->mode != IPSET_LIST_SAVE) {
+ ipset_data_set(data, IPSET_OPT_FLAGS, &flags);
+ ADDATTR(session, nlh, data, IPSET_ATTR_FLAGS, AF_INET,
+ cmd_attrs);
+ }
+ break;
+ }
case IPSET_CMD_RENAME:
case IPSET_CMD_SWAP:
if (!ipset_data_test(data, IPSET_SETNAME))
/* Reset saved data and nested state */
session->saved_setname[0] = '\0';
+ session->printed_set = 0;
for (i = session->nestid - 1; i >= 0; i--)
session->nested[i] = NULL;
session->nestid = 0;
free(session);
return 0;
}
+
+#ifdef IPSET_DEBUG
+#include "debug.c"
+#endif
kmax, tmin);
}
+ /* Disable unsupported revisions */
+ for (match = NULL, t = typelist; t != NULL; t = t->next) {
+ /* Skip revisions which are unsupported by the kernel */
+ if (t->kernel_check == IPSET_KERNEL_MISMATCH)
+ continue;
+ if (ipset_match_typename(typename, t)
+ && MATCH_FAMILY(t, family)) {
+ if (t->revision < kmin || t->revision > kmax)
+ t->kernel_check = IPSET_KERNEL_MISMATCH;
+ else if (match == NULL)
+ match = t;
+ }
+ }
match->kernel_check = IPSET_KERNEL_OK;
found:
ipset_data_set(data, IPSET_OPT_TYPE, match);
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF),
};
EXPORT_SYMBOL_GPL(ip_set_hostmask_map);
+
+/* Find the largest network which matches the range from left, in host order. */
+u32
+ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr)
+{
+ u32 last;
+ u8 i;
+
+ for (i = 1; i < 32; i++) {
+ if ((from & ip_set_hostmask(i)) != from)
+ continue;
+ last = from | ~ip_set_hostmask(i);
+ if (!after(last, to)) {
+ *cidr = i;
+ return last;
+ }
+ }
+ *cidr = 32;
+ return from;
+}
+EXPORT_SYMBOL_GPL(ip_set_range_to_cidr);
#include <asm/byteorder.h>
#include <linux/netfilter.h>
+#include <net/tcp.h>
/* Prefixlen maps, by Jan Engelhardt */
extern const union nf_inet_addr ip_set_netmask_map[];
return &ip_set_hostmask_map[pfxlen].ip6[0];
}
+extern u32 ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr);
+
+#define ip_set_mask_from_to(from, to, cidr) \
+do { \
+ from &= ip_set_hostmask(cidr); \
+ to = from | ~ip_set_hostmask(cidr); \
+} while (0)
+
#endif /*_PFXLEN_H */
+++ /dev/null
-#ifndef _IP_SET_SLIST_H
-#define _IP_SET_SLIST_H
-
-#include <linux/stddef.h>
-#include <linux/prefetch.h>
-#include <asm/system.h>
-
-/*
- * Single linked lists with a single pointer.
- * Mostly useful for hash tables where the two pointer list head
- * and list node is too wasteful.
- */
-
-struct slist {
- struct slist *next;
-};
-
-#define SLIST(name) struct slist name = { .next = NULL }
-#define INIT_SLIST(ptr) ((ptr)->next = NULL)
-
-#define slist_entry(ptr, type, member) container_of(ptr, type, member)
-
-#define slist_for_each(pos, head) \
- for (pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \
- pos = pos->next)
-
-#define slist_for_each_prev(prev, pos, head) \
- for (prev = head, pos = (head)->next; \
- pos && ({ prefetch(pos->next); 1; }); \
- prev = pos, pos = pos->next)
-
-#define slist_for_each_safe(pos, n, head) \
- for (pos = (head)->next; pos && ({ n = pos->next; 1; }); \
- pos = n)
-
-/**
- * slist_for_each_entry - iterate over list of given type
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct slist to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the slist within the struct.
- */
-#define slist_for_each_entry(tpos, pos, head, member) \
- for (pos = (head)->next; \
- pos && ({ prefetch(pos->next); 1; }) && \
- ({ tpos = slist_entry(pos, typeof(*tpos), member); 1; });\
- pos = pos->next)
-
-/**
- * slist_for_each_entry_continue - iterate over a hlist continuing
- * after current point
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct slist to use as a loop cursor.
- * @member: the name of the slist within the struct.
- */
-#define slist_for_each_entry_continue(tpos, pos, member) \
- for (pos = (pos)->next; \
- pos && ({ prefetch(pos->next); 1; }) && \
- ({ tpos = slist_entry(pos, typeof(*tpos), member); 1; });\
- pos = pos->next)
-
-/**
- * slist_for_each_entry_from - iterate over a hlist continuing
- * from current point
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct slist to use as a loop cursor.
- * @member: the name of the slist within the struct.
- */
-#define slist_for_each_entry_from(tpos, pos, member) \
- for (; pos && ({ prefetch(pos->next); 1; }) && \
- ({ tpos = slist_entry(pos, typeof(*tpos), member); 1; });\
- pos = pos->next)
-
-/**
- * slist_for_each_entry_safe - iterate over list of given type safe against
- * removal of list entry
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct slist to use as a loop cursor.
- * @n: another &struct slist to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the slist within the struct.
- */
-#define slist_for_each_entry_safe(tpos, pos, n, head, member) \
- for (pos = (head)->next; \
- pos && ({ n = pos->next; 1; }) && \
- ({ tpos = slist_entry(pos, typeof(*tpos), member); 1; });\
- pos = n)
-
-#endif /* _IP_SET_SLIST_H */
"Invalid protocol specified" },
{ IPSET_ERR_MISSING_PROTO, 0,
"Protocol missing, but must be specified" },
+ { IPSET_ERR_HASH_RANGE_UNSUPPORTED, 0,
+ "Range is not supported in the \"net\" component of the element" },
+ { IPSET_ERR_HASH_RANGE, 0,
+ "Invalid range, covers the whole address space" },
{ },
};
.PP
COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR | \fBlist\fR | \fBsave\fR | \fBrestore\fR | \fBflush\fR | \fBrename\fR | \fBswap\fR | \fBhelp\fR | \fBversion\fR | \fB\-\fR }
.PP
-\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR }
+\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR | \fB\-name\fR | \fB\-terse\fR }
.PP
\fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ]
.PP
If the set has got reference(s), nothing is done and no set destroyed.
.TP
-\fBlist\fP [ \fISETNAME\fP ]
+\fBlist\fP [ \fISETNAME\fP ] [ \fIOPTIONS\fP ]
List the header data and the entries for the specified set, or for
all sets if none is given. The
\fB\-resolve\fP
\fB\-output\fR
can be used to control the format of the listing:
\fBplain\fR, \fBsave\fR or \fBxml\fR.
-The default is
-\fBplain\fR.
+(The default is
+\fBplain\fR.)
+If the option
+\fB\-name\fR
+is specified, just the names of the existing sets are listed. If the option
+\fB\-terse\fR
+is specified, just the set names and headers are listed.
.TP
\fBsave\fP [ \fISETNAME\fP ]
Save the given set, or all sets if none is given
.TP
\fB\-s\fP, \fB\-sorted\fP
Sorted output. When listing sets entries are listed sorted. Not supported yet.
+.TP
+\fB\-n\fP, \fB\-name\fP
+List just the names of the existing sets, i.e. suppress listing of set headers and members.
+.TP
+\fB\-t\fP, \fB\-terse\fP
+List the set names and headers, i.e. suppress listing of set members.
+
.SH "SET TYPES"
A set type comprises of the storage method by which the data is stored and
the data type(s) which are stored in the set. Therefore the
where the current list of the methods are
\fBbitmap\fR, \fBhash\fR, and \fBlist\fR and the possible data types
-are \fBip\fR, \fBmac\fR and \fBport\fR. The dimension of a set
+are \fBip\fR, \fBnet\fR, \fBmac\fR and \fBport\fR. The dimension of a set
is equal to the number of data types in its type name.
When adding, deleting or testing entries in a set, the same comma separated
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
+\fIADD\-ENTRY\fR := \fInetaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
+\fIDEL\-ENTRY\fR := \fInetaddr\fR
.PP
-\fITEST\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
+\fITEST\-ENTRY\fR := \fInetaddr\fR
+.PP
+where
+\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
+For the \fBinet\fR family one can add or delete multiple entries by specifying
+a range, which is converted internally to network(s) equal to the range:
+.PP
+\fInetaddr\fR := { \fIip\fR[/\fIcidr\fR] | \fIfromaddr\fR\-\fItoaddr\fR }
+.PP
When adding/deleting/testing entries, if the cidr prefix parameter is not specified,
then the host prefix value is assumed. When adding/deleting entries, the exact
element is added/deleted and overlapping elements are not checked by the kernel.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
+\fIADD\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
+\fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
+.PP
+\fITEST\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
-\fITEST\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
+where
+\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
-For the
+For the \fInetaddr\fR part of the elements
+see the description at the \fBhash:net\fR set type. For the
[\fIproto\fR:]\fIport\fR
part of the elements see the description at the
\fBhash:ip,port\fR set type.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
+\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
+\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
-\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
+\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
-For the first \fIipaddr\fR and
+where
+\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
+.PP
+For the \fIipaddr\fR and
[\fIproto\fR:]\fIport\fR
parts of the elements see the descriptions at the
-\fBhash:ip,port\fR set type.
+\fBhash:ip,port\fR set type. For the \fInetaddr\fR part of the elements
+see the description at the \fBhash:net\fR set type.
.PP
Optional \fBcreate\fR options:
.TP
extern struct ipset_type ipset_bitmap_port0;
extern struct ipset_type ipset_hash_ip0;
extern struct ipset_type ipset_hash_net0;
-extern struct ipset_type ipset_hash_netport0;
-extern struct ipset_type ipset_hash_ipport0;
-extern struct ipset_type ipset_hash_ipportip0;
-extern struct ipset_type ipset_hash_ipportnet0;
+extern struct ipset_type ipset_hash_net1;
+extern struct ipset_type ipset_hash_netport1;
+extern struct ipset_type ipset_hash_netport2;
+extern struct ipset_type ipset_hash_ipport1;
+extern struct ipset_type ipset_hash_ipportip1;
+extern struct ipset_type ipset_hash_ipportnet1;
+extern struct ipset_type ipset_hash_ipportnet2;
extern struct ipset_type ipset_list_set0;
enum exittype {
newargv[i] = NULL;
newargc = 1;
- ptr = strtok(buffer, " \t\n");
+ ptr = strtok(buffer, " \t\r\n");
newargv[newargc++] = ptr;
- while ((ptr = strtok(NULL, " \t\n")) != NULL) {
+ while ((ptr = strtok(NULL, " \t\r\n")) != NULL) {
if ((newargc + 1) < (int)(sizeof(newargv)/sizeof(char *)))
newargv[newargc++] = ptr;
else {
c++;
if (c[0] == '\0' || c[0] == '#')
continue;
- else if (strcmp(c, "COMMIT\n") == 0) {
+ else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) {
ret = ipset_commit(session);
if (ret < 0)
handle_error();
int
main(int argc, char *argv[])
{
+ int ret;
+
/* Register types */
ipset_type_add(&ipset_bitmap_ip0);
ipset_type_add(&ipset_bitmap_ipmac0);
ipset_type_add(&ipset_bitmap_port0);
ipset_type_add(&ipset_hash_ip0);
ipset_type_add(&ipset_hash_net0);
- ipset_type_add(&ipset_hash_netport0);
- ipset_type_add(&ipset_hash_ipport0);
- ipset_type_add(&ipset_hash_ipportip0);
- ipset_type_add(&ipset_hash_ipportnet0);
+ ipset_type_add(&ipset_hash_net1);
+ ipset_type_add(&ipset_hash_netport1);
+ ipset_type_add(&ipset_hash_netport2);
+ ipset_type_add(&ipset_hash_ipport1);
+ ipset_type_add(&ipset_hash_ipportip1);
+ ipset_type_add(&ipset_hash_ipportnet1);
+ ipset_type_add(&ipset_hash_ipportnet2);
ipset_type_add(&ipset_list_set0);
/* Initialize session */
return exit_error(OTHER_PROBLEM,
"Cannot initialize ipset session, aborting.");
- return parse_commandline(argc, argv);
+ ret = parse_commandline(argc, argv);
+
+ ipset_session_fini(session);
+
+ return ret;
}
struct ipset_type ipset_hash_ip0 = {
.name = "hash:ip",
- .alias = { "iphash", "iptree", "iptreemap", NULL },
+ .alias = { "iphash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_ONE,
.opt = IPSET_OPT_IP
},
},
- .compat_parse_elem = ipset_parse_iptimeout,
.args = {
[IPSET_CREATE] = hash_ip_create_args,
[IPSET_ADD] = hash_ip_add_args,
{ },
};
-static const char hash_ipport_usage[] =
+static const char hash_ipport1_usage[] =
"create SETNAME hash:ip,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
-struct ipset_type ipset_hash_ipport0 = {
+struct ipset_type ipset_hash_ipport1 = {
.name = "hash:ip,port",
.alias = { "ipporthash", NULL },
.revision = 1,
| IPSET_FLAG(IPSET_OPT_PROTO),
},
- .usage = hash_ipport_usage,
+ .usage = hash_ipport1_usage,
.usagefn = ipset_port_usage,
};
{ },
};
-static const char hash_ipportip_usage[] =
+static const char hash_ipportip1_usage[] =
"create SETNAME hash:ip,port,ip\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
-struct ipset_type ipset_hash_ipportip0 = {
+struct ipset_type ipset_hash_ipportip1 = {
.name = "hash:ip,port,ip",
.alias = { "ipportiphash", NULL },
.revision = 1,
| IPSET_FLAG(IPSET_OPT_IP2),
},
- .usage = hash_ipportip_usage,
+ .usage = hash_ipportip1_usage,
.usagefn = ipset_port_usage,
};
{ },
};
-static const char hash_ipportnet_usage[] =
+static const char hash_ipportnet1_usage[] =
"create SETNAME hash:ip,port,net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
-struct ipset_type ipset_hash_ipportnet0 = {
+struct ipset_type ipset_hash_ipportnet1 = {
.name = "hash:ip,port,net",
.alias = { "ipportnethash", NULL },
.revision = 1,
| IPSET_FLAG(IPSET_OPT_MAXELEM)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_CIDR2)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
| IPSET_FLAG(IPSET_OPT_IP_TO)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_PORT_TO)
| IPSET_FLAG(IPSET_OPT_CIDR2),
},
- .usage = hash_ipportnet_usage,
+ .usage = hash_ipportnet1_usage,
.usagefn = ipset_port_usage,
};
+
+static const char hash_ipportnet2_usage[] =
+"create SETNAME hash:ip,port,net\n"
+" [family inet|inet6]\n"
+" [hashsize VALUE] [maxelem VALUE]\n"
+" [timeout VALUE]\n"
+"add SETNAME IP,PROTO:PORT,IP[/CIDR] [timeout VALUE]\n"
+"del SETNAME IP,PROTO:PORT,IP[/CIDR]\n"
+"test SETNAME IP,PROTO:PORT,IP[/CIDR]\n\n"
+"where depending on the INET family\n"
+" IP are valid IPv4 or IPv6 addresses (or hostnames),\n"
+" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+" in both IP components are supported for IPv4.\n"
+" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+" port range is supported both for IPv4 and IPv6.\n";
+
+struct ipset_type ipset_hash_ipportnet2 = {
+ .name = "hash:ip,port,net",
+ .alias = { "ipportnethash", NULL },
+ .revision = 2,
+ .family = AF_INET46,
+ .dimension = IPSET_DIM_THREE,
+ .elem = {
+ [IPSET_DIM_ONE] = {
+ .parse = ipset_parse_ip4_single6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ [IPSET_DIM_TWO] = {
+ .parse = ipset_parse_proto_port,
+ .print = ipset_print_proto_port,
+ .opt = IPSET_OPT_PORT
+ },
+ [IPSET_DIM_THREE] = {
+ .parse = ipset_parse_ip4_net6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP2
+ },
+ },
+ .args = {
+ [IPSET_CREATE] = hash_ipportnet_create_args,
+ [IPSET_ADD] = hash_ipportnet_add_args,
+ },
+ .mandatory = {
+ [IPSET_CREATE] = 0,
+ [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_IP2),
+ [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_IP2),
+ [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_IP2),
+ },
+ .full = {
+ [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+ | IPSET_FLAG(IPSET_OPT_MAXELEM)
+ | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+ [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PORT_TO)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_IP2)
+ | IPSET_FLAG(IPSET_OPT_CIDR2)
+ | IPSET_FLAG(IPSET_OPT_IP2_TO)
+ | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+ [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PORT_TO)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_IP2)
+ | IPSET_FLAG(IPSET_OPT_CIDR2)
+ | IPSET_FLAG(IPSET_OPT_IP2_TO),
+ [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_IP2)
+ | IPSET_FLAG(IPSET_OPT_CIDR2),
+ },
+
+ .usage = hash_ipportnet2_usage,
+ .usagefn = ipset_port_usage,
+};
+
{ },
};
-static const char hash_net_usage[] =
+static const char hash_net0_usage[] =
"create SETNAME hash:net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
| IPSET_FLAG(IPSET_OPT_CIDR),
},
- .usage = hash_net_usage,
+ .usage = hash_net0_usage,
};
+
+static const char hash_net1_usage[] =
+"create SETNAME hash:net\n"
+" [family inet|inet6]\n"
+" [hashsize VALUE] [maxelem VALUE]\n"
+" [timeout VALUE]\n"
+"add SETNAME IP[/CIDR]|FROM-TO [timeout VALUE]\n"
+"del SETNAME IP[/CIDR]|FROM-TO\n"
+"test SETNAME IP[/CIDR]\n\n"
+"where depending on the INET family\n"
+" IP is an IPv4 or IPv6 address (or hostname),\n"
+" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+" IP range is not supported with IPv6.\n";
+
+struct ipset_type ipset_hash_net1 = {
+ .name = "hash:net",
+ .alias = { "nethash", NULL },
+ .revision = 1,
+ .family = AF_INET46,
+ .dimension = IPSET_DIM_ONE,
+ .elem = {
+ [IPSET_DIM_ONE] = {
+ .parse = ipset_parse_ip4_net6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ },
+ .args = {
+ [IPSET_CREATE] = hash_net_create_args,
+ [IPSET_ADD] = hash_net_add_args,
+ },
+ .mandatory = {
+ [IPSET_CREATE] = 0,
+ [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP),
+ [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP),
+ [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
+ },
+ .full = {
+ [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+ | IPSET_FLAG(IPSET_OPT_MAXELEM)
+ | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+ [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+ [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP_TO),
+ [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR),
+ },
+
+ .usage = hash_net1_usage,
+};
+
{ },
};
-static const char hash_netport_usage[] =
+static const char hash_netport1_usage[] =
"create SETNAME hash:net,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
" port range is supported both for IPv4 and IPv6.\n";
-struct ipset_type ipset_hash_netport0 = {
+struct ipset_type ipset_hash_netport1 = {
.name = "hash:net,port",
.alias = { "netporthash", NULL },
.revision = 1,
| IPSET_FLAG(IPSET_OPT_CIDR),
},
- .usage = hash_netport_usage,
+ .usage = hash_netport1_usage,
+ .usagefn = ipset_port_usage,
+};
+
+static const char hash_netport2_usage[] =
+"create SETNAME hash:net,port\n"
+" [family inet|inet6]\n"
+" [hashsize VALUE] [maxelem VALUE]\n"
+" [timeout VALUE]\n"
+"add SETNAME IP[/CIDR]|FROM-TO,PROTO:PORT [timeout VALUE]\n"
+"del SETNAME IP[/CIDR]|FROM-TO,PROTO:PORT\n"
+"test SETNAME IP[/CIDR],PROTO:PORT\n\n"
+"where depending on the INET family\n"
+" IP is a valid IPv4 or IPv6 address (or hostname),\n"
+" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+" Adding/deleting multiple elements with IPv4 is supported.\n"
+" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+" port range is supported both for IPv4 and IPv6.\n";
+
+struct ipset_type ipset_hash_netport2 = {
+ .name = "hash:net,port",
+ .alias = { "netporthash", NULL },
+ .revision = 2,
+ .family = AF_INET46,
+ .dimension = IPSET_DIM_TWO,
+ .elem = {
+ [IPSET_DIM_ONE] = {
+ .parse = ipset_parse_ip4_net6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ [IPSET_DIM_TWO] = {
+ .parse = ipset_parse_proto_port,
+ .print = ipset_print_proto_port,
+ .opt = IPSET_OPT_PORT
+ },
+ },
+ .args = {
+ [IPSET_CREATE] = hash_netport_create_args,
+ [IPSET_ADD] = hash_netport_add_args,
+ },
+ .mandatory = {
+ [IPSET_CREATE] = 0,
+ [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT),
+ [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT),
+ [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT),
+ },
+ .full = {
+ [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+ | IPSET_FLAG(IPSET_OPT_MAXELEM)
+ | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+ [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PORT_TO)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+ [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PORT_TO)
+ | IPSET_FLAG(IPSET_OPT_PROTO),
+ [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO),
+ },
+
+ .usage = hash_netport2_usage,
.usagefn = ipset_port_usage,
};
" when adding already existing elements\n"
" or when deleting non-existing elements.",
},
+ { .name = { "-n", "-name" },
+ .parse = ipset_envopt_parse,
+ .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_LIST_SETNAME,
+ .help = "\n"
+ " When listing, list just setnames from kernel.\n",
+ },
+ { .name = { "-t", "-terse" },
+ .parse = ipset_envopt_parse,
+ .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_LIST_HEADER,
+ .help = "\n"
+ " When listing, list setnames and set headers\n"
+ " from kernel only.",
+ },
{ },
};
const char *name;
printf(" [PROTO:]PORT is a valid pattern of the following:\n"
- " PORTNAME port name from /etc/services\n"
- " PORTNUMBER port number identifier\n"
- " tcp|udp:PORTNAME|PORTNUMBER\n"
+ " PORTNAME TCP port name from /etc/services\n"
+ " PORTNUMBER TCP port number identifier\n"
+ " tcp|sctp|udp|udplite:PORTNAME|PORTNUMBER\n"
" icmp:CODENAME supported ICMP codename\n"
" icmp:TYPE/CODE ICMP type/code value\n"
" icmpv6:CODENAME supported ICMPv6 codename\n"
static inline int
match_set(ip_set_id_t index, const struct sk_buff *skb,
- u8 pf, u8 dim, u8 flags, int inv)
+ const struct xt_action_param *par,
+ const struct ip_set_adt_opt *opt, int inv)
{
- if (ip_set_test(index, skb, pf, dim, flags))
+ if (ip_set_test(index, skb, par, opt))
inv = !inv;
return inv;
}
-/* Revision 0 interface: backward compatible with netfilter/iptables */
-
-/* Backward compatibility constrains (incomplete):
- * 2.6.24: [NETLINK]: Introduce nested and byteorder flag to netlink attribute
- * 2.6.25: is_vmalloc_addr(): Check if an address is within the vmalloc
- * boundaries
- * 2.6.27: rcu: split list.h and move rcu-protected lists into rculist.h
- * 2.6.28: netfilter: ctnetlink: remove bogus module dependency between
- * ctnetlink and nf_nat (nfnl_lock/nfnl_unlock)
- * 2.6.29: generic swap(): introduce global macro swap(a, b)
- * 2.6.31: netfilter: passive OS fingerprint xtables match
- * 2.6.34: rcu: Add lockdep-enabled variants of rcu_dereference()
- */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
-#error "Linux kernel version too old: must be >= 2.6.34"
-#endif
+#define ADT_OPT(n, f, d, fs, cfs, t) \
+const struct ip_set_adt_opt n = { \
+ .family = f, \
+ .dim = d, \
+ .flags = fs, \
+ .cmdflags = cfs, \
+ .timeout = t, \
+}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-#define CHECK_OK 1
-#define CHECK_FAIL(err) 0
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
-#define CHECK_OK 0
-#define CHECK_FAIL(err) (err)
-#endif
+/* Revision 0 interface: backward compatible with netfilter/iptables */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static bool
-set_match_v0(const struct sk_buff *skb, const struct xt_match_param *par)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static bool
set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
-#endif
{
const struct xt_set_info_match_v0 *info = par->matchinfo;
+ ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
+ info->match_set.u.compat.flags, 0, UINT_MAX);
- return match_set(info->match_set.index, skb, par->family,
- info->match_set.u.compat.dim,
- info->match_set.u.compat.flags,
+ return match_set(info->match_set.index, skb, par, &opt,
info->match_set.u.compat.flags & IPSET_INV_MATCH);
}
}
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static bool
-set_match_v0_checkentry(const struct xt_mtchk_param *par)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_match_v0_checkentry(const struct xt_mtchk_param *par)
-#endif
{
struct xt_set_info_match_v0 *info = par->matchinfo;
ip_set_id_t index;
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match\n",
info->match_set.index);
- return CHECK_FAIL(-ENOENT); /* error */
+ return -ENOENT;
}
if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: set match dimension "
"is over the limit!\n");
ip_set_nfnl_put(info->match_set.index);
- return CHECK_FAIL(-ERANGE); /* error */
+ return -ERANGE;
}
/* Fill out compatibility data */
compat_flags(&info->match_set);
- return CHECK_OK;
+ return 0;
}
static void
ip_set_nfnl_put(info->match_set.index);
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static unsigned int
-set_target_v0(struct sk_buff *skb, const struct xt_target_param *par)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static unsigned int
set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
-#endif
{
const struct xt_set_info_target_v0 *info = par->targinfo;
+ ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
+ info->add_set.u.compat.flags, 0, UINT_MAX);
+ ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
+ info->del_set.u.compat.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
- ip_set_add(info->add_set.index, skb, par->family,
- info->add_set.u.compat.dim,
- info->add_set.u.compat.flags);
+ ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
- ip_set_del(info->del_set.index, skb, par->family,
- info->del_set.u.compat.dim,
- info->del_set.u.compat.flags);
+ ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static bool
-set_target_v0_checkentry(const struct xt_tgchk_param *par)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
set_target_v0_checkentry(const struct xt_tgchk_param *par)
-#endif
{
struct xt_set_info_target_v0 *info = par->targinfo;
ip_set_id_t index;
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
- return CHECK_FAIL(-ENOENT); /* error */
+ return -ENOENT;
}
}
info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
- return CHECK_FAIL(-ENOENT); /* error */
+ return -ENOENT;
}
}
if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
- return CHECK_FAIL(-ERANGE); /* error */
+ return -ERANGE;
}
/* Fill out compatibility data */
compat_flags(&info->add_set);
compat_flags(&info->del_set);
- return CHECK_OK;
+ return 0;
}
static void
ip_set_nfnl_put(info->del_set.index);
}
-/* Revision 1: current interface to netfilter/iptables */
+/* Revision 1 match and target */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static bool
-set_match(const struct sk_buff *skb, const struct xt_match_param *par)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static bool
-set_match(const struct sk_buff *skb, struct xt_action_param *par)
-#endif
+set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
{
- const struct xt_set_info_match *info = par->matchinfo;
+ const struct xt_set_info_match_v1 *info = par->matchinfo;
+ ADT_OPT(opt, par->family, info->match_set.dim,
+ info->match_set.flags, 0, UINT_MAX);
- return match_set(info->match_set.index, skb, par->family,
- info->match_set.dim,
- info->match_set.flags,
+ return match_set(info->match_set.index, skb, par, &opt,
info->match_set.flags & IPSET_INV_MATCH);
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static bool
-set_match_checkentry(const struct xt_mtchk_param *par)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
-set_match_checkentry(const struct xt_mtchk_param *par)
-#endif
+set_match_v1_checkentry(const struct xt_mtchk_param *par)
{
- struct xt_set_info_match *info = par->matchinfo;
+ struct xt_set_info_match_v1 *info = par->matchinfo;
ip_set_id_t index;
index = ip_set_nfnl_get_byindex(info->match_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match\n",
info->match_set.index);
- return CHECK_FAIL(-ENOENT); /* error */
+ return -ENOENT;
}
if (info->match_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: set match dimension "
"is over the limit!\n");
ip_set_nfnl_put(info->match_set.index);
- return CHECK_FAIL(-ERANGE); /* error */
+ return -ERANGE;
}
- return CHECK_OK;
+ return 0;
}
static void
-set_match_destroy(const struct xt_mtdtor_param *par)
+set_match_v1_destroy(const struct xt_mtdtor_param *par)
{
- struct xt_set_info_match *info = par->matchinfo;
+ struct xt_set_info_match_v1 *info = par->matchinfo;
ip_set_nfnl_put(info->match_set.index);
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static unsigned int
-set_target(struct sk_buff *skb, const struct xt_target_param *par)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static unsigned int
-set_target(struct sk_buff *skb, const struct xt_action_param *par)
-#endif
+set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
{
- const struct xt_set_info_target *info = par->targinfo;
+ const struct xt_set_info_target_v1 *info = par->targinfo;
+ ADT_OPT(add_opt, par->family, info->add_set.dim,
+ info->add_set.flags, 0, UINT_MAX);
+ ADT_OPT(del_opt, par->family, info->del_set.dim,
+ info->del_set.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
- ip_set_add(info->add_set.index,
- skb, par->family,
- info->add_set.dim,
- info->add_set.flags);
+ ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
- ip_set_del(info->del_set.index,
- skb, par->family,
- info->del_set.dim,
- info->del_set.flags);
+ ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static bool
-set_target_checkentry(const struct xt_tgchk_param *par)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
static int
-set_target_checkentry(const struct xt_tgchk_param *par)
-#endif
+set_target_v1_checkentry(const struct xt_tgchk_param *par)
{
- const struct xt_set_info_target *info = par->targinfo;
+ const struct xt_set_info_target_v1 *info = par->targinfo;
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
- return CHECK_FAIL(-ENOENT); /* error */
+ return -ENOENT;
}
}
info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
- return CHECK_FAIL(-ENOENT); /* error */
+ return -ENOENT;
}
}
if (info->add_set.dim > IPSET_DIM_MAX ||
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
- return CHECK_FAIL(-ERANGE); /* error */
+ return -ERANGE;
}
- return CHECK_OK;
+ return 0;
}
static void
-set_target_destroy(const struct xt_tgdtor_param *par)
+set_target_v1_destroy(const struct xt_tgdtor_param *par)
{
- const struct xt_set_info_target *info = par->targinfo;
+ const struct xt_set_info_target_v1 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
ip_set_nfnl_put(info->del_set.index);
}
+/* Revision 2 target */
+
+static unsigned int
+set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
+{
+ const struct xt_set_info_target_v2 *info = par->targinfo;
+ ADT_OPT(add_opt, par->family, info->add_set.dim,
+ info->add_set.flags, info->flags, info->timeout);
+ ADT_OPT(del_opt, par->family, info->del_set.dim,
+ info->del_set.flags, 0, UINT_MAX);
+
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_add(info->add_set.index, skb, par, &add_opt);
+ if (info->del_set.index != IPSET_INVALID_ID)
+ ip_set_del(info->del_set.index, skb, par, &del_opt);
+
+ return XT_CONTINUE;
+}
+
+#define set_target_v2_checkentry set_target_v1_checkentry
+#define set_target_v2_destroy set_target_v1_destroy
+
static struct xt_match set_matches[] __read_mostly = {
{
.name = "set",
.name = "set",
.family = NFPROTO_IPV4,
.revision = 1,
- .match = set_match,
- .matchsize = sizeof(struct xt_set_info_match),
- .checkentry = set_match_checkentry,
- .destroy = set_match_destroy,
+ .match = set_match_v1,
+ .matchsize = sizeof(struct xt_set_info_match_v1),
+ .checkentry = set_match_v1_checkentry,
+ .destroy = set_match_v1_destroy,
.me = THIS_MODULE
},
{
.name = "set",
.family = NFPROTO_IPV6,
.revision = 1,
- .match = set_match,
- .matchsize = sizeof(struct xt_set_info_match),
- .checkentry = set_match_checkentry,
- .destroy = set_match_destroy,
+ .match = set_match_v1,
+ .matchsize = sizeof(struct xt_set_info_match_v1),
+ .checkentry = set_match_v1_checkentry,
+ .destroy = set_match_v1_destroy,
.me = THIS_MODULE
},
};
.name = "SET",
.revision = 1,
.family = NFPROTO_IPV4,
- .target = set_target,
- .targetsize = sizeof(struct xt_set_info_target),
- .checkentry = set_target_checkentry,
- .destroy = set_target_destroy,
+ .target = set_target_v1,
+ .targetsize = sizeof(struct xt_set_info_target_v1),
+ .checkentry = set_target_v1_checkentry,
+ .destroy = set_target_v1_destroy,
.me = THIS_MODULE
},
{
.name = "SET",
.revision = 1,
.family = NFPROTO_IPV6,
- .target = set_target,
- .targetsize = sizeof(struct xt_set_info_target),
- .checkentry = set_target_checkentry,
- .destroy = set_target_destroy,
+ .target = set_target_v1,
+ .targetsize = sizeof(struct xt_set_info_target_v1),
+ .checkentry = set_target_v1_checkentry,
+ .destroy = set_target_v1_destroy,
+ .me = THIS_MODULE
+ },
+ {
+ .name = "SET",
+ .revision = 2,
+ .family = NFPROTO_IPV4,
+ .target = set_target_v2,
+ .targetsize = sizeof(struct xt_set_info_target_v2),
+ .checkentry = set_target_v2_checkentry,
+ .destroy = set_target_v2_destroy,
+ .me = THIS_MODULE
+ },
+ {
+ .name = "SET",
+ .revision = 2,
+ .family = NFPROTO_IPV6,
+ .target = set_target_v2,
+ .targetsize = sizeof(struct xt_set_info_target_v2),
+ .checkentry = set_target_v2_checkentry,
+ .destroy = set_target_v2_destroy,
.me = THIS_MODULE
},
};
struct xt_set_info_v0 del_set;
};
-/* Revision 1: current interface to netfilter/iptables */
+/* Revision 1 match and target */
struct xt_set_info {
ip_set_id_t index;
};
/* match and target infos */
-struct xt_set_info_match {
+struct xt_set_info_match_v1 {
struct xt_set_info match_set;
};
-struct xt_set_info_target {
+struct xt_set_info_target_v1 {
struct xt_set_info add_set;
struct xt_set_info del_set;
};
+/* Revision 2 target */
+
+struct xt_set_info_target_v2 {
+ struct xt_set_info add_set;
+ struct xt_set_info del_set;
+ u32 flags;
+ u32 timeout;
+};
+
#endif /*_XT_SET_H*/