diff --git a/README b/README index e51ea2a..d556c8a 100644 --- a/README +++ b/README @@ -19,7 +19,7 @@ simplified, and sped up. Included in this package ======================== - ipset 4.5 -- ipset 5.4.1 +- ipset 6.6a-genl - xt_ACCOUNT 1.16, libxt_ACCOUNT 1.3 diff --git a/extensions/ipset-6/Makefile.am b/extensions/ipset-6/Makefile.am index f21c279..1160eaf 100644 --- a/extensions/ipset-6/Makefile.am +++ b/extensions/ipset-6/Makefile.am @@ -21,4 +21,4 @@ ipset_SOURCES = src/ipset.c src/errcode.c src/ui.c src/ipset_bitmap_ip.c \ src/ipset_list_set.c ipset_LDADD = libipset.la -man_MANS = ipset.8 +man_MANS = src/ipset.8 diff --git a/extensions/ipset-6/include/libipset/data.h b/extensions/ipset-6/include/libipset/data.h index 4710963..8902ddf 100644 --- a/extensions/ipset-6/include/libipset/data.h +++ b/extensions/ipset-6/include/libipset/data.h @@ -44,6 +44,7 @@ enum ipset_opt { IPSET_OPT_NAMEREF, IPSET_OPT_IP2, IPSET_OPT_CIDR2, + IPSET_OPT_IP2_TO, IPSET_OPT_PROTO, /* Swap/rename to */ IPSET_OPT_SETNAME2, diff --git a/extensions/ipset-6/include/libipset/linux_ip_set.h b/extensions/ipset-6/include/libipset/linux_ip_set.h index d81a811..0537fb8 100644 --- a/extensions/ipset-6/include/libipset/linux_ip_set.h +++ b/extensions/ipset-6/include/libipset/linux_ip_set.h @@ -12,7 +12,7 @@ */ /* 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 @@ -104,6 +104,7 @@ enum { 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) @@ -142,6 +143,10 @@ enum ipset_errno { 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 */ diff --git a/extensions/ipset-6/include/libipset/linux_ip_set_hash.h b/extensions/ipset-6/include/libipset/linux_ip_set_hash.h index 7c6336a..7a9e5f7 100644 --- a/extensions/ipset-6/include/libipset/linux_ip_set_hash.h +++ b/extensions/ipset-6/include/libipset/linux_ip_set_hash.h @@ -11,6 +11,10 @@ enum { IPSET_ERR_INVALID_PROTO, /* Protocol missing but must be specified */ IPSET_ERR_MISSING_PROTO, + /* Range not supported */ + IPSET_ERR_HASH_RANGE_UNSUPPORTED, + /* Invalid range */ + IPSET_ERR_HASH_RANGE, }; #endif /* __IP_SET_HASH_H */ diff --git a/extensions/ipset-6/include/libipset/parse.h b/extensions/ipset-6/include/libipset/parse.h index 760df49..bc96a6e 100644 --- a/extensions/ipset-6/include/libipset/parse.h +++ b/extensions/ipset-6/include/libipset/parse.h @@ -60,6 +60,8 @@ extern int ipset_parse_ipnet(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_ip4_single6(struct ipset_session *session, enum ipset_opt opt, const char *str); +extern int ipset_parse_ip4_net6(struct ipset_session *session, + enum ipset_opt opt, const char *str); extern int ipset_parse_name(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_before(struct ipset_session *session, diff --git a/extensions/ipset-6/include/libipset/session.h b/extensions/ipset-6/include/libipset/session.h index a06c79f..9301ccd 100644 --- a/extensions/ipset-6/include/libipset/session.h +++ b/extensions/ipset-6/include/libipset/session.h @@ -65,6 +65,10 @@ enum ipset_envopt { IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE), IPSET_ENV_BIT_EXIST = 3, IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST), + IPSET_ENV_BIT_LIST_SETNAME = 4, + IPSET_ENV_LIST_SETNAME = (1 << IPSET_ENV_BIT_LIST_SETNAME), + IPSET_ENV_BIT_LIST_HEADER = 5, + IPSET_ENV_LIST_HEADER = (1 << IPSET_ENV_BIT_LIST_HEADER), }; extern int ipset_envopt_parse(struct ipset_session *session, @@ -92,4 +96,6 @@ typedef int (*ipset_outfn)(const char *fmt, ...) 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 */ diff --git a/extensions/ipset-6/ip_set.h b/extensions/ipset-6/ip_set.h index 08f59eb..ff8d39a 100644 --- a/extensions/ipset-6/ip_set.h +++ b/extensions/ipset-6/ip_set.h @@ -14,7 +14,7 @@ #include /* 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 @@ -106,6 +106,7 @@ enum { 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) @@ -144,6 +145,10 @@ enum ipset_errno { 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 */ @@ -167,6 +172,7 @@ enum ipset_adt { #include #include #include +#include #include #include @@ -219,6 +225,15 @@ struct ip_set; 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 @@ -226,14 +241,15 @@ struct ip_set_type_variant { * zero for no match/success to add/delete * positive for matching element */ int (*kadt)(struct ip_set *set, const struct sk_buff * skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags); + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt); /* Userspace: test/add/del entries * returns negative error code, * zero for no match/success to add/delete * positive for matching element */ int (*uadt)(struct ip_set *set, struct nlattr *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]; @@ -271,8 +287,8 @@ struct ip_set_type { u8 dimension; /* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */ u8 family; - /* Type revision */ - u8 revision; + /* Type revisions */ + u8 revision_min, revision_max; /* Create set */ int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags); @@ -303,6 +319,8 @@ struct ip_set { 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; }; @@ -316,12 +334,16 @@ extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); extern void ip_set_nfnl_put(ip_set_id_t index); /* API for iptables set match, and SET target */ + extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags); + const struct xt_action_param *par, + const struct ip_set_adt_opt *opt); extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags); + const struct xt_action_param *par, + const struct ip_set_adt_opt *opt); extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags); + const struct xt_action_param *par, + const struct ip_set_adt_opt *opt); /* Utility functions */ extern void * ip_set_alloc(size_t size); diff --git a/extensions/ipset-6/ip_set_ahash.h b/extensions/ipset-6/ip_set_ahash.h index 36a5b00..e4acbd4 100644 --- a/extensions/ipset-6/ip_set_ahash.h +++ b/extensions/ipset-6/ip_set_ahash.h @@ -5,6 +5,11 @@ #include "jhash.h" #include "ip_set_timeout.h" +#define CONCAT(a, b, c) a##b##c +#define TOKEN(a, b, c) CONCAT(a, b, c) + +#define type_pf_next TOKEN(TYPE, PF, _elem) + /* Hashing which uses arrays to resolve clashing. The hash table is resized * (doubled) when searching becomes too long. * Internally jhash is used with the assumption that the size of the @@ -54,6 +59,7 @@ struct ip_set_hash { u32 initval; /* random jhash init value */ u32 timeout; /* timeout value, if enabled */ struct timer_list gc; /* garbage collection when timeout enabled */ + struct type_pf_next next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; /* netmask value for subnets to store */ #endif @@ -217,6 +223,7 @@ ip_set_hash_destroy(struct ip_set *set) #define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask) #define type_pf_data_list TOKEN(TYPE, PF, _data_list) #define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist) +#define type_pf_data_next TOKEN(TYPE, PF, _data_next) #define type_pf_elem TOKEN(TYPE, PF, _elem) #define type_pf_telem TOKEN(TYPE, PF, _telem) @@ -346,6 +353,9 @@ retry: 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 @@ -372,8 +382,11 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) } 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); @@ -586,10 +599,11 @@ nla_put_failure: 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, @@ -821,8 +835,11 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) 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); @@ -840,7 +857,7 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) 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; @@ -851,7 +868,7 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) 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)); diff --git a/extensions/ipset-6/ip_set_bitmap_ip.c b/extensions/ipset-6/ip_set_bitmap_ip.c index cceac52..bae805a 100644 --- a/extensions/ipset-6/ip_set_bitmap_ip.c +++ b/extensions/ipset-6/ip_set_bitmap_ip.c @@ -219,24 +219,25 @@ nla_put_failure: 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]; @@ -283,8 +284,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], 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; @@ -478,7 +478,7 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) 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; @@ -551,7 +551,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = AF_INET, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = bitmap_ip_create, .create_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, diff --git a/extensions/ipset-6/ip_set_bitmap_ipmac.c b/extensions/ipset-6/ip_set_bitmap_ipmac.c index 175d871..9dd9bfe 100644 --- a/extensions/ipset-6/ip_set_bitmap_ipmac.c +++ b/extensions/ipset-6/ip_set_bitmap_ipmac.c @@ -338,17 +338,18 @@ nla_put_failure: 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; @@ -360,12 +361,12 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, 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]; @@ -578,7 +579,7 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[], 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; @@ -623,7 +624,8 @@ static struct ip_set_type bitmap_ipmac_type = { .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 }, diff --git a/extensions/ipset-6/ip_set_bitmap_port.c b/extensions/ipset-6/ip_set_bitmap_port.c index 6512542..4c6b26c 100644 --- a/extensions/ipset-6/ip_set_bitmap_port.c +++ b/extensions/ipset-6/ip_set_bitmap_port.c @@ -208,14 +208,16 @@ nla_put_failure: 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); @@ -225,12 +227,12 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, 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]; @@ -482,7 +484,8 @@ static struct ip_set_type bitmap_port_type = { .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 }, diff --git a/extensions/ipset-6/ip_set_chash.h b/extensions/ipset-6/ip_set_chash.h deleted file mode 100644 index dc8a9ab..0000000 --- a/extensions/ipset-6/ip_set_chash.h +++ /dev/null @@ -1,1164 +0,0 @@ -#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 diff --git a/extensions/ipset-6/ip_set_core.c b/extensions/ipset-6/ip_set_core.c index 509a87c..43ad973 100644 --- a/extensions/ipset-6/ip_set_core.c +++ b/extensions/ipset-6/ip_set_core.c @@ -21,6 +21,7 @@ #include #include +#include #include #include "ip_set.h" #include @@ -75,7 +76,8 @@ find_set_type(const char *name, u8 family, u8 revision) 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; } @@ -148,10 +150,10 @@ retry: 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) @@ -175,25 +177,27 @@ ip_set_type_register(struct ip_set_type *type) 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; @@ -205,15 +209,15 @@ void 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(); @@ -346,7 +350,8 @@ __ip_set_put(ip_set_id_t index) 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; @@ -354,19 +359,19 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb, 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; } @@ -378,7 +383,8 @@ EXPORT_SYMBOL_GPL(ip_set_test); 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; @@ -386,12 +392,12 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb, 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; @@ -400,7 +406,8 @@ EXPORT_SYMBOL_GPL(ip_set_add); 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; @@ -408,12 +415,12 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb, 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; @@ -668,6 +675,7 @@ ip_set_create(struct sk_buff *skb, struct genl_info *info) 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 @@ -708,7 +716,8 @@ ip_set_create(struct sk_buff *skb, struct genl_info *info) (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; @@ -778,7 +787,7 @@ ip_set_destroy(struct sk_buff *skb, struct genl_info *info) 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; } } @@ -825,7 +834,7 @@ ip_set_flush(struct sk_buff *skb, struct genl_info *info) 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++) @@ -947,10 +956,13 @@ ip_set_swap(struct sk_buff *skb, struct genl_info *info) /* 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) @@ -983,6 +995,7 @@ dump_init(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 */ @@ -994,17 +1007,22 @@ dump_init(struct netlink_callback *cb) * [..]: 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; + + 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; - index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME])); - if (index == IPSET_INVALID_ID) - return -ENOENT; - - cb->args[0] = DUMP_ONE; - cb->args[1] = index; return 0; } @@ -1015,9 +1033,10 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) 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); @@ -1032,13 +1051,17 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) 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; } @@ -1047,9 +1070,9 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) /* 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]) { @@ -1066,6 +1089,8 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) } 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 */ @@ -1074,40 +1099,45 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) 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); @@ -1153,17 +1183,18 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, 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; @@ -1338,7 +1369,7 @@ ip_set_utest(struct sk_buff *skb, struct genl_info *info) 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) @@ -1381,7 +1412,7 @@ ip_set_header(struct sk_buff *skb, struct genl_info *info) 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); diff --git a/extensions/ipset-6/ip_set_hash.h b/extensions/ipset-6/ip_set_hash.h index b86f15c..e2a9fae 100644 --- a/extensions/ipset-6/ip_set_hash.h +++ b/extensions/ipset-6/ip_set_hash.h @@ -11,6 +11,10 @@ enum { IPSET_ERR_INVALID_PROTO, /* Protocol missing but must be specified */ IPSET_ERR_MISSING_PROTO, + /* Range not supported */ + IPSET_ERR_HASH_RANGE_UNSUPPORTED, + /* Invalid range */ + IPSET_ERR_HASH_RANGE, }; #ifdef __KERNEL__ diff --git a/extensions/ipset-6/ip_set_hash_ip.c b/extensions/ipset-6/ip_set_hash_ip.c index 94092f3..084d606 100644 --- a/extensions/ipset-6/ip_set_hash_ip.c +++ b/extensions/ipset-6/ip_set_hash_ip.c @@ -108,25 +108,32 @@ nla_put_failure: #define HOST_MASK 32 #include "ip_set_ahash.h" +static inline void +hash_ip4_data_next(struct ip_set_hash *h, const struct hash_ip4_elem *d) +{ + h->next.ip = ntohl(d->ip); +} + static int hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; __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]; @@ -171,13 +178,14 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], 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) @@ -281,20 +289,26 @@ nla_put_failure: #define HOST_MASK 128 #include "ip_set_ahash.h" +static inline void +hash_ip6_data_next(struct ip_set_hash *h, const struct hash_ip6_elem *d) +{ +} + static int hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; union nf_inet_addr ip; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip.in6); ip6_netmask(&ip, h->netmask); if (ipv6_addr_any(&ip.in6)) return -EINVAL; - return adtfn(set, &ip, h->timeout, 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] = { @@ -305,7 +319,7 @@ 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]; @@ -428,7 +442,8 @@ static struct ip_set_type hash_ip_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = hash_ip_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/extensions/ipset-6/ip_set_hash_ipport.c b/extensions/ipset-6/ip_set_hash_ipport.c index e8b506a..c450a31 100644 --- a/extensions/ipset-6/ip_set_hash_ipport.c +++ b/extensions/ipset-6/ip_set_hash_ipport.c @@ -124,31 +124,40 @@ nla_put_failure: #define HOST_MASK 32 #include "ip_set_ahash.h" +static inline void +hash_ipport4_data_next(struct ip_set_hash *h, + const struct hash_ipport4_elem *d) +{ + h->next.ip = ntohl(d->ip); + h->next.port = ntohs(d->port); +} + static int hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem data = { }; - if (!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; @@ -208,8 +217,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], 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; @@ -220,8 +228,11 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], 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); @@ -231,6 +242,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], else ret = 0; } + } return ret; } @@ -328,26 +340,34 @@ nla_put_failure: #define HOST_MASK 128 #include "ip_set_ahash.h" +static inline void +hash_ipport6_data_next(struct ip_set_hash *h, + const struct hash_ipport6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport6_elem data = { }; - if (!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]; @@ -405,6 +425,8 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], 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); @@ -491,7 +513,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .dimension = IPSET_DIM_TWO, .family = AF_UNSPEC, - .revision = 1, + .revision_min = 0, + .revision_max = 1, /* SCTP and UDPLITE support added */ .create = hash_ipport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/extensions/ipset-6/ip_set_hash_ipportip.c b/extensions/ipset-6/ip_set_hash_ipportip.c index f7726b3..b608bd3 100644 --- a/extensions/ipset-6/ip_set_hash_ipportip.c +++ b/extensions/ipset-6/ip_set_hash_ipportip.c @@ -127,32 +127,41 @@ nla_put_failure: #define HOST_MASK 32 #include "ip_set_ahash.h" +static inline void +hash_ipportip4_data_next(struct ip_set_hash *h, + const struct hash_ipportip4_elem *d) +{ + h->next.ip = ntohl(d->ip); + h->next.port = ntohs(d->port); +} + static int hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem data = { }; - if (!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; @@ -216,8 +225,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], 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; @@ -228,8 +236,11 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], 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); @@ -239,6 +250,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], else ret = 0; } + } return ret; } @@ -341,27 +353,35 @@ nla_put_failure: #define HOST_MASK 128 #include "ip_set_ahash.h" +static inline void +hash_ipportip6_data_next(struct ip_set_hash *h, + const struct hash_ipportip6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip6_elem data = { }; - if (!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]; @@ -423,6 +443,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], 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); @@ -509,7 +531,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, .family = AF_UNSPEC, - .revision = 1, + .revision_min = 0, + .revision_max = 1, /* SCTP and UDPLITE support added */ .create = hash_ipportip_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/extensions/ipset-6/ip_set_hash_ipportnet.c b/extensions/ipset-6/ip_set_hash_ipportnet.c index 52d5353..0babe3d 100644 --- a/extensions/ipset-6/ip_set_hash_ipportnet.c +++ b/extensions/ipset-6/ip_set_hash_ipportnet.c @@ -140,39 +140,51 @@ nla_put_failure: #define HOST_MASK 32 #include "ip_set_ahash.h" +static inline void +hash_ipportnet4_data_next(struct ip_set_hash *h, + const struct hash_ipportnet4_elem *d) +{ + h->next.ip = ntohl(d->ip); + h->next.port = ntohs(d->port); + h->next.ip2 = ntohl(d->ip2); +} + static int hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet4_elem data = - { .cidr = h->nets[0].cidr || HOST_MASK }; + struct hash_ipportnet4_elem data = { + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + }; if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - if (!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; @@ -186,21 +198,19 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], 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]); @@ -225,14 +235,16 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], 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) @@ -244,29 +256,50 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], 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); + 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; + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + ip2 = ip2_last + 1; + } } + } return ret; } @@ -388,34 +421,43 @@ nla_put_failure: #define HOST_MASK 128 #include "ip_set_ahash.h" +static inline void +hash_ipportnet6_data_next(struct ip_set_hash *h, + const struct hash_ipportnet6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet6_elem data = - { .cidr = h->nets[0].cidr || HOST_MASK }; + struct hash_ipportnet6_elem data = { + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + }; if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - if (!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]; @@ -432,6 +474,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], 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]); @@ -485,6 +529,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], 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); @@ -574,7 +620,9 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, .family = AF_UNSPEC, - .revision = 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 }, @@ -587,6 +635,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { [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 }, diff --git a/extensions/ipset-6/ip_set_hash_net.c b/extensions/ipset-6/ip_set_hash_net.c index 9f1b14e..60cdb50 100644 --- a/extensions/ipset-6/ip_set_hash_net.c +++ b/extensions/ipset-6/ip_set_hash_net.c @@ -125,33 +125,44 @@ nla_put_failure: #define HOST_MASK 32 #include "ip_set_ahash.h" +static inline void +hash_net4_data_next(struct ip_set_hash *h, + const struct hash_net4_elem *d) +{ + h->next.ip = ntohl(d->ip); +} + static int hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_net4_elem data = { .cidr = h->nets[0].cidr || HOST_MASK }; + struct hash_net4_elem data = { + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + }; if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + 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] || @@ -161,27 +172,51 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], 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 @@ -290,28 +325,37 @@ nla_put_failure: #define HOST_MASK 128 #include "ip_set_ahash.h" +static inline void +hash_net6_data_next(struct ip_set_hash *h, + const struct hash_net6_elem *d) +{ +} + static int hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_net6_elem data = { .cidr = h->nets[0].cidr || HOST_MASK }; + struct hash_net6_elem data = { + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + }; if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6_netmask(&data.ip, data.cidr); - return adtfn(set, &data, h->timeout, 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]; @@ -322,6 +366,8 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], 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]); @@ -425,7 +471,8 @@ static struct ip_set_type hash_net_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, - .revision = 0, + .revision_min = 0, + .revision_max = 1, /* Range as input support for IPv4 added */ .create = hash_net_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -436,6 +483,7 @@ static struct ip_set_type hash_net_type __read_mostly = { }, .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 }, }, diff --git a/extensions/ipset-6/ip_set_hash_netport.c b/extensions/ipset-6/ip_set_hash_netport.c index 989d266..5d14145 100644 --- a/extensions/ipset-6/ip_set_hash_netport.c +++ b/extensions/ipset-6/ip_set_hash_netport.c @@ -137,38 +137,48 @@ nla_put_failure: #define HOST_MASK 32 #include "ip_set_ahash.h" +static inline void +hash_netport4_data_next(struct ip_set_hash *h, + const struct hash_netport4_elem *d) +{ + h->next.ip = ntohl(d->ip); + h->next.port = ntohs(d->port); +} + static int hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem data = { - .cidr = h->nets[0].cidr || HOST_MASK }; + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + }; if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - if (!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; @@ -182,15 +192,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], 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]); @@ -215,24 +225,47 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], 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; } @@ -350,33 +383,42 @@ nla_put_failure: #define HOST_MASK 128 #include "ip_set_ahash.h" +static inline void +hash_netport6_data_next(struct ip_set_hash *h, + const struct hash_netport6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem data = { - .cidr = h->nets[0].cidr || HOST_MASK }; + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + }; if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - if (!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]; @@ -391,6 +433,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], !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]); @@ -438,6 +482,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], 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); @@ -526,7 +572,9 @@ static struct ip_set_type hash_netport_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .dimension = IPSET_DIM_TWO, .family = AF_UNSPEC, - .revision = 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 }, @@ -538,6 +586,7 @@ static struct ip_set_type hash_netport_type __read_mostly = { }, .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 }, diff --git a/extensions/ipset-6/ip_set_list_set.c b/extensions/ipset-6/ip_set_list_set.c index e15f819..5253a52 100644 --- a/extensions/ipset-6/ip_set_list_set.c +++ b/extensions/ipset-6/ip_set_list_set.c @@ -72,7 +72,8 @@ list_set_expired(const struct list_set *map, u32 id) 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; @@ -87,17 +88,17 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb, continue; switch (adt) { case IPSET_TEST: - ret = ip_set_test(elem->id, skb, pf, dim, flags); + ret = ip_set_test(elem->id, skb, par, opt); if (ret > 0) return ret; break; case IPSET_ADD: - ret = ip_set_add(elem->id, skb, pf, dim, flags); + ret = ip_set_add(elem->id, skb, par, opt); if (ret == 0) return ret; break; case IPSET_DEL: - ret = ip_set_del(elem->id, skb, pf, dim, flags); + ret = ip_set_del(elem->id, skb, par, opt); if (ret == 0) return ret; break; @@ -218,7 +219,7 @@ cleanup_entries(struct list_set *map) 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); @@ -575,7 +576,8 @@ static struct ip_set_type list_set_type __read_mostly = { .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = list_set_create, .create_policy = { [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, diff --git a/extensions/ipset-6/ip_set_timeout.h b/extensions/ipset-6/ip_set_timeout.h index 9f30c5f..bae086a 100644 --- a/extensions/ipset-6/ip_set_timeout.h +++ b/extensions/ipset-6/ip_set_timeout.h @@ -22,6 +22,9 @@ #define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT) +#define opt_timeout(opt, map) \ + (with_timeout((opt)->timeout) ? (opt)->timeout : (map)->timeout) + static inline unsigned int ip_set_timeout_uget(struct nlattr *tb) { @@ -45,7 +48,7 @@ ip_set_timeout_test(unsigned long timeout) { return timeout != IPSET_ELEM_UNSET && (timeout == IPSET_ELEM_PERMANENT || - time_after(timeout, jiffies)); + time_is_after_jiffies(timeout)); } static inline bool @@ -53,7 +56,7 @@ ip_set_timeout_expired(unsigned long timeout) { return timeout != IPSET_ELEM_UNSET && timeout != IPSET_ELEM_PERMANENT && - time_before(timeout, jiffies); + time_is_before_jiffies(timeout); } static inline unsigned long @@ -64,7 +67,7 @@ ip_set_timeout_set(u32 timeout) if (!timeout) return IPSET_ELEM_PERMANENT; - t = timeout * HZ + jiffies; + t = msecs_to_jiffies(timeout * 1000) + jiffies; if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT) /* Bingo! */ t++; @@ -75,7 +78,8 @@ ip_set_timeout_set(u32 timeout) static inline u32 ip_set_timeout_get(unsigned long timeout) { - return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ; + return timeout == IPSET_ELEM_PERMANENT ? 0 : + jiffies_to_msecs(timeout - jiffies)/1000; } #else @@ -89,14 +93,14 @@ static inline bool 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 @@ -107,7 +111,7 @@ ip_set_timeout_set(u32 timeout) if (!timeout) return IPSET_ELEM_PERMANENT; - t = timeout * HZ + jiffies; + t = msecs_to_jiffies(timeout * 1000) + jiffies; if (t == IPSET_ELEM_PERMANENT) /* Bingo! :-) */ t++; @@ -118,7 +122,8 @@ ip_set_timeout_set(u32 timeout) static inline u32 ip_set_timeout_get(unsigned long timeout) { - return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ; + return timeout == IPSET_ELEM_PERMANENT ? 0 : + jiffies_to_msecs(timeout - jiffies)/1000; } #endif /* ! IP_SET_BITMAP_TIMEOUT */ diff --git a/extensions/ipset-6/libipset/data.c b/extensions/ipset-6/libipset/data.c index 022e4b4..1541728 100644 --- a/extensions/ipset-6/libipset/data.c +++ b/extensions/ipset-6/libipset/data.c @@ -66,6 +66,7 @@ struct ipset_data { /* ADT/LIST/SAVE */ struct { union nf_inet_addr ip2; + union nf_inet_addr ip2_to; uint8_t cidr2; uint8_t proto; char ether[ETH_ALEN]; @@ -289,6 +290,11 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value) return -1; copy_addr(data->family, &data->adt.ip2, value); break; + case IPSET_OPT_IP2_TO: + if (!(data->family == AF_INET || data->family == AF_INET6)) + return -1; + copy_addr(data->family, &data->adt.ip2_to, value); + break; case IPSET_OPT_CIDR2: data->adt.cidr2 = *(const uint8_t *) value; break; @@ -401,6 +407,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt) return data->adt.nameref; case IPSET_OPT_IP2: return &data->adt.ip2; + case IPSET_OPT_IP2_TO: + return &data->adt.ip2_to; case IPSET_OPT_CIDR2: return &data->adt.cidr2; case IPSET_OPT_PROTO: @@ -436,6 +444,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family) case IPSET_OPT_IP: case IPSET_OPT_IP_TO: case IPSET_OPT_IP2: + case IPSET_OPT_IP2_TO: return family == AF_INET ? sizeof(uint32_t) : sizeof(struct in6_addr); case IPSET_OPT_PORT: diff --git a/extensions/ipset-6/libipset/debug.c b/extensions/ipset-6/libipset/debug.c new file mode 100644 index 0000000..174567d --- /dev/null +++ b/extensions/ipset-6/libipset/debug.c @@ -0,0 +1,268 @@ +/* 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 /* inet_ntop */ +#include /* 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; +} diff --git a/extensions/ipset-6/libipset/mnl.c b/extensions/ipset-6/libipset/mnl.c index 91a5ee4..646a5cd 100644 --- a/extensions/ipset-6/libipset/mnl.c +++ b/extensions/ipset-6/libipset/mnl.c @@ -83,7 +83,6 @@ ipset_mnl_fill_hdr(struct ipset_handle *handle, enum ipset_cmd cmd, nlh->nlmsg_flags = NLM_F_REQUEST; if (cmdflags[cmd-1] & NLM_F_ACK) nlh->nlmsg_flags |= NLM_F_ACK; - nlh->nlmsg_seq = ++handle->seq; ghdr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); ghdr->cmd = cmd; @@ -102,19 +101,24 @@ ipset_mnl_query(struct ipset_handle *handle, void *buffer, size_t len) assert(handle); assert(buffer); + nlh->nlmsg_seq = ++handle->seq; +#ifdef IPSET_DEBUG + ipset_debug_msg("sent", nlh, nlh->nlmsg_len); +#endif if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0) return -ECOMM; - D("message sent"); ret = mnl_socket_recvfrom(handle->h, buffer, len); - D("message received, ret: %d", ret); +#ifdef IPSET_DEBUG + ipset_debug_msg("received", buffer, ret); +#endif while (ret > 0) { ret = mnl_cb_run2(buffer, ret, handle->seq, handle->portid, handle->cb_ctl[NLMSG_MIN_TYPE], handle->data, handle->cb_ctl, NLMSG_MIN_TYPE); - D("nfln_cb_run2, ret: %d", ret); + D("nfln_cb_run2, ret: %d, errno %d", ret, errno); if (ret <= 0) break; ret = mnl_socket_recvfrom(handle->h, buffer, len); diff --git a/extensions/ipset-6/libipset/parse.c b/extensions/ipset-6/libipset/parse.c index 0c15231..091fc6d 100644 --- a/extensions/ipset-6/libipset/parse.c +++ b/extensions/ipset-6/libipset/parse.c @@ -667,8 +667,15 @@ parse_ipaddr(struct ipset_session *session, char *saved = strdup(str); char *a, *tmp = saved; struct addrinfo *info; - enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR - : IPSET_OPT_CIDR2; + enum ipset_opt copt, opt2; + + if (opt == IPSET_OPT_IP) { + copt = IPSET_OPT_CIDR; + opt2 = IPSET_OPT_IP_TO; + } else { + copt = IPSET_OPT_CIDR2; + opt2 = IPSET_OPT_IP2_TO; + } if (tmp == NULL) return ipset_err(session, @@ -691,7 +698,7 @@ parse_ipaddr(struct ipset_session *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) @@ -973,6 +980,46 @@ ipset_parse_ip4_single6(struct ipset_session *session, } +/** + * ipset_parse_ip4_net6 - parse IPv4|IPv6 address or address/cidr pattern + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as an IPv4|IPv6 address or address/cidr pattern. For IPv4, + * address range is valid too. + * If family is not set yet in the data blob, INET is assumed. + * The values are stored in the data blob of the session. + * + * FIXME: if the hostname resolves to multiple addresses, + * the first one is used only. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_ip4_net6(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + struct ipset_data *data; + uint8_t family; + + assert(session); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); + assert(str); + + data = ipset_session_data(session); + family = ipset_data_family(data); + + if (family == AF_UNSPEC) { + family = AF_INET; + ipset_data_set(data, IPSET_OPT_FAMILY, &family); + } + + return family == AF_INET ? parse_ip(session, opt, str, IPADDR_ANY) + : ipset_parse_ipnet(session, opt, str); + +} + /** * ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout * @session: session structure diff --git a/extensions/ipset-6/libipset/session.c b/extensions/ipset-6/libipset/session.c index 5b5eca9..3a53e59 100644 --- a/extensions/ipset-6/libipset/session.c +++ b/extensions/ipset-6/libipset/session.c @@ -37,6 +37,7 @@ struct ipset_session { /* Command state */ enum ipset_cmd cmd; /* Current command */ uint32_t lineno; /* Current lineno in restore mode */ + uint32_t printed_set; /* Printed sets so far */ char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */ const struct ipset_type *saved_type; /* Saved type */ struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */ @@ -138,6 +139,8 @@ ipset_envopt_parse(struct ipset_session *session, int opt, case IPSET_ENV_QUIET: case IPSET_ENV_RESOLVE: case IPSET_ENV_EXIST: + case IPSET_ENV_LIST_SETNAME: + case IPSET_ENV_LIST_HEADER: session->envopts |= opt; return 0; default: @@ -468,6 +471,10 @@ static const struct ipset_attr_policy adt_attrs[] = { .type = MNL_TYPE_U8, .opt = IPSET_OPT_CIDR2, }, + [IPSET_ATTR_IP2_TO] = { + .type = MNL_TYPE_NESTED, + .opt = IPSET_OPT_IP2_TO, + }, }; static const struct ipset_attr_policy ipaddr_attrs[] = { @@ -480,6 +487,10 @@ static const struct ipset_attr_policy ipaddr_attrs[] = { }, }; +#ifdef IPSET_DEBUG +static int debug = 1; +#endif + static int generic_data_attr_cb(const struct nlattr *attr, void *data, int attr_max, const struct ipset_attr_policy *policy) @@ -487,14 +498,14 @@ generic_data_attr_cb(const struct nlattr *attr, void *data, const struct nlattr **tb = data; int type = mnl_attr_get_type(attr); - D("attr type: %u, len %u", type, attr->nla_len); + IF_D(debug, "attr type: %u, len %u", type, attr->nla_len); if (mnl_attr_type_valid(attr, attr_max) < 0) { - D("attr type: %u INVALID", type); + IF_D(debug, "attr type: %u INVALID", type); return MNL_CB_ERROR; } if (mnl_attr_validate(attr, policy[type].type) < 0) { - D("attr type: %u POLICY, attrlen %u", type, - 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 @@ -829,8 +840,9 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) type->name); break; case IPSET_LIST_PLAIN: - safe_snprintf(session, "Name: %s\n" + safe_snprintf(session, "%sName: %s\n" "Type: %s\nHeader: ", + session->printed_set ? "\n" : "", ipset_data_setname(data), type->name); break; @@ -887,18 +899,24 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE); safe_snprintf(session, "\nReferences: "); safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES); - safe_snprintf(session, "\nMembers:\n"); + safe_snprintf(session, + session->envopts & IPSET_ENV_LIST_HEADER ? + "\n" : "\nMembers:\n"); break; case IPSET_LIST_XML: - safe_snprintf(session, "\n "); + safe_snprintf(session, " "); safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE); safe_snprintf(session, "\n "); safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES); - safe_snprintf(session, "\n \n \n"); + safe_snprintf(session, + session->envopts & IPSET_ENV_LIST_HEADER ? + "\n \n" : + "\n \n \n"); break; default: break; } + session->printed_set++; return MNL_CB_OK; } @@ -910,16 +928,17 @@ print_set_done(struct ipset_session *session) ? "NONE" : session->saved_setname); switch (session->mode) { case IPSET_LIST_XML: - if (session->saved_setname[0] == '\0') - safe_snprintf(session, "\n"); - else + if (session->envopts & IPSET_ENV_LIST_SETNAME) + break; + if (session->envopts & IPSET_ENV_LIST_HEADER) { + if (session->saved_setname[0] != '\0') + safe_snprintf(session, "\n"); + break; + } + if (session->saved_setname[0] != '\0') safe_snprintf(session, " \n\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; @@ -931,8 +950,11 @@ callback_list(struct ipset_session *session, struct nlattr *nla[], { struct ipset_data *data = session->data; - if (setjmp(printf_failure)) + if (setjmp(printf_failure)) { + session->saved_setname[0] = '\0'; + session->printed_set = 0; return MNL_CB_ERROR; + } if (!nla[IPSET_ATTR_SETNAME]) FAILURE("Broken %s kernel message: missing setname!", @@ -940,6 +962,17 @@ callback_list(struct ipset_session *session, struct nlattr *nla[], ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs); D("setname %s", ipset_data_setname(data)); + if (session->envopts & IPSET_ENV_LIST_SETNAME && + session->mode != IPSET_LIST_SAVE) { + if (session->mode == IPSET_LIST_XML) + safe_snprintf(session, "\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) @@ -1422,6 +1455,9 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh, return 1; switch (attr->type) { + case MNL_TYPE_NUL_STRING: + alen = strlen((const char *)d) + 1; + break; case MNL_TYPE_U32: { uint32_t value = htonl(*(const uint32_t *)d); @@ -1497,7 +1533,7 @@ addattr_adt(struct ipset_session *session, static int build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd) { - char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned)); + char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned)) = {}; struct nlmsghdr *nlh = (void *)buffer; struct ipset_data *data = session->data; int len = PRIVATE_MSG_BUFLEN, ret; @@ -1592,7 +1628,12 @@ build_msg(struct ipset_session *session, bool aggregate) 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"); @@ -1604,11 +1645,26 @@ build_msg(struct ipset_session *session, bool aggregate) } case IPSET_CMD_DESTROY: case IPSET_CMD_FLUSH: - case IPSET_CMD_LIST: case IPSET_CMD_SAVE: if (ipset_data_test(data, IPSET_SETNAME)) ADDATTR_SETNAME(session, nlh, data); break; + case IPSET_CMD_LIST: { + uint32_t flags = 0; + + if (session->envopts & IPSET_ENV_LIST_SETNAME) + flags |= IPSET_FLAG_LIST_SETNAME; + if (session->envopts & IPSET_ENV_LIST_HEADER) + flags |= IPSET_FLAG_LIST_HEADER; + if (ipset_data_test(data, IPSET_SETNAME)) + ADDATTR_SETNAME(session, nlh, data); + if (flags && session->mode != IPSET_LIST_SAVE) { + ipset_data_set(data, IPSET_OPT_FLAGS, &flags); + ADDATTR(session, nlh, data, IPSET_ATTR_FLAGS, AF_INET, + cmd_attrs); + } + break; + } case IPSET_CMD_RENAME: case IPSET_CMD_SWAP: if (!ipset_data_test(data, IPSET_SETNAME)) @@ -1729,6 +1785,7 @@ ipset_commit(struct ipset_session *session) /* Reset saved data and nested state */ session->saved_setname[0] = '\0'; + session->printed_set = 0; for (i = session->nestid - 1; i >= 0; i--) session->nested[i] = NULL; session->nestid = 0; @@ -1926,3 +1983,7 @@ ipset_session_fini(struct ipset_session *session) free(session); return 0; } + +#ifdef IPSET_DEBUG +#include "debug.c" +#endif diff --git a/extensions/ipset-6/libipset/types.c b/extensions/ipset-6/libipset/types.c index 402e726..c440993 100644 --- a/extensions/ipset-6/libipset/types.c +++ b/extensions/ipset-6/libipset/types.c @@ -267,6 +267,19 @@ create_type_get(struct ipset_session *session) 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); diff --git a/extensions/ipset-6/pfxlen.c b/extensions/ipset-6/pfxlen.c index e9c0764..5750cf5 100644 --- a/extensions/ipset-6/pfxlen.c +++ b/extensions/ipset-6/pfxlen.c @@ -289,3 +289,24 @@ const union nf_inet_addr ip_set_hostmask_map[] = { 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); diff --git a/extensions/ipset-6/pfxlen.h b/extensions/ipset-6/pfxlen.h index 0e1fb50..d55a6cc 100644 --- a/extensions/ipset-6/pfxlen.h +++ b/extensions/ipset-6/pfxlen.h @@ -3,6 +3,7 @@ #include #include +#include /* Prefixlen maps, by Jan Engelhardt */ extern const union nf_inet_addr ip_set_netmask_map[]; @@ -32,4 +33,12 @@ ip_set_hostmask6(u8 pfxlen) return &ip_set_hostmask_map[pfxlen].ip6[0]; } +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 */ diff --git a/extensions/ipset-6/slist.h b/extensions/ipset-6/slist.h deleted file mode 100644 index e472e43..0000000 --- a/extensions/ipset-6/slist.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef _IP_SET_SLIST_H -#define _IP_SET_SLIST_H - -#include -#include -#include - -/* - * 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 */ diff --git a/extensions/ipset-6/src/errcode.c b/extensions/ipset-6/src/errcode.c index 6370ec7..9d5f226 100644 --- a/extensions/ipset-6/src/errcode.c +++ b/extensions/ipset-6/src/errcode.c @@ -113,6 +113,10 @@ static const struct ipset_errcode_table hash_errcode_table[] = { "Invalid protocol specified" }, { IPSET_ERR_MISSING_PROTO, 0, "Protocol missing, but must be specified" }, + { IPSET_ERR_HASH_RANGE_UNSUPPORTED, 0, + "Range is not supported in the \"net\" component of the element" }, + { IPSET_ERR_HASH_RANGE, 0, + "Invalid range, covers the whole address space" }, { }, }; diff --git a/extensions/ipset-6/ipset.8 b/extensions/ipset-6/src/ipset.8 similarity index 94% rename from extensions/ipset-6/ipset.8 rename to extensions/ipset-6/src/ipset.8 index d9e5ff8..90914f4 100644 --- a/extensions/ipset-6/ipset.8 +++ b/extensions/ipset-6/src/ipset.8 @@ -21,7 +21,7 @@ ipset \(em administration tool for IP sets .PP COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR | \fBlist\fR | \fBsave\fR | \fBrestore\fR | \fBflush\fR | \fBrename\fR | \fBswap\fR | \fBhelp\fR | \fBversion\fR | \fB\-\fR } .PP -\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR } +\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR | \fB\-name\fR | \fB\-terse\fR } .PP \fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ] .PP @@ -109,7 +109,7 @@ Destroy the specified set or all the sets if none is given. If the set has got reference(s), nothing is done and no set destroyed. .TP -\fBlist\fP [ \fISETNAME\fP ] +\fBlist\fP [ \fISETNAME\fP ] [ \fIOPTIONS\fP ] List the header data and the entries for the specified set, or for all sets if none is given. The \fB\-resolve\fP @@ -120,8 +120,13 @@ type supports the operation). The option \fB\-output\fR can be used to control the format of the listing: \fBplain\fR, \fBsave\fR or \fBxml\fR. -The default is -\fBplain\fR. +(The default is +\fBplain\fR.) +If the option +\fB\-name\fR +is specified, just the names of the existing sets are listed. If the option +\fB\-terse\fR +is specified, just the set names and headers are listed. .TP \fBsave\fP [ \fISETNAME\fP ] Save the given set, or all sets if none is given @@ -190,6 +195,13 @@ DNS lookups. .TP \fB\-s\fP, \fB\-sorted\fP Sorted output. When listing sets entries are listed sorted. Not supported yet. +.TP +\fB\-n\fP, \fB\-name\fP +List just the names of the existing sets, i.e. suppress listing of set headers and members. +.TP +\fB\-t\fP, \fB\-terse\fP +List the set names and headers, i.e. suppress listing of set members. + .SH "SET TYPES" A set type comprises of the storage method by which the data is stored and the data type(s) which are stored in the set. Therefore the @@ -202,7 +214,7 @@ command follows the syntax where the current list of the methods are \fBbitmap\fR, \fBhash\fR, and \fBlist\fR and the possible data types -are \fBip\fR, \fBmac\fR and \fBport\fR. The dimension of a set +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 @@ -397,13 +409,16 @@ Network address with zero prefix size cannot be stored in this type of sets. .PP \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := \fIip\fR[/\fIcidr\fR] +\fIADD\-ENTRY\fR := \fInetaddr\fR .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := \fIip\fR[/\fIcidr\fR] +\fIDEL\-ENTRY\fR := \fInetaddr\fR .PP -\fITEST\-ENTRY\fR := \fIip\fR[/\fIcidr\fR] +\fITEST\-ENTRY\fR := \fInetaddr\fR +.PP +where +\fInetaddr\fR := \fIip\fR[/\fIcidr\fR] .PP Optional \fBcreate\fR options: .TP @@ -419,6 +434,11 @@ correct value. \fBmaxelem\fR \fIvalue\fR The maximal number of elements which can be stored in the set, default 65536. .PP +For the \fBinet\fR family one can add or delete multiple entries by specifying +a range, which is converted internally to network(s) equal to the range: +.PP +\fInetaddr\fR := { \fIip\fR[/\fIcidr\fR] | \fIfromaddr\fR\-\fItoaddr\fR } +.PP When adding/deleting/testing entries, if the cidr prefix parameter is not specified, then the host prefix value is assumed. When adding/deleting entries, the exact element is added/deleted and overlapping elements are not checked by the kernel. @@ -527,13 +547,16 @@ address with zero prefix size is not accepted either. .PP \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR +\fIADD\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR +\fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR .PP -\fITEST\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR +\fITEST\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR +.PP +where +\fInetaddr\fR := \fIip\fR[/\fIcidr\fR] .PP Optional \fBcreate\fR options: .TP @@ -549,7 +572,8 @@ correct value. \fBmaxelem\fR \fIvalue\fR The maximal number of elements which can be stored in the set, default 65536. .PP -For the +For the \fInetaddr\fR part of the elements +see the description at the \fBhash:net\fR set type. For the [\fIproto\fR:]\fIport\fR part of the elements see the description at the \fBhash:ip,port\fR set type. @@ -633,18 +657,22 @@ address with zero prefix size cannot be stored either. .PP \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR] +\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR] +\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR .PP -\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR] +\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR .PP -For the first \fIipaddr\fR and +where +\fInetaddr\fR := \fIip\fR[/\fIcidr\fR] +.PP +For the \fIipaddr\fR and [\fIproto\fR:]\fIport\fR parts of the elements see the descriptions at the -\fBhash:ip,port\fR set type. +\fBhash:ip,port\fR set type. For the \fInetaddr\fR part of the elements +see the description at the \fBhash:net\fR set type. .PP Optional \fBcreate\fR options: .TP diff --git a/extensions/ipset-6/src/ipset.c b/extensions/ipset-6/src/ipset.c index 05f8ef3..371d851 100644 --- a/extensions/ipset-6/src/ipset.c +++ b/extensions/ipset-6/src/ipset.c @@ -39,10 +39,13 @@ extern struct ipset_type ipset_bitmap_ipmac0; extern struct ipset_type ipset_bitmap_port0; extern struct ipset_type ipset_hash_ip0; extern struct ipset_type ipset_hash_net0; -extern struct ipset_type ipset_hash_netport0; -extern struct ipset_type ipset_hash_ipport0; -extern struct ipset_type ipset_hash_ipportip0; -extern struct ipset_type ipset_hash_ipportnet0; +extern struct ipset_type ipset_hash_net1; +extern struct ipset_type ipset_hash_netport1; +extern struct ipset_type ipset_hash_netport2; +extern struct ipset_type ipset_hash_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 { @@ -145,9 +148,9 @@ build_argv(char *buffer) newargv[i] = NULL; newargc = 1; - ptr = strtok(buffer, " \t\n"); + ptr = strtok(buffer, " \t\r\n"); newargv[newargc++] = ptr; - while ((ptr = strtok(NULL, " \t\n")) != NULL) { + while ((ptr = strtok(NULL, " \t\r\n")) != NULL) { if ((newargc + 1) < (int)(sizeof(newargv)/sizeof(char *))) newargv[newargc++] = ptr; else { @@ -181,7 +184,7 @@ restore(char *argv0) c++; if (c[0] == '\0' || c[0] == '#') continue; - else if (strcmp(c, "COMMIT\n") == 0) { + else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) { ret = ipset_commit(session); if (ret < 0) handle_error(); @@ -715,16 +718,21 @@ parse_commandline(int argc, char *argv[]) int main(int argc, char *argv[]) { + int ret; + /* Register types */ ipset_type_add(&ipset_bitmap_ip0); ipset_type_add(&ipset_bitmap_ipmac0); ipset_type_add(&ipset_bitmap_port0); ipset_type_add(&ipset_hash_ip0); ipset_type_add(&ipset_hash_net0); - ipset_type_add(&ipset_hash_netport0); - ipset_type_add(&ipset_hash_ipport0); - ipset_type_add(&ipset_hash_ipportip0); - ipset_type_add(&ipset_hash_ipportnet0); + ipset_type_add(&ipset_hash_net1); + ipset_type_add(&ipset_hash_netport1); + ipset_type_add(&ipset_hash_netport2); + ipset_type_add(&ipset_hash_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 */ @@ -733,5 +741,9 @@ main(int argc, char *argv[]) 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; } diff --git a/extensions/ipset-6/src/ipset_hash_ip.c b/extensions/ipset-6/src/ipset_hash_ip.c index 0af65b7..6926644 100644 --- a/extensions/ipset-6/src/ipset_hash_ip.c +++ b/extensions/ipset-6/src/ipset_hash_ip.c @@ -81,7 +81,7 @@ static const char hash_ip_usage[] = struct ipset_type ipset_hash_ip0 = { .name = "hash:ip", - .alias = { "iphash", "iptree", "iptreemap", NULL }, + .alias = { "iphash", NULL }, .revision = 0, .family = AF_INET46, .dimension = IPSET_DIM_ONE, @@ -92,7 +92,6 @@ struct ipset_type ipset_hash_ip0 = { .opt = IPSET_OPT_IP }, }, - .compat_parse_elem = ipset_parse_iptimeout, .args = { [IPSET_CREATE] = hash_ip_create_args, [IPSET_ADD] = hash_ip_add_args, diff --git a/extensions/ipset-6/src/ipset_hash_ipport.c b/extensions/ipset-6/src/ipset_hash_ipport.c index 3179805..58ea76c 100644 --- a/extensions/ipset-6/src/ipset_hash_ipport.c +++ b/extensions/ipset-6/src/ipset_hash_ipport.c @@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipport_add_args[] = { { }, }; -static const char hash_ipport_usage[] = +static const char hash_ipport1_usage[] = "create SETNAME hash:ip,port\n" " [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" @@ -85,7 +85,7 @@ static const char hash_ipport_usage[] = " 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, @@ -139,6 +139,6 @@ struct ipset_type ipset_hash_ipport0 = { | IPSET_FLAG(IPSET_OPT_PROTO), }, - .usage = hash_ipport_usage, + .usage = hash_ipport1_usage, .usagefn = ipset_port_usage, }; diff --git a/extensions/ipset-6/src/ipset_hash_ipportip.c b/extensions/ipset-6/src/ipset_hash_ipportip.c index 944ee81..2f310ea 100644 --- a/extensions/ipset-6/src/ipset_hash_ipportip.c +++ b/extensions/ipset-6/src/ipset_hash_ipportip.c @@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipportip_add_args[] = { { }, }; -static const char hash_ipportip_usage[] = +static const char hash_ipportip1_usage[] = "create SETNAME hash:ip,port,ip\n" " [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" @@ -85,7 +85,7 @@ static const char hash_ipportip_usage[] = " 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, @@ -150,6 +150,6 @@ struct ipset_type ipset_hash_ipportip0 = { | IPSET_FLAG(IPSET_OPT_IP2), }, - .usage = hash_ipportip_usage, + .usage = hash_ipportip1_usage, .usagefn = ipset_port_usage, }; diff --git a/extensions/ipset-6/src/ipset_hash_ipportnet.c b/extensions/ipset-6/src/ipset_hash_ipportnet.c index bd94d12..c2eece8 100644 --- a/extensions/ipset-6/src/ipset_hash_ipportnet.c +++ b/extensions/ipset-6/src/ipset_hash_ipportnet.c @@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipportnet_add_args[] = { { }, }; -static const char hash_ipportnet_usage[] = +static const char hash_ipportnet1_usage[] = "create SETNAME hash:ip,port,net\n" " [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" @@ -86,7 +86,7 @@ static const char hash_ipportnet_usage[] = " 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, @@ -133,6 +133,7 @@ struct ipset_type ipset_hash_ipportnet0 = { | IPSET_FLAG(IPSET_OPT_MAXELEM) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) | IPSET_FLAG(IPSET_OPT_IP_TO) | IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_PORT_TO) @@ -141,6 +142,7 @@ struct ipset_type ipset_hash_ipportnet0 = { | IPSET_FLAG(IPSET_OPT_CIDR2) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) | IPSET_FLAG(IPSET_OPT_IP_TO) | IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_PORT_TO) @@ -154,6 +156,99 @@ struct ipset_type ipset_hash_ipportnet0 = { | IPSET_FLAG(IPSET_OPT_CIDR2), }, - .usage = hash_ipportnet_usage, + .usage = hash_ipportnet1_usage, .usagefn = ipset_port_usage, }; + +static const char hash_ipportnet2_usage[] = +"create SETNAME hash:ip,port,net\n" +" [family inet|inet6]\n" +" [hashsize VALUE] [maxelem VALUE]\n" +" [timeout VALUE]\n" +"add SETNAME IP,PROTO:PORT,IP[/CIDR] [timeout VALUE]\n" +"del SETNAME IP,PROTO:PORT,IP[/CIDR]\n" +"test SETNAME IP,PROTO:PORT,IP[/CIDR]\n\n" +"where depending on the INET family\n" +" IP are valid IPv4 or IPv6 addresses (or hostnames),\n" +" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n" +" Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n" +" in both IP components are supported for IPv4.\n" +" Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n" +" port range is supported both for IPv4 and IPv6.\n"; + +struct ipset_type ipset_hash_ipportnet2 = { + .name = "hash:ip,port,net", + .alias = { "ipportnethash", NULL }, + .revision = 2, + .family = AF_INET46, + .dimension = IPSET_DIM_THREE, + .elem = { + [IPSET_DIM_ONE] = { + .parse = ipset_parse_ip4_single6, + .print = ipset_print_ip, + .opt = IPSET_OPT_IP + }, + [IPSET_DIM_TWO] = { + .parse = ipset_parse_proto_port, + .print = ipset_print_proto_port, + .opt = IPSET_OPT_PORT + }, + [IPSET_DIM_THREE] = { + .parse = ipset_parse_ip4_net6, + .print = ipset_print_ip, + .opt = IPSET_OPT_IP2 + }, + }, + .args = { + [IPSET_CREATE] = hash_ipportnet_create_args, + [IPSET_ADD] = hash_ipportnet_add_args, + }, + .mandatory = { + [IPSET_CREATE] = 0, + [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_IP2), + [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_IP2), + [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_IP2), + }, + .full = { + [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) + | IPSET_FLAG(IPSET_OPT_MAXELEM) + | IPSET_FLAG(IPSET_OPT_TIMEOUT), + [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PORT_TO) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_IP2) + | IPSET_FLAG(IPSET_OPT_CIDR2) + | IPSET_FLAG(IPSET_OPT_IP2_TO) + | IPSET_FLAG(IPSET_OPT_TIMEOUT), + [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PORT_TO) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_IP2) + | IPSET_FLAG(IPSET_OPT_CIDR2) + | IPSET_FLAG(IPSET_OPT_IP2_TO), + [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_IP2) + | IPSET_FLAG(IPSET_OPT_CIDR2), + }, + + .usage = hash_ipportnet2_usage, + .usagefn = ipset_port_usage, +}; + diff --git a/extensions/ipset-6/src/ipset_hash_net.c b/extensions/ipset-6/src/ipset_hash_net.c index e8891c1..9c0a6ca 100644 --- a/extensions/ipset-6/src/ipset_hash_net.c +++ b/extensions/ipset-6/src/ipset_hash_net.c @@ -57,7 +57,7 @@ static const struct ipset_arg hash_net_add_args[] = { { }, }; -static const char hash_net_usage[] = +static const char hash_net0_usage[] = "create SETNAME hash:net\n" " [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" @@ -105,5 +105,60 @@ struct ipset_type ipset_hash_net0 = { | IPSET_FLAG(IPSET_OPT_CIDR), }, - .usage = hash_net_usage, + .usage = hash_net0_usage, }; + +static const char hash_net1_usage[] = +"create SETNAME hash:net\n" +" [family inet|inet6]\n" +" [hashsize VALUE] [maxelem VALUE]\n" +" [timeout VALUE]\n" +"add SETNAME IP[/CIDR]|FROM-TO [timeout VALUE]\n" +"del SETNAME IP[/CIDR]|FROM-TO\n" +"test SETNAME IP[/CIDR]\n\n" +"where depending on the INET family\n" +" IP is an IPv4 or IPv6 address (or hostname),\n" +" CIDR is a valid IPv4 or IPv6 CIDR prefix.\n" +" IP range is not supported with IPv6.\n"; + +struct ipset_type ipset_hash_net1 = { + .name = "hash:net", + .alias = { "nethash", NULL }, + .revision = 1, + .family = AF_INET46, + .dimension = IPSET_DIM_ONE, + .elem = { + [IPSET_DIM_ONE] = { + .parse = ipset_parse_ip4_net6, + .print = ipset_print_ip, + .opt = IPSET_OPT_IP + }, + }, + .args = { + [IPSET_CREATE] = hash_net_create_args, + [IPSET_ADD] = hash_net_add_args, + }, + .mandatory = { + [IPSET_CREATE] = 0, + [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP), + [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP), + [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP), + }, + .full = { + [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) + | IPSET_FLAG(IPSET_OPT_MAXELEM) + | IPSET_FLAG(IPSET_OPT_TIMEOUT), + [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_TIMEOUT), + [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) + | IPSET_FLAG(IPSET_OPT_IP_TO), + [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR), + }, + + .usage = hash_net1_usage, +}; + diff --git a/extensions/ipset-6/src/ipset_hash_netport.c b/extensions/ipset-6/src/ipset_hash_netport.c index 8ca77df..7964319 100644 --- a/extensions/ipset-6/src/ipset_hash_netport.c +++ b/extensions/ipset-6/src/ipset_hash_netport.c @@ -49,7 +49,7 @@ static const struct ipset_arg hash_netport_add_args[] = { { }, }; -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" @@ -63,7 +63,7 @@ static const char hash_netport_usage[] = " 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, @@ -118,6 +118,82 @@ struct ipset_type ipset_hash_netport0 = { | 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, }; diff --git a/extensions/ipset-6/src/ui.c b/extensions/ipset-6/src/ui.c index 3601c65..c603c53 100644 --- a/extensions/ipset-6/src/ui.c +++ b/extensions/ipset-6/src/ui.c @@ -188,6 +188,19 @@ const struct ipset_envopts ipset_envopts[] = { " when adding already existing elements\n" " or when deleting non-existing elements.", }, + { .name = { "-n", "-name" }, + .parse = ipset_envopt_parse, + .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_LIST_SETNAME, + .help = "\n" + " When listing, list just setnames from kernel.\n", + }, + { .name = { "-t", "-terse" }, + .parse = ipset_envopt_parse, + .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_LIST_HEADER, + .help = "\n" + " When listing, list setnames and set headers\n" + " from kernel only.", + }, { }, }; @@ -256,9 +269,9 @@ ipset_port_usage(void) const char *name; printf(" [PROTO:]PORT is a valid pattern of the following:\n" - " PORTNAME port name from /etc/services\n" - " PORTNUMBER port number identifier\n" - " tcp|udp:PORTNAME|PORTNUMBER\n" + " PORTNAME TCP port name from /etc/services\n" + " PORTNUMBER TCP port number identifier\n" + " tcp|sctp|udp|udplite:PORTNAME|PORTNUMBER\n" " icmp:CODENAME supported ICMP codename\n" " icmp:TYPE/CODE ICMP type/code value\n" " icmpv6:CODENAME supported ICMPv6 codename\n" diff --git a/extensions/ipset-6/xt_set.c b/extensions/ipset-6/xt_set.c index 93de81d..0e8e90a 100644 --- a/extensions/ipset-6/xt_set.c +++ b/extensions/ipset-6/xt_set.c @@ -29,52 +29,33 @@ MODULE_ALIAS("ip6t_SET"); static inline int match_set(ip_set_id_t index, const struct sk_buff *skb, - u8 pf, u8 dim, u8 flags, int inv) + const struct xt_action_param *par, + const struct ip_set_adt_opt *opt, int inv) { - if (ip_set_test(index, skb, pf, dim, flags)) + if (ip_set_test(index, skb, par, opt)) inv = !inv; return inv; } +#define ADT_OPT(n, f, d, fs, cfs, t) \ +const struct ip_set_adt_opt n = { \ + .family = f, \ + .dim = d, \ + .flags = fs, \ + .cmdflags = cfs, \ + .timeout = t, \ +} + /* Revision 0 interface: backward compatible with netfilter/iptables */ -/* Backward compatibility constrains (incomplete): - * 2.6.24: [NETLINK]: Introduce nested and byteorder flag to netlink attribute - * 2.6.25: is_vmalloc_addr(): Check if an address is within the vmalloc - * boundaries - * 2.6.27: rcu: split list.h and move rcu-protected lists into rculist.h - * 2.6.28: netfilter: ctnetlink: remove bogus module dependency between - * ctnetlink and nf_nat (nfnl_lock/nfnl_unlock) - * 2.6.29: generic swap(): introduce global macro swap(a, b) - * 2.6.31: netfilter: passive OS fingerprint xtables match - * 2.6.34: rcu: Add lockdep-enabled variants of rcu_dereference() - */ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) -#error "Linux kernel version too old: must be >= 2.6.34" -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -#define CHECK_OK 1 -#define CHECK_FAIL(err) 0 -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ -#define CHECK_OK 0 -#define CHECK_FAIL(err) (err) -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -static bool -set_match_v0(const struct sk_buff *skb, const struct xt_match_param *par) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ static bool set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) -#endif { const struct xt_set_info_match_v0 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.u.compat.dim, + info->match_set.u.compat.flags, 0, UINT_MAX); - return match_set(info->match_set.index, skb, par->family, - info->match_set.u.compat.dim, - info->match_set.u.compat.flags, + return match_set(info->match_set.index, skb, par, &opt, info->match_set.u.compat.flags & IPSET_INV_MATCH); } @@ -94,13 +75,8 @@ compat_flags(struct xt_set_info_v0 *info) } } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -static bool -set_match_v0_checkentry(const struct xt_mtchk_param *par) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ static int set_match_v0_checkentry(const struct xt_mtchk_param *par) -#endif { struct xt_set_info_match_v0 *info = par->matchinfo; ip_set_id_t index; @@ -110,19 +86,19 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par) 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 @@ -133,35 +109,25 @@ set_match_v0_destroy(const struct xt_mtdtor_param *par) ip_set_nfnl_put(info->match_set.index); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -static unsigned int -set_target_v0(struct sk_buff *skb, const struct xt_target_param *par) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ static unsigned int set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) -#endif { const struct xt_set_info_target_v0 *info = par->targinfo; + ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim, + info->add_set.u.compat.flags, 0, UINT_MAX); + ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim, + info->del_set.u.compat.flags, 0, UINT_MAX); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_add(info->add_set.index, skb, par->family, - info->add_set.u.compat.dim, - info->add_set.u.compat.flags); + ip_set_add(info->add_set.index, skb, par, &add_opt); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_del(info->del_set.index, skb, par->family, - info->del_set.u.compat.dim, - info->del_set.u.compat.flags); + ip_set_del(info->del_set.index, skb, par, &del_opt); return XT_CONTINUE; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -static bool -set_target_v0_checkentry(const struct xt_tgchk_param *par) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ static int set_target_v0_checkentry(const struct xt_tgchk_param *par) -#endif { struct xt_set_info_target_v0 *info = par->targinfo; ip_set_id_t index; @@ -171,7 +137,7 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) 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; } } @@ -182,7 +148,7 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) 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 || @@ -193,14 +159,14 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) 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 @@ -214,33 +180,23 @@ set_target_v0_destroy(const struct xt_tgdtor_param *par) ip_set_nfnl_put(info->del_set.index); } -/* Revision 1: current interface to netfilter/iptables */ +/* Revision 1 match and target */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) static bool -set_match(const struct sk_buff *skb, const struct xt_match_param *par) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ -static bool -set_match(const struct sk_buff *skb, struct xt_action_param *par) -#endif +set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) { - const struct xt_set_info_match *info = par->matchinfo; + const struct xt_set_info_match_v1 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.dim, + info->match_set.flags, 0, UINT_MAX); - return match_set(info->match_set.index, skb, par->family, - info->match_set.dim, - info->match_set.flags, + return match_set(info->match_set.index, skb, par, &opt, info->match_set.flags & IPSET_INV_MATCH); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -static bool -set_match_checkentry(const struct xt_mtchk_param *par) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ static int -set_match_checkentry(const struct xt_mtchk_param *par) -#endif +set_match_v1_checkentry(const struct xt_mtchk_param *par) { - struct xt_set_info_match *info = par->matchinfo; + struct xt_set_info_match_v1 *info = par->matchinfo; ip_set_id_t index; index = ip_set_nfnl_get_byindex(info->match_set.index); @@ -248,59 +204,47 @@ set_match_checkentry(const struct xt_mtchk_param *par) 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) { @@ -308,7 +252,7 @@ set_target_checkentry(const struct xt_tgchk_param *par) 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; } } @@ -319,7 +263,7 @@ set_target_checkentry(const struct xt_tgchk_param *par) 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 || @@ -330,16 +274,16 @@ set_target_checkentry(const struct xt_tgchk_param *par) 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); @@ -347,6 +291,28 @@ set_target_destroy(const struct xt_tgdtor_param *par) ip_set_nfnl_put(info->del_set.index); } +/* Revision 2 target */ + +static unsigned int +set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_set_info_target_v2 *info = par->targinfo; + ADT_OPT(add_opt, par->family, info->add_set.dim, + info->add_set.flags, info->flags, info->timeout); + ADT_OPT(del_opt, par->family, info->del_set.dim, + info->del_set.flags, 0, UINT_MAX); + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_add(info->add_set.index, skb, par, &add_opt); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_del(info->del_set.index, skb, par, &del_opt); + + return XT_CONTINUE; +} + +#define set_target_v2_checkentry set_target_v1_checkentry +#define set_target_v2_destroy set_target_v1_destroy + static struct xt_match set_matches[] __read_mostly = { { .name = "set", @@ -362,20 +328,20 @@ static struct xt_match set_matches[] __read_mostly = { .name = "set", .family = NFPROTO_IPV4, .revision = 1, - .match = set_match, - .matchsize = sizeof(struct xt_set_info_match), - .checkentry = set_match_checkentry, - .destroy = set_match_destroy, + .match = set_match_v1, + .matchsize = sizeof(struct xt_set_info_match_v1), + .checkentry = set_match_v1_checkentry, + .destroy = set_match_v1_destroy, .me = THIS_MODULE }, { .name = "set", .family = NFPROTO_IPV6, .revision = 1, - .match = set_match, - .matchsize = sizeof(struct xt_set_info_match), - .checkentry = set_match_checkentry, - .destroy = set_match_destroy, + .match = set_match_v1, + .matchsize = sizeof(struct xt_set_info_match_v1), + .checkentry = set_match_v1_checkentry, + .destroy = set_match_v1_destroy, .me = THIS_MODULE }, }; @@ -395,20 +361,40 @@ static struct xt_target set_targets[] __read_mostly = { .name = "SET", .revision = 1, .family = NFPROTO_IPV4, - .target = set_target, - .targetsize = sizeof(struct xt_set_info_target), - .checkentry = set_target_checkentry, - .destroy = set_target_destroy, + .target = set_target_v1, + .targetsize = sizeof(struct xt_set_info_target_v1), + .checkentry = set_target_v1_checkentry, + .destroy = set_target_v1_destroy, .me = THIS_MODULE }, { .name = "SET", .revision = 1, .family = NFPROTO_IPV6, - .target = set_target, - .targetsize = sizeof(struct xt_set_info_target), - .checkentry = set_target_checkentry, - .destroy = set_target_destroy, + .target = set_target_v1, + .targetsize = sizeof(struct xt_set_info_target_v1), + .checkentry = set_target_v1_checkentry, + .destroy = set_target_v1_destroy, + .me = THIS_MODULE + }, + { + .name = "SET", + .revision = 2, + .family = NFPROTO_IPV4, + .target = set_target_v2, + .targetsize = sizeof(struct xt_set_info_target_v2), + .checkentry = set_target_v2_checkentry, + .destroy = set_target_v2_destroy, + .me = THIS_MODULE + }, + { + .name = "SET", + .revision = 2, + .family = NFPROTO_IPV6, + .target = set_target_v2, + .targetsize = sizeof(struct xt_set_info_target_v2), + .checkentry = set_target_v2_checkentry, + .destroy = set_target_v2_destroy, .me = THIS_MODULE }, }; diff --git a/extensions/ipset-6/xt_set.h b/extensions/ipset-6/xt_set.h index 6d6e04f..c46eaee 100644 --- a/extensions/ipset-6/xt_set.h +++ b/extensions/ipset-6/xt_set.h @@ -35,7 +35,7 @@ struct xt_set_info_target_v0 { struct xt_set_info_v0 del_set; }; -/* Revision 1: current interface to netfilter/iptables */ +/* Revision 1 match and target */ struct xt_set_info { ip_set_id_t index; @@ -44,13 +44,22 @@ struct xt_set_info { }; /* match and target infos */ -struct xt_set_info_match { +struct xt_set_info_match_v1 { struct xt_set_info match_set; }; -struct xt_set_info_target { +struct xt_set_info_target_v1 { struct xt_set_info add_set; struct xt_set_info del_set; }; +/* Revision 2 target */ + +struct xt_set_info_target_v2 { + struct xt_set_info add_set; + struct xt_set_info del_set; + u32 flags; + u32 timeout; +}; + #endif /*_XT_SET_H*/