Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next

Pablo Neira Ayuso says:

====================
The following patchset contains relevant updates for the Netfilter
tree, they are:

* Enhancements for ipset: Add the counter extension for sets, this
  information can be used from the iptables set match, to change
  the matching behaviour. Jozsef required to add the extension
  infrastructure and moved the existing timeout support upon it.
  This also includes a change in net/sched/em_ipset to adapt it to
  the new extension structure.

* Enhancements for performance boosting in nfnetlink_queue: Add new
  configuration flags that allows user-space to receive big packets (GRO)
  and to disable checksumming calculation. This were proposed by Eric
  Dumazet during the Netfilter Workshop 2013 in Copenhagen. Florian
  Westphal was kind enough to find the time to materialize the proposal.

* A sparse fix from Simon, he noticed it in the SCTP NAT helper, the fix
  required a change in the interface of sctp_end_cksum.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2013-04-29 14:29:06 -04:00
commit 14d3692f04
29 changed files with 3767 additions and 4718 deletions

View File

@ -1,7 +1,7 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2013 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
@ -47,10 +47,36 @@ enum ip_set_feature {
IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG),
};
/* Set extensions */
enum ip_set_extension {
IPSET_EXT_NONE = 0,
IPSET_EXT_BIT_TIMEOUT = 1,
IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT),
IPSET_EXT_BIT_COUNTER = 2,
IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER),
};
/* Extension offsets */
enum ip_set_offset {
IPSET_OFFSET_TIMEOUT = 0,
IPSET_OFFSET_COUNTER,
IPSET_OFFSET_MAX,
};
#define SET_WITH_TIMEOUT(s) ((s)->extensions & IPSET_EXT_TIMEOUT)
#define SET_WITH_COUNTER(s) ((s)->extensions & IPSET_EXT_COUNTER)
struct ip_set_ext {
unsigned long timeout;
u64 packets;
u64 bytes;
};
struct ip_set;
typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
u32 timeout, u32 flags);
const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 cmdflags);
/* Kernel API function options */
struct ip_set_adt_opt {
@ -58,7 +84,7 @@ struct ip_set_adt_opt {
u8 dim; /* Dimension of match/target */
u8 flags; /* Direction and negation flags */
u32 cmdflags; /* Command-like flags */
u32 timeout; /* Timeout value */
struct ip_set_ext ext; /* Extensions */
};
/* Set type, variant-specific part */
@ -69,7 +95,7 @@ struct ip_set_type_variant {
* positive for matching element */
int (*kadt)(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt);
enum ipset_adt adt, struct ip_set_adt_opt *opt);
/* Userspace: test/add/del entries
* returns negative error code,
@ -151,10 +177,76 @@ struct ip_set {
u8 family;
/* The type revision */
u8 revision;
/* Extensions */
u8 extensions;
/* The type specific data */
void *data;
};
struct ip_set_counter {
atomic64_t bytes;
atomic64_t packets;
};
static inline void
ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter)
{
atomic64_add((long long)bytes, &(counter)->bytes);
}
static inline void
ip_set_add_packets(u64 packets, struct ip_set_counter *counter)
{
atomic64_add((long long)packets, &(counter)->packets);
}
static inline u64
ip_set_get_bytes(const struct ip_set_counter *counter)
{
return (u64)atomic64_read(&(counter)->bytes);
}
static inline u64
ip_set_get_packets(const struct ip_set_counter *counter)
{
return (u64)atomic64_read(&(counter)->packets);
}
static inline void
ip_set_update_counter(struct ip_set_counter *counter,
const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags)
{
if (ext->packets != ULLONG_MAX &&
!(flags & IPSET_FLAG_SKIP_COUNTER_UPDATE)) {
ip_set_add_bytes(ext->bytes, counter);
ip_set_add_packets(ext->packets, counter);
}
if (flags & IPSET_FLAG_MATCH_COUNTERS) {
mext->packets = ip_set_get_packets(counter);
mext->bytes = ip_set_get_bytes(counter);
}
}
static inline bool
ip_set_put_counter(struct sk_buff *skb, struct ip_set_counter *counter)
{
return nla_put_net64(skb, IPSET_ATTR_BYTES,
cpu_to_be64(ip_set_get_bytes(counter))) ||
nla_put_net64(skb, IPSET_ATTR_PACKETS,
cpu_to_be64(ip_set_get_packets(counter)));
}
static inline void
ip_set_init_counter(struct ip_set_counter *counter,
const struct ip_set_ext *ext)
{
if (ext->bytes != ULLONG_MAX)
atomic64_set(&(counter)->bytes, (long long)(ext->bytes));
if (ext->packets != ULLONG_MAX)
atomic64_set(&(counter)->packets, (long long)(ext->packets));
}
/* register and unregister set references */
extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set);
extern void ip_set_put_byindex(ip_set_id_t index);
@ -167,19 +259,21 @@ extern void ip_set_nfnl_put(ip_set_id_t index);
extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt);
struct ip_set_adt_opt *opt);
extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt);
struct ip_set_adt_opt *opt);
extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt);
struct ip_set_adt_opt *opt);
/* Utility functions */
extern void *ip_set_alloc(size_t size);
extern void ip_set_free(void *members);
extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr);
extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr);
extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
struct ip_set_ext *ext);
static inline int
ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr)
@ -200,6 +294,14 @@ ip_set_eexist(int ret, u32 flags)
return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST);
}
/* Match elements marked with nomatch */
static inline bool
ip_set_enomatch(int ret, u32 flags, enum ipset_adt adt)
{
return adt == IPSET_TEST &&
ret == -ENOTEMPTY && ((flags >> 16) & IPSET_FLAG_NOMATCH);
}
/* Check the NLA_F_NET_BYTEORDER flag */
static inline bool
ip_set_attr_netorder(struct nlattr *tb[], int type)
@ -284,4 +386,14 @@ bitmap_bytes(u32 a, u32 b)
return 4 * ((((b - a + 8) / 8) + 3) / 4);
}
#include <linux/netfilter/ipset/ip_set_timeout.h>
#define IP_SET_INIT_KEXT(skb, opt, map) \
{ .bytes = (skb)->len, .packets = 1, \
.timeout = ip_set_adt_opt_timeout(opt, map) }
#define IP_SET_INIT_UEXT(map) \
{ .bytes = ULLONG_MAX, .packets = ULLONG_MAX, \
.timeout = (map)->timeout }
#endif /*_IP_SET_H */

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,12 @@
#define IPSET_BITMAP_MAX_RANGE 0x0000FFFF
enum {
IPSET_ADD_FAILED = 1,
IPSET_ADD_STORE_PLAIN_TIMEOUT,
IPSET_ADD_START_STORED_TIMEOUT,
};
/* Common functions */
static inline u32

View File

@ -1,7 +1,7 @@
#ifndef _IP_SET_TIMEOUT_H
#define _IP_SET_TIMEOUT_H
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2013 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
@ -17,13 +17,14 @@
#define IPSET_GC_PERIOD(timeout) \
((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1)
/* Set is defined without timeout support: timeout value may be 0 */
/* Entry is set with no timeout value */
#define IPSET_ELEM_PERMANENT 0
/* Set is defined with timeout support: timeout value may be 0 */
#define IPSET_NO_TIMEOUT UINT_MAX
#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT)
#define opt_timeout(opt, map) \
(with_timeout((opt)->timeout) ? (opt)->timeout : (map)->timeout)
#define ip_set_adt_opt_timeout(opt, map) \
((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (map)->timeout)
static inline unsigned int
ip_set_timeout_uget(struct nlattr *tb)
@ -38,61 +39,6 @@ ip_set_timeout_uget(struct nlattr *tb)
return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout;
}
#ifdef IP_SET_BITMAP_TIMEOUT
/* Bitmap specific timeout constants and macros for the entries */
/* Bitmap entry is unset */
#define IPSET_ELEM_UNSET 0
/* Bitmap entry is set with no timeout value */
#define IPSET_ELEM_PERMANENT (UINT_MAX/2)
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
return timeout != IPSET_ELEM_UNSET &&
(timeout == IPSET_ELEM_PERMANENT ||
time_is_after_jiffies(timeout));
}
static inline bool
ip_set_timeout_expired(unsigned long timeout)
{
return timeout != IPSET_ELEM_UNSET &&
timeout != IPSET_ELEM_PERMANENT &&
time_is_before_jiffies(timeout);
}
static inline unsigned long
ip_set_timeout_set(u32 timeout)
{
unsigned long t;
if (!timeout)
return IPSET_ELEM_PERMANENT;
t = msecs_to_jiffies(timeout * 1000) + jiffies;
if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT)
/* Bingo! */
t++;
return t;
}
static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT ? 0 :
jiffies_to_msecs(timeout - jiffies)/1000;
}
#else
/* Hash specific timeout constants and macros for the entries */
/* Hash entry is set with no timeout value */
#define IPSET_ELEM_PERMANENT 0
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
@ -101,36 +47,32 @@ ip_set_timeout_test(unsigned long timeout)
}
static inline bool
ip_set_timeout_expired(unsigned long timeout)
ip_set_timeout_expired(unsigned long *timeout)
{
return timeout != IPSET_ELEM_PERMANENT &&
time_is_before_jiffies(timeout);
return *timeout != IPSET_ELEM_PERMANENT &&
time_is_before_jiffies(*timeout);
}
static inline unsigned long
ip_set_timeout_set(u32 timeout)
static inline void
ip_set_timeout_set(unsigned long *timeout, u32 t)
{
unsigned long t;
if (!t) {
*timeout = IPSET_ELEM_PERMANENT;
return;
}
if (!timeout)
return IPSET_ELEM_PERMANENT;
t = msecs_to_jiffies(timeout * 1000) + jiffies;
if (t == IPSET_ELEM_PERMANENT)
*timeout = msecs_to_jiffies(t * 1000) + jiffies;
if (*timeout == IPSET_ELEM_PERMANENT)
/* Bingo! :-) */
t++;
return t;
(*timeout)--;
}
static inline u32
ip_set_timeout_get(unsigned long timeout)
ip_set_timeout_get(unsigned long *timeout)
{
return timeout == IPSET_ELEM_PERMANENT ? 0 :
jiffies_to_msecs(timeout - jiffies)/1000;
return *timeout == IPSET_ELEM_PERMANENT ? 0 :
jiffies_to_msecs(*timeout - jiffies)/1000;
}
#endif /* ! IP_SET_BITMAP_TIMEOUT */
#endif /* __KERNEL__ */
#endif /* _IP_SET_TIMEOUT_H */

View File

@ -41,4 +41,13 @@ do { \
to = from | ~ip_set_hostmask(cidr); \
} while (0)
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
}
#endif /*_PFXLEN_H */

View File

@ -9,10 +9,13 @@ struct nf_queue_entry {
struct nf_hook_ops *elem;
u_int8_t pf;
u16 size; /* sizeof(entry) + saved route keys */
unsigned int hook;
struct net_device *indev;
struct net_device *outdev;
int (*okfn)(struct sk_buff *);
/* extra space to store route keys */
};
#define nf_queue_entry_reroute(x) ((void *)x + sizeof(struct nf_queue_entry))
@ -27,4 +30,7 @@ void nf_register_queue_handler(const struct nf_queue_handler *qh);
void nf_unregister_queue_handler(void);
extern void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
bool nf_queue_entry_get_refs(struct nf_queue_entry *entry);
void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
#endif /* _NF_QUEUE_H */

View File

@ -77,7 +77,7 @@ static inline __u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32)
return sctp_crc32c(crc32, buffer, length);
}
static inline __le32 sctp_end_cksum(__be32 crc32)
static inline __le32 sctp_end_cksum(__u32 crc32)
{
return cpu_to_le32(~crc32);
}

View File

@ -108,6 +108,8 @@ enum {
IPSET_ATTR_CIDR2,
IPSET_ATTR_IP2_TO,
IPSET_ATTR_IFACE,
IPSET_ATTR_BYTES,
IPSET_ATTR_PACKETS,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
@ -137,12 +139,13 @@ enum ipset_errno {
IPSET_ERR_REFERENCED,
IPSET_ERR_IPADDR_IPV4,
IPSET_ERR_IPADDR_IPV6,
IPSET_ERR_COUNTER,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 4352,
};
/* Flags at command level */
/* Flags at command level or match/target flags, lower half of cmdattrs*/
enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
@ -150,10 +153,20 @@ enum ipset_cmd_flags {
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),
IPSET_FLAG_CMD_MAX = 15, /* Lower half */
IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE = 3,
IPSET_FLAG_SKIP_COUNTER_UPDATE =
(1 << IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE),
IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE = 4,
IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE =
(1 << IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE),
IPSET_FLAG_BIT_MATCH_COUNTERS = 5,
IPSET_FLAG_MATCH_COUNTERS = (1 << IPSET_FLAG_BIT_MATCH_COUNTERS),
IPSET_FLAG_BIT_RETURN_NOMATCH = 7,
IPSET_FLAG_RETURN_NOMATCH = (1 << IPSET_FLAG_BIT_RETURN_NOMATCH),
IPSET_FLAG_CMD_MAX = 15,
};
/* Flags at CADT attribute level */
/* Flags at CADT attribute level, upper half of cmdattrs */
enum ipset_cadt_flags {
IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE),
@ -161,7 +174,9 @@ enum ipset_cadt_flags {
IPSET_FLAG_PHYSDEV = (1 << IPSET_FLAG_BIT_PHYSDEV),
IPSET_FLAG_BIT_NOMATCH = 2,
IPSET_FLAG_NOMATCH = (1 << IPSET_FLAG_BIT_NOMATCH),
IPSET_FLAG_CADT_MAX = 15, /* Upper half */
IPSET_FLAG_BIT_WITH_COUNTERS = 3,
IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
IPSET_FLAG_CADT_MAX = 15,
};
/* Commands with settype-specific attributes */
@ -190,6 +205,7 @@ enum ip_set_dim {
* If changed, new revision of iptables match/target is required.
*/
IPSET_DIM_MAX = 6,
/* Backward compatibility: set match revision 2 */
IPSET_BIT_RETURN_NOMATCH = 7,
};
@ -202,6 +218,18 @@ enum ip_set_kopt {
IPSET_RETURN_NOMATCH = (1 << IPSET_BIT_RETURN_NOMATCH),
};
enum {
IPSET_COUNTER_NONE = 0,
IPSET_COUNTER_EQ,
IPSET_COUNTER_NE,
IPSET_COUNTER_LT,
IPSET_COUNTER_GT,
};
struct ip_set_counter_match {
__u8 op;
__u64 value;
};
/* Interface to iptables/ip6tables */

View File

@ -45,6 +45,7 @@ enum nfqnl_attr_type {
NFQA_CT, /* nf_conntrack_netlink.h */
NFQA_CT_INFO, /* enum ip_conntrack_info */
NFQA_CAP_LEN, /* __u32 length of captured packet */
NFQA_SKB_INFO, /* __u32 skb meta information */
__NFQA_MAX
};
@ -96,6 +97,13 @@ enum nfqnl_attr_config {
/* Flags for NFQA_CFG_FLAGS */
#define NFQA_CFG_F_FAIL_OPEN (1 << 0)
#define NFQA_CFG_F_CONNTRACK (1 << 1)
#define NFQA_CFG_F_MAX (1 << 2)
#define NFQA_CFG_F_GSO (1 << 2)
#define NFQA_CFG_F_MAX (1 << 3)
/* flags for NFQA_SKB_INFO */
/* packet appears to have wrong checksums, but they are ok */
#define NFQA_SKB_CSUMNOTREADY (1 << 0)
/* packet is GSO (i.e., exceeds device mtu) */
#define NFQA_SKB_GSO (1 << 1)
#endif /* _NFNETLINK_QUEUE_H */

View File

@ -62,4 +62,13 @@ struct xt_set_info_target_v2 {
__u32 timeout;
};
/* Revision 3 match */
struct xt_set_info_match_v3 {
struct xt_set_info match_set;
struct ip_set_counter_match packets;
struct ip_set_counter_match bytes;
__u32 flags;
};
#endif /*_XT_SET_H*/

View File

@ -0,0 +1,277 @@
/* Copyright (C) 2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __IP_SET_BITMAP_IP_GEN_H
#define __IP_SET_BITMAP_IP_GEN_H
#define CONCAT(a, b) a##b
#define TOKEN(a,b) CONCAT(a, b)
#define mtype_do_test TOKEN(MTYPE, _do_test)
#define mtype_gc_test TOKEN(MTYPE, _gc_test)
#define mtype_is_filled TOKEN(MTYPE, _is_filled)
#define mtype_do_add TOKEN(MTYPE, _do_add)
#define mtype_do_del TOKEN(MTYPE, _do_del)
#define mtype_do_list TOKEN(MTYPE, _do_list)
#define mtype_do_head TOKEN(MTYPE, _do_head)
#define mtype_adt_elem TOKEN(MTYPE, _adt_elem)
#define mtype_add_timeout TOKEN(MTYPE, _add_timeout)
#define mtype_gc_init TOKEN(MTYPE, _gc_init)
#define mtype_kadt TOKEN(MTYPE, _kadt)
#define mtype_uadt TOKEN(MTYPE, _uadt)
#define mtype_destroy TOKEN(MTYPE, _destroy)
#define mtype_flush TOKEN(MTYPE, _flush)
#define mtype_head TOKEN(MTYPE, _head)
#define mtype_same_set TOKEN(MTYPE, _same_set)
#define mtype_elem TOKEN(MTYPE, _elem)
#define mtype_test TOKEN(MTYPE, _test)
#define mtype_add TOKEN(MTYPE, _add)
#define mtype_del TOKEN(MTYPE, _del)
#define mtype_list TOKEN(MTYPE, _list)
#define mtype_gc TOKEN(MTYPE, _gc)
#define mtype MTYPE
#define ext_timeout(e, m) \
(unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
#define ext_counter(e, m) \
(struct ip_set_counter *)((e) + (m)->offset[IPSET_OFFSET_COUNTER])
#define get_ext(map, id) ((map)->extensions + (map)->dsize * (id))
static void
mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
{
struct mtype *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static void
mtype_destroy(struct ip_set *set)
{
struct mtype *map = set->data;
if (SET_WITH_TIMEOUT(set))
del_timer_sync(&map->gc);
ip_set_free(map->members);
if (map->dsize)
ip_set_free(map->extensions);
kfree(map);
set->data = NULL;
}
static void
mtype_flush(struct ip_set *set)
{
struct mtype *map = set->data;
memset(map->members, 0, map->memsize);
}
static int
mtype_head(struct ip_set *set, struct sk_buff *skb)
{
const struct mtype *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
if (mtype_do_head(skb, map) ||
nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) +
map->memsize +
map->dsize * map->elements)) ||
(SET_WITH_TIMEOUT(set) &&
nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
(SET_WITH_COUNTER(set) &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
htonl(IPSET_FLAG_WITH_COUNTERS))))
goto nla_put_failure;
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EMSGSIZE;
}
static int
mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags)
{
struct mtype *map = set->data;
const struct mtype_adt_elem *e = value;
void *x = get_ext(map, e->id);
int ret = mtype_do_test(e, map);
if (ret <= 0)
return ret;
if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(x, map)))
return 0;
if (SET_WITH_COUNTER(set))
ip_set_update_counter(ext_counter(x, map), ext, mext, flags);
return 1;
}
static int
mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags)
{
struct mtype *map = set->data;
const struct mtype_adt_elem *e = value;
void *x = get_ext(map, e->id);
int ret = mtype_do_add(e, map, flags);
if (ret == IPSET_ADD_FAILED) {
if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(x, map)))
ret = 0;
else if (!(flags & IPSET_FLAG_EXIST))
return -IPSET_ERR_EXIST;
}
if (SET_WITH_TIMEOUT(set))
#ifdef IP_SET_BITMAP_STORED_TIMEOUT
mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret);
#else
ip_set_timeout_set(ext_timeout(x, map), ext->timeout);
#endif
if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(x, map), ext);
return 0;
}
static int
mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags)
{
struct mtype *map = set->data;
const struct mtype_adt_elem *e = value;
const void *x = get_ext(map, e->id);
if (mtype_do_del(e, map) ||
(SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(x, map))))
return -IPSET_ERR_EXIST;
return 0;
}
static int
mtype_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
struct mtype *map = set->data;
struct nlattr *adt, *nested;
void *x;
u32 id, first = cb->args[2];
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!adt)
return -EMSGSIZE;
for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2];
x = get_ext(map, id);
if (!test_bit(id, map->members) ||
(SET_WITH_TIMEOUT(set) &&
#ifdef IP_SET_BITMAP_STORED_TIMEOUT
mtype_is_filled((const struct mtype_elem *) x) &&
#endif
ip_set_timeout_expired(ext_timeout(x, map))))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, adt);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
if (mtype_do_list(skb, map, id))
goto nla_put_failure;
if (SET_WITH_TIMEOUT(set)) {
#ifdef IP_SET_BITMAP_STORED_TIMEOUT
if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_stored(map, id,
ext_timeout(x, map)))))
goto nla_put_failure;
#else
if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(
ext_timeout(x, map)))))
goto nla_put_failure;
#endif
}
if (SET_WITH_COUNTER(set) &&
ip_set_put_counter(skb, ext_counter(x, map)))
goto nla_put_failure;
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, adt);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
static void
mtype_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct mtype *map = set->data;
const void *x;
u32 id;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (id = 0; id < map->elements; id++)
if (mtype_gc_test(id, map)) {
x = get_ext(map, id);
if (ip_set_timeout_expired(ext_timeout(x, map)))
clear_bit(id, map->members);
}
read_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static const struct ip_set_type_variant mtype = {
.kadt = mtype_kadt,
.uadt = mtype_uadt,
.adt = {
[IPSET_ADD] = mtype_add,
[IPSET_DEL] = mtype_del,
[IPSET_TEST] = mtype_test,
},
.destroy = mtype_destroy,
.flush = mtype_flush,
.head = mtype_head,
.list = mtype_list,
.same_set = mtype_same_set,
};
#endif /* __IP_SET_BITMAP_IP_GEN_H */

View File

@ -1,6 +1,6 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2013 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
@ -24,31 +24,37 @@
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_bitmap.h>
#define IP_SET_BITMAP_TIMEOUT
#include <linux/netfilter/ipset/ip_set_timeout.h>
#define REVISION_MIN 0
#define REVISION_MAX 0
#define REVISION_MAX 1 /* Counter support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("bitmap:ip", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_bitmap:ip");
#define MTYPE bitmap_ip
/* Type structure */
struct bitmap_ip {
void *members; /* the set members */
void *extensions; /* data extensions */
u32 first_ip; /* host byte order, included in range */
u32 last_ip; /* host byte order, included in range */
u32 elements; /* number of max elements in the set */
u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */
size_t dsize; /* extensions struct size */
size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
u8 netmask; /* subnet netmask */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
/* Base variant */
/* ADT structure for generic function args */
struct bitmap_ip_adt_elem {
u16 id;
};
static inline u32
ip_to_id(const struct bitmap_ip *m, u32 ip)
@ -56,188 +62,67 @@ ip_to_id(const struct bitmap_ip *m, u32 ip)
return ((ip & ip_set_hostmask(m->netmask)) - m->first_ip)/m->hosts;
}
static int
bitmap_ip_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
const struct bitmap_ip *map = set->data;
u16 id = *(u16 *)value;
/* Common functions */
static inline int
bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
{
return !!test_bit(e->id, map->members);
}
static inline int
bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map)
{
return !!test_bit(id, map->members);
}
static int
bitmap_ip_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map,
u32 flags)
{
struct bitmap_ip *map = set->data;
u16 id = *(u16 *)value;
if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
return !!test_and_set_bit(e->id, map->members);
}
static int
bitmap_ip_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_ip_do_del(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
{
struct bitmap_ip *map = set->data;
u16 id = *(u16 *)value;
if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
return !test_and_clear_bit(e->id, map->members);
}
static int
bitmap_ip_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
static inline int
bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id)
{
const struct bitmap_ip *map = set->data;
struct nlattr *atd, *nested;
u32 id, first = cb->args[2];
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EMSGSIZE;
for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2];
if (!test_bit(id, map->members))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts)))
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:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts));
}
/* Timeout variant */
static int
bitmap_ip_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_ip_do_head(struct sk_buff *skb, const struct bitmap_ip *map)
{
const struct bitmap_ip *map = set->data;
const unsigned long *members = map->members;
u16 id = *(u16 *)value;
return ip_set_timeout_test(members[id]);
}
static int
bitmap_ip_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ip *map = set->data;
unsigned long *members = map->members;
u16 id = *(u16 *)value;
if (ip_set_timeout_test(members[id]) && !(flags & IPSET_FLAG_EXIST))
return -IPSET_ERR_EXIST;
members[id] = ip_set_timeout_set(timeout);
return 0;
}
static int
bitmap_ip_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ip *map = set->data;
unsigned long *members = map->members;
u16 id = *(u16 *)value;
int ret = -IPSET_ERR_EXIST;
if (ip_set_timeout_test(members[id]))
ret = 0;
members[id] = IPSET_ELEM_UNSET;
return ret;
}
static int
bitmap_ip_tlist(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ip *map = set->data;
struct nlattr *adt, *nested;
u32 id, first = cb->args[2];
const unsigned long *members = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!adt)
return -EMSGSIZE;
for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2];
if (!ip_set_timeout_test(members[id]))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, adt);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts)) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(members[id]))))
goto nla_put_failure;
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, adt);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) ||
nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) ||
(map->netmask != 32 &&
nla_put_u8(skb, IPSET_ATTR_NETMASK, map->netmask));
}
static int
bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
struct bitmap_ip *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct bitmap_ip_adt_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
u32 ip;
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);
e.id = ip_to_id(map, ip);
return adtfn(set, &ip, opt_timeout(opt, map), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
@ -246,33 +131,31 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
{
struct bitmap_ip *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 timeout = map->timeout;
u32 ip, ip_to, id;
u32 ip, ip_to;
struct bitmap_ip_adt_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
int ret = 0;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(map->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (adt == IPSET_TEST) {
id = ip_to_id(map, ip);
return adtfn(set, &id, timeout, flags);
e.id = ip_to_id(map, ip);
return adtfn(set, &e, &ext, &ext, flags);
}
if (tb[IPSET_ATTR_IP_TO]) {
@ -297,8 +180,8 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
return -IPSET_ERR_BITMAP_RANGE;
for (; !before(ip_to, ip); ip += map->hosts) {
id = ip_to_id(map, ip);
ret = adtfn(set, &id, timeout, flags);
e.id = ip_to_id(map, ip);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -308,54 +191,6 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static void
bitmap_ip_destroy(struct ip_set *set)
{
struct bitmap_ip *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_ip_flush(struct ip_set *set)
{
struct bitmap_ip *map = set->data;
memset(map->members, 0, map->memsize);
}
static int
bitmap_ip_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ip *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) ||
nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) ||
(map->netmask != 32 &&
nla_put_u8(skb, IPSET_ATTR_NETMASK, map->netmask)) ||
nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->memsize)) ||
(with_timeout(map->timeout) &&
nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
goto nla_put_failure;
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EMSGSIZE;
}
static bool
bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
{
@ -365,70 +200,35 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
return x->first_ip == y->first_ip &&
x->last_ip == y->last_ip &&
x->netmask == y->netmask &&
x->timeout == y->timeout;
x->timeout == y->timeout &&
a->extensions == b->extensions;
}
static const struct ip_set_type_variant bitmap_ip = {
.kadt = bitmap_ip_kadt,
.uadt = bitmap_ip_uadt,
.adt = {
[IPSET_ADD] = bitmap_ip_add,
[IPSET_DEL] = bitmap_ip_del,
[IPSET_TEST] = bitmap_ip_test,
},
.destroy = bitmap_ip_destroy,
.flush = bitmap_ip_flush,
.head = bitmap_ip_head,
.list = bitmap_ip_list,
.same_set = bitmap_ip_same_set,
/* Plain variant */
struct bitmap_ip_elem {
};
static const struct ip_set_type_variant bitmap_tip = {
.kadt = bitmap_ip_kadt,
.uadt = bitmap_ip_uadt,
.adt = {
[IPSET_ADD] = bitmap_ip_tadd,
[IPSET_DEL] = bitmap_ip_tdel,
[IPSET_TEST] = bitmap_ip_ttest,
},
.destroy = bitmap_ip_destroy,
.flush = bitmap_ip_flush,
.head = bitmap_ip_head,
.list = bitmap_ip_tlist,
.same_set = bitmap_ip_same_set,
/* Timeout variant */
struct bitmap_ipt_elem {
unsigned long timeout;
};
static void
bitmap_ip_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_ip *map = set->data;
unsigned long *table = map->members;
u32 id;
/* Plain variant with counter */
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (id = 0; id < map->elements; id++)
if (ip_set_timeout_expired(table[id]))
table[id] = IPSET_ELEM_UNSET;
read_unlock_bh(&set->lock);
struct bitmap_ipc_elem {
struct ip_set_counter counter;
};
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
/* Timeout variant with counter */
static void
bitmap_ip_gc_init(struct ip_set *set)
{
struct bitmap_ip *map = set->data;
struct bitmap_ipct_elem {
unsigned long timeout;
struct ip_set_counter counter;
};
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = bitmap_ip_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
#include "ip_set_bitmap_gen.h"
/* Create bitmap:ip type of sets */
@ -440,6 +240,13 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
map->members = ip_set_alloc(map->memsize);
if (!map->members)
return false;
if (map->dsize) {
map->extensions = ip_set_alloc(map->dsize * elements);
if (!map->extensions) {
kfree(map->members);
return false;
}
}
map->first_ip = first_ip;
map->last_ip = last_ip;
map->elements = elements;
@ -457,13 +264,14 @@ static int
bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct bitmap_ip *map;
u32 first_ip, last_ip, hosts;
u32 first_ip, last_ip, hosts, cadt_flags = 0;
u64 elements;
u8 netmask = 32;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip);
@ -526,8 +334,45 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->memsize = elements * sizeof(unsigned long);
map->memsize = bitmap_bytes(0, elements - 1);
set->variant = &bitmap_ip;
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
set->extensions |= IPSET_EXT_COUNTER;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->dsize = sizeof(struct bitmap_ipct_elem);
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct bitmap_ipct_elem, timeout);
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct bitmap_ipct_elem, counter);
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(
tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ip_gc_init(set, bitmap_ip_gc);
} else {
map->dsize = sizeof(struct bitmap_ipc_elem);
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct bitmap_ipc_elem, counter);
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
}
} else if (tb[IPSET_ATTR_TIMEOUT]) {
map->dsize = sizeof(struct bitmap_ipt_elem);
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct bitmap_ipt_elem, timeout);
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
@ -536,19 +381,16 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
}
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_tip;
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ip_gc_init(set);
bitmap_ip_gc_init(set, bitmap_ip_gc);
} else {
map->memsize = bitmap_bytes(0, elements - 1);
map->dsize = 0;
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
set->variant = &bitmap_ip;
}
return 0;
}
@ -568,6 +410,7 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -575,6 +418,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,7 +1,7 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2013 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
@ -23,344 +23,208 @@
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_bitmap.h>
#define REVISION_MIN 0
#define REVISION_MAX 0
#define REVISION_MAX 1 /* Counter support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("bitmap:ip,mac", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_bitmap:ip,mac");
#define MTYPE bitmap_ipmac
#define IP_SET_BITMAP_STORED_TIMEOUT
enum {
MAC_EMPTY, /* element is not set */
MAC_FILLED, /* element is set with MAC */
MAC_UNSET, /* element is set, without MAC */
MAC_FILLED, /* element is set with MAC */
};
/* Type structure */
struct bitmap_ipmac {
void *members; /* the set members */
void *extensions; /* MAC + data extensions */
u32 first_ip; /* host byte order, included in range */
u32 last_ip; /* host byte order, included in range */
u32 elements; /* number of max elements in the set */
u32 timeout; /* timeout value */
struct timer_list gc; /* garbage collector */
size_t memsize; /* members size */
size_t dsize; /* size of element */
size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
};
/* ADT structure for generic function args */
struct ipmac {
u32 id; /* id in array */
unsigned char *ether; /* ethernet address */
struct bitmap_ipmac_adt_elem {
u16 id;
unsigned char *ether;
};
/* Member element without and with timeout */
struct ipmac_elem {
struct bitmap_ipmac_elem {
unsigned char ether[ETH_ALEN];
unsigned char match;
unsigned char filled;
} __attribute__ ((aligned));
struct ipmac_telem {
unsigned char ether[ETH_ALEN];
unsigned char match;
unsigned long timeout;
} __attribute__ ((aligned));
static inline void *
bitmap_ipmac_elem(const struct bitmap_ipmac *map, u32 id)
static inline u32
ip_to_id(const struct bitmap_ipmac *m, u32 ip)
{
return (void *)((char *)map->members + id * map->dsize);
return ip - m->first_ip;
}
static inline bool
bitmap_timeout(const struct bitmap_ipmac *map, u32 id)
static inline struct bitmap_ipmac_elem *
get_elem(void *extensions, u16 id, size_t dsize)
{
const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id);
return ip_set_timeout_test(elem->timeout);
return (struct bitmap_ipmac_elem *)(extensions + id * dsize);
}
static inline bool
bitmap_expired(const struct bitmap_ipmac *map, u32 id)
{
const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id);
/* Common functions */
return ip_set_timeout_expired(elem->timeout);
static inline int
bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e,
const struct bitmap_ipmac *map)
{
const struct bitmap_ipmac_elem *elem;
if (!test_bit(e->id, map->members))
return 0;
elem = get_elem(map->extensions, e->id, map->dsize);
if (elem->filled == MAC_FILLED)
return e->ether == NULL ||
ether_addr_equal(e->ether, elem->ether);
/* Trigger kernel to fill out the ethernet address */
return -EAGAIN;
}
static inline int
bitmap_ipmac_exist(const struct ipmac_telem *elem)
bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map)
{
return elem->match == MAC_UNSET ||
(elem->match == MAC_FILLED &&
!ip_set_timeout_expired(elem->timeout));
const struct bitmap_ipmac_elem *elem;
if (!test_bit(id, map->members))
return 0;
elem = get_elem(map->extensions, id, map->dsize);
/* Timer not started for the incomplete elements */
return elem->filled == MAC_FILLED;
}
/* Base variant */
static int
bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_ipmac_is_filled(const struct bitmap_ipmac_elem *elem)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
switch (elem->match) {
case MAC_UNSET:
/* Trigger kernel to fill out the ethernet address */
return -EAGAIN;
case MAC_FILLED:
return data->ether == NULL ||
ether_addr_equal(data->ether, elem->ether);
}
return 0;
return elem->filled == MAC_FILLED;
}
static int
bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_ipmac_add_timeout(unsigned long *timeout,
const struct bitmap_ipmac_adt_elem *e,
const struct ip_set_ext *ext,
struct bitmap_ipmac *map, int mode)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
u32 t = ext->timeout;
switch (elem->match) {
case MAC_UNSET:
if (!data->ether)
/* Already added without ethernet address */
return -IPSET_ERR_EXIST;
/* Fill the MAC address */
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
break;
case MAC_FILLED:
return -IPSET_ERR_EXIST;
case MAC_EMPTY:
if (data->ether) {
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
} else
elem->match = MAC_UNSET;
}
return 0;
}
static int
bitmap_ipmac_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
if (elem->match == MAC_EMPTY)
return -IPSET_ERR_EXIST;
elem->match = MAC_EMPTY;
return 0;
}
static int
bitmap_ipmac_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac_elem *elem;
struct nlattr *atd, *nested;
u32 id, first = cb->args[2];
u32 last = map->last_ip - map->first_ip;
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
elem = bitmap_ipmac_elem(map, id);
if (elem->match == MAC_EMPTY)
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id)) ||
(elem->match == MAC_FILLED &&
nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN,
elem->ether)))
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:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
}
/* Timeout variant */
static int
bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
switch (elem->match) {
case MAC_UNSET:
/* Trigger kernel to fill out the ethernet address */
return -EAGAIN;
case MAC_FILLED:
return (data->ether == NULL ||
ether_addr_equal(data->ether, elem->ether)) &&
!bitmap_expired(map, data->id);
}
return 0;
}
static int
bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
bool flag_exist = flags & IPSET_FLAG_EXIST;
switch (elem->match) {
case MAC_UNSET:
if (!(data->ether || flag_exist))
/* Already added without ethernet address */
return -IPSET_ERR_EXIST;
/* Fill the MAC address and activate the timer */
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
if (timeout == map->timeout)
if (mode == IPSET_ADD_START_STORED_TIMEOUT) {
if (t == map->timeout)
/* Timeout was not specified, get stored one */
timeout = elem->timeout;
elem->timeout = ip_set_timeout_set(timeout);
break;
case MAC_FILLED:
if (!(bitmap_expired(map, data->id) || flag_exist))
return -IPSET_ERR_EXIST;
/* Fall through */
case MAC_EMPTY:
if (data->ether) {
memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
} else
elem->match = MAC_UNSET;
t = *timeout;
ip_set_timeout_set(timeout, t);
} else {
/* If MAC is unset yet, we store plain timeout value
* because the timer is not activated yet
* and we can reuse it later when MAC is filled out,
* possibly by the kernel */
elem->timeout = data->ether ? ip_set_timeout_set(timeout)
: timeout;
break;
if (e->ether)
ip_set_timeout_set(timeout, t);
else
*timeout = t;
}
return 0;
}
static int
bitmap_ipmac_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e,
struct bitmap_ipmac *map, u32 flags)
{
struct bitmap_ipmac *map = set->data;
const struct ipmac *data = value;
struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
struct bitmap_ipmac_elem *elem;
if (elem->match == MAC_EMPTY || bitmap_expired(map, data->id))
return -IPSET_ERR_EXIST;
elem->match = MAC_EMPTY;
return 0;
elem = get_elem(map->extensions, e->id, map->dsize);
if (test_and_set_bit(e->id, map->members)) {
if (elem->filled == MAC_FILLED) {
if (e->ether && (flags & IPSET_FLAG_EXIST))
memcpy(elem->ether, e->ether, ETH_ALEN);
return IPSET_ADD_FAILED;
} else if (!e->ether)
/* Already added without ethernet address */
return IPSET_ADD_FAILED;
/* Fill the MAC address and trigger the timer activation */
memcpy(elem->ether, e->ether, ETH_ALEN);
elem->filled = MAC_FILLED;
return IPSET_ADD_START_STORED_TIMEOUT;
} else if (e->ether) {
/* We can store MAC too */
memcpy(elem->ether, e->ether, ETH_ALEN);
elem->filled = MAC_FILLED;
return 0;
} else {
elem->filled = MAC_UNSET;
/* MAC is not stored yet, don't start timer */
return IPSET_ADD_STORE_PLAIN_TIMEOUT;
}
}
static int
bitmap_ipmac_tlist(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
static inline int
bitmap_ipmac_do_del(const struct bitmap_ipmac_adt_elem *e,
struct bitmap_ipmac *map)
{
const struct bitmap_ipmac *map = set->data;
const struct ipmac_telem *elem;
struct nlattr *atd, *nested;
u32 id, first = cb->args[2];
u32 timeout, last = map->last_ip - map->first_ip;
return !test_and_clear_bit(e->id, map->members);
}
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
elem = bitmap_ipmac_elem(map, id);
if (!bitmap_ipmac_exist(elem))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id)) ||
(elem->match == MAC_FILLED &&
nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN,
elem->ether)))
goto nla_put_failure;
timeout = elem->match == MAC_UNSET ? elem->timeout
: ip_set_timeout_get(elem->timeout);
if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(timeout)))
goto nla_put_failure;
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, atd);
/* Set listing finished */
cb->args[2] = 0;
static inline unsigned long
ip_set_timeout_stored(struct bitmap_ipmac *map, u32 id, unsigned long *timeout)
{
const struct bitmap_ipmac_elem *elem =
get_elem(map->extensions, id, map->dsize);
return 0;
return elem->filled == MAC_FILLED ? ip_set_timeout_get(timeout) :
*timeout;
}
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
static inline int
bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map,
u32 id)
{
const struct bitmap_ipmac_elem *elem =
get_elem(map->extensions, id, map->dsize);
return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id)) ||
(elem->filled == MAC_FILLED &&
nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, elem->ether));
}
static inline int
bitmap_ipmac_do_head(struct sk_buff *skb, const struct bitmap_ipmac *map)
{
return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) ||
nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
}
static int
bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data;
struct bitmap_ipmac_adt_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
u32 ip;
/* MAC can be src only */
if (!(opt->flags & IPSET_DIM_TWO_SRC))
return 0;
data.id = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
if (data.id < map->first_ip || data.id > map->last_ip)
ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
/* Backward compatibility: we don't check the second flag */
@ -368,10 +232,10 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
(skb_mac_header(skb) + ETH_HLEN) > skb->data)
return -EINVAL;
data.id -= map->first_ip;
data.ether = eth_hdr(skb)->h_source;
e.id = ip_to_id(map, ip);
e.ether = eth_hdr(skb)->h_source;
return adtfn(set, &data, opt_timeout(opt, map), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
@ -380,91 +244,39 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
{
const struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data;
u32 timeout = map->timeout;
struct bitmap_ipmac_adt_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
u32 ip;
int ret = 0;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &data.id);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (data.id < map->first_ip || data.id > map->last_ip)
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
e.id = ip_to_id(map, ip);
if (tb[IPSET_ATTR_ETHER])
data.ether = nla_data(tb[IPSET_ATTR_ETHER]);
e.ether = nla_data(tb[IPSET_ATTR_ETHER]);
else
data.ether = NULL;
e.ether = NULL;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(map->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
data.id -= map->first_ip;
ret = adtfn(set, &data, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
static void
bitmap_ipmac_destroy(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_ipmac_flush(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
memset(map->members, 0,
(map->last_ip - map->first_ip + 1) * map->dsize);
}
static int
bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ipmac *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) ||
nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) ||
nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) +
((map->last_ip - map->first_ip + 1) *
map->dsize))) ||
(with_timeout(map->timeout) &&
nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
goto nla_put_failure;
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EMSGSIZE;
}
static bool
bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
{
@ -473,85 +285,64 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
return x->first_ip == y->first_ip &&
x->last_ip == y->last_ip &&
x->timeout == y->timeout;
x->timeout == y->timeout &&
a->extensions == b->extensions;
}
static const struct ip_set_type_variant bitmap_ipmac = {
.kadt = bitmap_ipmac_kadt,
.uadt = bitmap_ipmac_uadt,
.adt = {
[IPSET_ADD] = bitmap_ipmac_add,
[IPSET_DEL] = bitmap_ipmac_del,
[IPSET_TEST] = bitmap_ipmac_test,
},
.destroy = bitmap_ipmac_destroy,
.flush = bitmap_ipmac_flush,
.head = bitmap_ipmac_head,
.list = bitmap_ipmac_list,
.same_set = bitmap_ipmac_same_set,
/* Plain variant */
/* Timeout variant */
struct bitmap_ipmact_elem {
struct {
unsigned char ether[ETH_ALEN];
unsigned char filled;
} __attribute__ ((aligned));
unsigned long timeout;
};
static const struct ip_set_type_variant bitmap_tipmac = {
.kadt = bitmap_ipmac_kadt,
.uadt = bitmap_ipmac_uadt,
.adt = {
[IPSET_ADD] = bitmap_ipmac_tadd,
[IPSET_DEL] = bitmap_ipmac_tdel,
[IPSET_TEST] = bitmap_ipmac_ttest,
},
.destroy = bitmap_ipmac_destroy,
.flush = bitmap_ipmac_flush,
.head = bitmap_ipmac_head,
.list = bitmap_ipmac_tlist,
.same_set = bitmap_ipmac_same_set,
/* Plain variant with counter */
struct bitmap_ipmacc_elem {
struct {
unsigned char ether[ETH_ALEN];
unsigned char filled;
} __attribute__ ((aligned));
struct ip_set_counter counter;
};
static void
bitmap_ipmac_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_ipmac *map = set->data;
struct ipmac_telem *elem;
u32 id, last = map->last_ip - map->first_ip;
/* Timeout variant with counter */
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (id = 0; id <= last; id++) {
elem = bitmap_ipmac_elem(map, id);
if (elem->match == MAC_FILLED &&
ip_set_timeout_expired(elem->timeout))
elem->match = MAC_EMPTY;
}
read_unlock_bh(&set->lock);
struct bitmap_ipmacct_elem {
struct {
unsigned char ether[ETH_ALEN];
unsigned char filled;
} __attribute__ ((aligned));
unsigned long timeout;
struct ip_set_counter counter;
};
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
static void
bitmap_ipmac_gc_init(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = bitmap_ipmac_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
#include "ip_set_bitmap_gen.h"
/* Create bitmap:ip,mac type of sets */
static bool
init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
u32 first_ip, u32 last_ip)
u32 first_ip, u32 last_ip, u32 elements)
{
map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize);
if (!map->members)
return false;
if (map->dsize) {
map->extensions = ip_set_alloc(map->dsize * elements);
if (!map->extensions) {
kfree(map->members);
return false;
}
}
map->first_ip = first_ip;
map->last_ip = last_ip;
map->elements = elements;
map->timeout = IPSET_NO_TIMEOUT;
set->data = map;
@ -564,13 +355,14 @@ static int
bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
u32 flags)
{
u32 first_ip, last_ip;
u32 first_ip, last_ip, cadt_flags = 0;
u64 elements;
struct bitmap_ipmac *map;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip);
@ -605,28 +397,59 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->dsize = sizeof(struct ipmac_telem);
map->memsize = bitmap_bytes(0, elements - 1);
set->variant = &bitmap_ipmac;
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
set->extensions |= IPSET_EXT_COUNTER;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->dsize = sizeof(struct bitmap_ipmacct_elem);
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct bitmap_ipmacct_elem, timeout);
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct bitmap_ipmacct_elem, counter);
if (!init_map_ipmac(set, map, first_ip, last_ip)) {
if (!init_map_ipmac(set, map, first_ip, last_ip,
elements)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(
tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
} else {
map->dsize = sizeof(struct bitmap_ipmacc_elem);
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct bitmap_ipmacc_elem, counter);
if (!init_map_ipmac(set, map, first_ip, last_ip,
elements)) {
kfree(map);
return -ENOMEM;
}
}
} else if (tb[IPSET_ATTR_TIMEOUT]) {
map->dsize = sizeof(struct bitmap_ipmact_elem);
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct bitmap_ipmact_elem, timeout);
if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_tipmac;
bitmap_ipmac_gc_init(set);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
} else {
map->dsize = sizeof(struct ipmac_elem);
map->dsize = sizeof(struct bitmap_ipmac_elem);
if (!init_map_ipmac(set, map, first_ip, last_ip)) {
if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
kfree(map);
return -ENOMEM;
}
set->variant = &bitmap_ipmac;
}
return 0;
}
@ -645,6 +468,7 @@ static struct ip_set_type bitmap_ipmac_type = {
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -652,6 +476,8 @@ static struct ip_set_type bitmap_ipmac_type = {
.len = ETH_ALEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2013 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
@ -19,205 +19,94 @@
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_bitmap.h>
#include <linux/netfilter/ipset/ip_set_getport.h>
#define IP_SET_BITMAP_TIMEOUT
#include <linux/netfilter/ipset/ip_set_timeout.h>
#define REVISION_MIN 0
#define REVISION_MAX 0
#define REVISION_MAX 1 /* Counter support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("bitmap:port", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_bitmap:port");
#define MTYPE bitmap_port
/* Type structure */
struct bitmap_port {
void *members; /* the set members */
void *extensions; /* data extensions */
u16 first_port; /* host byte order, included in range */
u16 last_port; /* host byte order, included in range */
u32 elements; /* number of max elements in the set */
size_t memsize; /* members size */
size_t dsize; /* extensions struct size */
size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
/* Base variant */
/* ADT structure for generic function args */
struct bitmap_port_adt_elem {
u16 id;
};
static int
bitmap_port_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline u16
port_to_id(const struct bitmap_port *m, u16 port)
{
const struct bitmap_port *map = set->data;
u16 id = *(u16 *)value;
return port - m->first_port;
}
/* Common functions */
static inline int
bitmap_port_do_test(const struct bitmap_port_adt_elem *e,
const struct bitmap_port *map)
{
return !!test_bit(e->id, map->members);
}
static inline int
bitmap_port_gc_test(u16 id, const struct bitmap_port *map)
{
return !!test_bit(id, map->members);
}
static int
bitmap_port_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_port_do_add(const struct bitmap_port_adt_elem *e,
struct bitmap_port *map, u32 flags)
{
struct bitmap_port *map = set->data;
u16 id = *(u16 *)value;
if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
return !!test_and_set_bit(e->id, map->members);
}
static int
bitmap_port_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_port_do_del(const struct bitmap_port_adt_elem *e,
struct bitmap_port *map)
{
struct bitmap_port *map = set->data;
u16 id = *(u16 *)value;
if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST;
return 0;
return !test_and_clear_bit(e->id, map->members);
}
static int
bitmap_port_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
static inline int
bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id)
{
const struct bitmap_port *map = set->data;
struct nlattr *atd, *nested;
u16 id, first = cb->args[2];
u16 last = map->last_port - map->first_port;
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
if (!test_bit(id, map->members))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, atd);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
if (nla_put_net16(skb, IPSET_ATTR_PORT,
htons(map->first_port + id)))
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:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
return nla_put_net16(skb, IPSET_ATTR_PORT,
htons(map->first_port + id));
}
/* Timeout variant */
static int
bitmap_port_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
static inline int
bitmap_port_do_head(struct sk_buff *skb, const struct bitmap_port *map)
{
const struct bitmap_port *map = set->data;
const unsigned long *members = map->members;
u16 id = *(u16 *)value;
return ip_set_timeout_test(members[id]);
}
static int
bitmap_port_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_port *map = set->data;
unsigned long *members = map->members;
u16 id = *(u16 *)value;
if (ip_set_timeout_test(members[id]) && !(flags & IPSET_FLAG_EXIST))
return -IPSET_ERR_EXIST;
members[id] = ip_set_timeout_set(timeout);
return 0;
}
static int
bitmap_port_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
{
struct bitmap_port *map = set->data;
unsigned long *members = map->members;
u16 id = *(u16 *)value;
int ret = -IPSET_ERR_EXIST;
if (ip_set_timeout_test(members[id]))
ret = 0;
members[id] = IPSET_ELEM_UNSET;
return ret;
}
static int
bitmap_port_tlist(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb)
{
const struct bitmap_port *map = set->data;
struct nlattr *adt, *nested;
u16 id, first = cb->args[2];
u16 last = map->last_port - map->first_port;
const unsigned long *members = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!adt)
return -EMSGSIZE;
for (; cb->args[2] <= last; cb->args[2]++) {
id = cb->args[2];
if (!ip_set_timeout_test(members[id]))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
if (id == first) {
nla_nest_cancel(skb, adt);
return -EMSGSIZE;
} else
goto nla_put_failure;
}
if (nla_put_net16(skb, IPSET_ATTR_PORT,
htons(map->first_port + id)) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(members[id]))))
goto nla_put_failure;
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, adt);
/* Set listing finished */
cb->args[2] = 0;
return 0;
nla_put_failure:
nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
if (unlikely(id == first)) {
cb->args[2] = 0;
return -EMSGSIZE;
}
return 0;
return nla_put_net16(skb, IPSET_ATTR_PORT, htons(map->first_port)) ||
nla_put_net16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port));
}
static int
bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
struct bitmap_port *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct bitmap_port_adt_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
__be16 __port;
u16 port = 0;
@ -230,9 +119,9 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
port -= map->first_port;
e.id = port_to_id(map, port);
return adtfn(set, &port, opt_timeout(opt, map), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
@ -241,14 +130,17 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
{
struct bitmap_port *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 timeout = map->timeout;
struct bitmap_port_adt_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
u32 port; /* wraparound */
u16 id, port_to;
u16 port_to;
int ret = 0;
if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
@ -257,16 +149,13 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
if (port < map->first_port || port > map->last_port)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(map->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
ret = ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (adt == IPSET_TEST) {
id = port - map->first_port;
return adtfn(set, &id, timeout, flags);
e.id = port_to_id(map, port);
return adtfn(set, &e, &ext, &ext, flags);
}
if (tb[IPSET_ATTR_PORT_TO]) {
@ -283,8 +172,8 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
return -IPSET_ERR_BITMAP_RANGE;
for (; port <= port_to; port++) {
id = port - map->first_port;
ret = adtfn(set, &id, timeout, flags);
e.id = port_to_id(map, port);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -294,52 +183,6 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static void
bitmap_port_destroy(struct ip_set *set)
{
struct bitmap_port *map = set->data;
if (with_timeout(map->timeout))
del_timer_sync(&map->gc);
ip_set_free(map->members);
kfree(map);
set->data = NULL;
}
static void
bitmap_port_flush(struct ip_set *set)
{
struct bitmap_port *map = set->data;
memset(map->members, 0, map->memsize);
}
static int
bitmap_port_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_port *map = set->data;
struct nlattr *nested;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
if (nla_put_net16(skb, IPSET_ATTR_PORT, htons(map->first_port)) ||
nla_put_net16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)) ||
nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->memsize)) ||
(with_timeout(map->timeout) &&
nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
goto nla_put_failure;
ipset_nest_end(skb, nested);
return 0;
nla_put_failure:
return -EMSGSIZE;
}
static bool
bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
{
@ -348,71 +191,35 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
return x->first_port == y->first_port &&
x->last_port == y->last_port &&
x->timeout == y->timeout;
x->timeout == y->timeout &&
a->extensions == b->extensions;
}
static const struct ip_set_type_variant bitmap_port = {
.kadt = bitmap_port_kadt,
.uadt = bitmap_port_uadt,
.adt = {
[IPSET_ADD] = bitmap_port_add,
[IPSET_DEL] = bitmap_port_del,
[IPSET_TEST] = bitmap_port_test,
},
.destroy = bitmap_port_destroy,
.flush = bitmap_port_flush,
.head = bitmap_port_head,
.list = bitmap_port_list,
.same_set = bitmap_port_same_set,
/* Plain variant */
struct bitmap_port_elem {
};
static const struct ip_set_type_variant bitmap_tport = {
.kadt = bitmap_port_kadt,
.uadt = bitmap_port_uadt,
.adt = {
[IPSET_ADD] = bitmap_port_tadd,
[IPSET_DEL] = bitmap_port_tdel,
[IPSET_TEST] = bitmap_port_ttest,
},
.destroy = bitmap_port_destroy,
.flush = bitmap_port_flush,
.head = bitmap_port_head,
.list = bitmap_port_tlist,
.same_set = bitmap_port_same_set,
/* Timeout variant */
struct bitmap_portt_elem {
unsigned long timeout;
};
static void
bitmap_port_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_port *map = set->data;
unsigned long *table = map->members;
u32 id; /* wraparound */
u16 last = map->last_port - map->first_port;
/* Plain variant with counter */
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
read_lock_bh(&set->lock);
for (id = 0; id <= last; id++)
if (ip_set_timeout_expired(table[id]))
table[id] = IPSET_ELEM_UNSET;
read_unlock_bh(&set->lock);
struct bitmap_portc_elem {
struct ip_set_counter counter;
};
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
/* Timeout variant with counter */
static void
bitmap_port_gc_init(struct ip_set *set)
{
struct bitmap_port *map = set->data;
struct bitmap_portct_elem {
unsigned long timeout;
struct ip_set_counter counter;
};
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = bitmap_port_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
#include "ip_set_bitmap_gen.h"
/* Create bitmap:ip type of sets */
@ -423,6 +230,13 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
map->members = ip_set_alloc(map->memsize);
if (!map->members)
return false;
if (map->dsize) {
map->extensions = ip_set_alloc(map->dsize * map->elements);
if (!map->extensions) {
kfree(map->members);
return false;
}
}
map->first_port = first_port;
map->last_port = last_port;
map->timeout = IPSET_NO_TIMEOUT;
@ -434,15 +248,16 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
}
static int
bitmap_port_create(struct ip_set *set, struct nlattr *tb[],
u32 flags)
bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct bitmap_port *map;
u16 first_port, last_port;
u32 cadt_flags = 0;
if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
first_port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
@ -458,28 +273,56 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[],
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->memsize = (last_port - first_port + 1)
* sizeof(unsigned long);
map->elements = last_port - first_port + 1;
map->memsize = map->elements * sizeof(unsigned long);
set->variant = &bitmap_port;
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
set->extensions |= IPSET_EXT_COUNTER;
if (tb[IPSET_ATTR_TIMEOUT]) {
map->dsize = sizeof(struct bitmap_portct_elem);
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct bitmap_portct_elem, timeout);
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct bitmap_portct_elem, counter);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
map->timeout =
ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_port_gc_init(set, bitmap_port_gc);
} else {
map->dsize = sizeof(struct bitmap_portc_elem);
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct bitmap_portc_elem, counter);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
}
} else if (tb[IPSET_ATTR_TIMEOUT]) {
map->dsize = sizeof(struct bitmap_portt_elem);
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct bitmap_portt_elem, timeout);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_tport;
bitmap_port_gc_init(set);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_port_gc_init(set, bitmap_port_gc);
} else {
map->memsize = bitmap_bytes(0, last_port - first_port);
pr_debug("memsize: %zu\n", map->memsize);
map->dsize = 0;
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
set->variant = &bitmap_port;
}
return 0;
}
@ -497,12 +340,15 @@ static struct ip_set_type bitmap_port_type = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,6 +1,6 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2013 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
@ -315,6 +315,29 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr)
}
EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
int
ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
struct ip_set_ext *ext)
{
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!(set->extensions & IPSET_EXT_TIMEOUT))
return -IPSET_ERR_TIMEOUT;
ext->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (tb[IPSET_ATTR_BYTES] || tb[IPSET_ATTR_PACKETS]) {
if (!(set->extensions & IPSET_EXT_COUNTER))
return -IPSET_ERR_COUNTER;
if (tb[IPSET_ATTR_BYTES])
ext->bytes = be64_to_cpu(nla_get_be64(
tb[IPSET_ATTR_BYTES]));
if (tb[IPSET_ATTR_PACKETS])
ext->packets = be64_to_cpu(nla_get_be64(
tb[IPSET_ATTR_PACKETS]));
}
return 0;
}
EXPORT_SYMBOL_GPL(ip_set_get_extensions);
/*
* Creating/destroying/renaming/swapping affect the existence and
* the properties of a set. All of these can be executed from userspace
@ -365,8 +388,7 @@ ip_set_rcu_get(ip_set_id_t index)
int
ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt)
const struct xt_action_param *par, struct ip_set_adt_opt *opt)
{
struct ip_set *set = ip_set_rcu_get(index);
int ret = 0;
@ -391,7 +413,7 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
ret = 1;
} else {
/* --return-nomatch: invert matched element */
if ((opt->flags & IPSET_RETURN_NOMATCH) &&
if ((opt->cmdflags & IPSET_FLAG_RETURN_NOMATCH) &&
(set->type->features & IPSET_TYPE_NOMATCH) &&
(ret > 0 || ret == -ENOTEMPTY))
ret = -ret;
@ -404,8 +426,7 @@ EXPORT_SYMBOL_GPL(ip_set_test);
int
ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt)
const struct xt_action_param *par, struct ip_set_adt_opt *opt)
{
struct ip_set *set = ip_set_rcu_get(index);
int ret;
@ -427,8 +448,7 @@ EXPORT_SYMBOL_GPL(ip_set_add);
int
ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt)
const struct xt_action_param *par, struct ip_set_adt_opt *opt)
{
struct ip_set *set = ip_set_rcu_get(index);
int ret = 0;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2013 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
@ -21,11 +21,10 @@
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0
#define REVISION_MAX 0
#define REVISION_MAX 1 /* Counters support */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@ -33,58 +32,47 @@ IP_SET_MODULE_DESC("hash:ip", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_hash:ip");
/* Type specific function prefix */
#define TYPE hash_ip
#define HTYPE hash_ip
#define IP_SET_HASH_WITH_NETMASK
static bool
hash_ip_same_set(const struct ip_set *a, const struct ip_set *b);
/* IPv4 variants */
#define hash_ip4_same_set hash_ip_same_set
#define hash_ip6_same_set hash_ip_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
/* Member elements */
struct hash_ip4_elem {
/* Zero valued IP addresses cannot be stored */
__be32 ip;
};
/* Member elements with timeout support */
struct hash_ip4_telem {
struct hash_ip4t_elem {
__be32 ip;
unsigned long timeout;
};
struct hash_ip4c_elem {
__be32 ip;
struct ip_set_counter counter;
};
struct hash_ip4ct_elem {
__be32 ip;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_ip4_data_equal(const struct hash_ip4_elem *ip1,
const struct hash_ip4_elem *ip2,
hash_ip4_data_equal(const struct hash_ip4_elem *e1,
const struct hash_ip4_elem *e2,
u32 *multi)
{
return ip1->ip == ip2->ip;
return e1->ip == e2->ip;
}
static inline bool
hash_ip4_data_isnull(const struct hash_ip4_elem *elem)
hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *e)
{
return elem->ip == 0;
}
static inline void
hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src)
{
dst->ip = src->ip;
}
/* Zero valued IP addresses cannot be stored */
static inline void
hash_ip4_data_zero_out(struct hash_ip4_elem *elem)
{
elem->ip = 0;
}
static inline bool
hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data)
{
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip))
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, e->ip))
goto nla_put_failure;
return 0;
@ -92,41 +80,26 @@ nla_put_failure:
return 1;
}
static bool
hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data)
static inline void
hash_ip4_data_next(struct hash_ip4_elem *next, const struct hash_ip4_elem *e)
{
const struct hash_ip4_telem *tdata =
(const struct hash_ip4_telem *)data;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
next->ip = e->ip;
}
#define IP_SET_HASH_WITH_NETMASK
#define MTYPE hash_ip4
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ipset/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 = d->ip;
}
#include "ip_set_hash_gen.h"
static int
hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ip4_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
__be32 ip;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
@ -134,43 +107,42 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
if (ip == 0)
return -EINVAL;
return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags);
e.ip = ip;
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip, ip_to, hosts, timeout = h->timeout;
__be32 nip;
struct hash_ip4_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 ip, ip_to, hosts;
int ret = 0;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
ip &= ip_set_hostmask(h->netmask);
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) {
nip = htonl(ip);
if (nip == 0)
e.ip = htonl(ip);
if (e.ip == 0)
return -IPSET_ERR_HASH_ELEM;
return adtfn(set, &nip, timeout, flags);
return adtfn(set, &e, &ext, &ext, flags);
}
ip_to = ip;
@ -193,10 +165,10 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
ip = ntohl(h->next.ip);
for (; !before(ip_to, ip); ip += hosts) {
nip = htonl(ip);
if (nip == 0)
e.ip = htonl(ip);
if (e.ip == 0)
return -IPSET_ERR_HASH_ELEM;
ret = adtfn(set, &nip, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -206,29 +178,31 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static bool
hash_ip_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct ip_set_hash *x = a->data;
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
x->timeout == y->timeout &&
x->netmask == y->netmask;
}
/* The type variant functions: IPv6 */
/* IPv6 variants */
/* Member elements */
struct hash_ip6_elem {
union nf_inet_addr ip;
};
struct hash_ip6_telem {
struct hash_ip6t_elem {
union nf_inet_addr ip;
unsigned long timeout;
};
struct hash_ip6c_elem {
union nf_inet_addr ip;
struct ip_set_counter counter;
};
struct hash_ip6ct_elem {
union nf_inet_addr ip;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
const struct hash_ip6_elem *ip2,
@ -237,37 +211,16 @@ hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6);
}
static inline bool
hash_ip6_data_isnull(const struct hash_ip6_elem *elem)
{
return ipv6_addr_any(&elem->ip.in6);
}
static inline void
hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src)
hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
dst->ip.in6 = src->ip.in6;
}
static inline void
hash_ip6_data_zero_out(struct hash_ip6_elem *elem)
{
ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0);
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
ip6_netmask(ip, prefix);
}
static bool
hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data)
hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *e)
{
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6))
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6))
goto nla_put_failure;
return 0;
@ -275,69 +228,55 @@ nla_put_failure:
return 1;
}
static bool
hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data)
static inline void
hash_ip6_data_next(struct hash_ip4_elem *next, const struct hash_ip6_elem *e)
{
const struct hash_ip6_telem *e =
(const struct hash_ip6_telem *)data;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#undef HKEY_DATALEN
#define MTYPE hash_ip6
#define PF 6
#define HOST_MASK 128
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_ip6_data_next(struct ip_set_hash *h, const struct hash_ip6_elem *d)
{
}
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip;
struct hash_ip6_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip.in6);
ip6_netmask(&ip, h->netmask);
if (ipv6_addr_any(&ip.in6))
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
hash_ip6_netmask(&e.ip, h->netmask);
if (ipv6_addr_any(&e.ip.in6))
return -EINVAL;
return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip;
u32 timeout = h->timeout;
struct hash_ip6_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
@ -345,110 +284,20 @@ hash_ip6_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_ipaddr6(tb[IPSET_ATTR_IP], &ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
ip6_netmask(&ip, h->netmask);
if (ipv6_addr_any(&ip.in6))
hash_ip6_netmask(&e.ip, h->netmask);
if (ipv6_addr_any(&e.ip.in6))
return -IPSET_ERR_HASH_ELEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
ret = adtfn(set, &ip, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
static int
hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 netmask, hbits;
size_t hsize;
struct ip_set_hash *h;
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
pr_debug("Create set %s with family %s\n",
set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6");
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
if (tb[IPSET_ATTR_NETMASK]) {
netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
if ((set->family == NFPROTO_IPV4 && netmask > 32) ||
(set->family == NFPROTO_IPV6 && netmask > 128) ||
netmask == 0)
return -IPSET_ERR_INVALID_NETMASK;
}
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
h->netmask = netmask;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
hsize = htable_size(hbits);
if (hsize == 0) {
kfree(h);
return -ENOMEM;
}
h->table = ip_set_alloc(hsize);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
set->data = h;
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == NFPROTO_IPV4
? &hash_ip4_tvariant : &hash_ip6_tvariant;
if (set->family == NFPROTO_IPV4)
hash_ip4_gc_init(set);
else
hash_ip6_gc_init(set);
} else {
set->variant = set->family == NFPROTO_IPV4
? &hash_ip4_variant : &hash_ip6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
return 0;
}
static struct ip_set_type hash_ip_type __read_mostly = {
.name = "hash:ip",
.protocol = IPSET_PROTOCOL,
@ -465,6 +314,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -472,6 +322,8 @@ static struct ip_set_type hash_ip_type __read_mostly = {
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2013 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
@ -21,12 +21,12 @@
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0
#define REVISION_MAX 1 /* SCTP and UDPLITE support added */
/* 1 SCTP and UDPLITE support added */
#define REVISION_MAX 2 /* Counters support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@ -34,17 +34,11 @@ IP_SET_MODULE_DESC("hash:ip,port", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_hash:ip,port");
/* Type specific function prefix */
#define TYPE hash_ipport
#define HTYPE hash_ipport
static bool
hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b);
/* IPv4 variants */
#define hash_ipport4_same_set hash_ipport_same_set
#define hash_ipport6_same_set hash_ipport_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
/* Member elements */
struct hash_ipport4_elem {
__be32 ip;
__be16 port;
@ -52,8 +46,7 @@ struct hash_ipport4_elem {
u8 padding;
};
/* Member elements with timeout support */
struct hash_ipport4_telem {
struct hash_ipport4t_elem {
__be32 ip;
__be16 port;
u8 proto;
@ -61,6 +54,25 @@ struct hash_ipport4_telem {
unsigned long timeout;
};
struct hash_ipport4c_elem {
__be32 ip;
__be16 port;
u8 proto;
u8 padding;
struct ip_set_counter counter;
};
struct hash_ipport4ct_elem {
__be32 ip;
__be16 port;
u8 proto;
u8 padding;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
const struct hash_ipport4_elem *ip2,
@ -71,27 +83,6 @@ hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
ip1->proto == ip2->proto;
}
static inline bool
hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipport4_data_copy(struct hash_ipport4_elem *dst,
const struct hash_ipport4_elem *src)
{
dst->ip = src->ip;
dst->port = src->port;
dst->proto = src->proto;
}
static inline void
hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem)
{
elem->proto = 0;
}
static bool
hash_ipport4_data_list(struct sk_buff *skb,
const struct hash_ipport4_elem *data)
@ -106,111 +97,91 @@ nla_put_failure:
return 1;
}
static bool
hash_ipport4_data_tlist(struct sk_buff *skb,
const struct hash_ipport4_elem *data)
{
const struct hash_ipport4_telem *tdata =
(const struct hash_ipport4_telem *)data;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_ipport4_data_next(struct ip_set_hash *h,
hash_ipport4_data_next(struct hash_ipport4_elem *next,
const struct hash_ipport4_elem *d)
{
h->next.ip = d->ip;
h->next.port = d->port;
next->ip = d->ip;
next->port = d->port;
}
#define MTYPE hash_ipport4
#define PF 4
#define HOST_MASK 32
#define HKEY_DATALEN sizeof(struct hash_ipport4_elem)
#include "ip_set_hash_gen.h"
static int
hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
struct hash_ipport4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
&e.port, &e.proto))
return -EINVAL;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
struct hash_ipport4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 ip, ip_to, p = 0, port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (data.proto == 0)
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || data.proto == IPPROTO_ICMP))
data.port = 0;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (!(with_ports || e.proto == IPPROTO_ICMP))
e.port = 0;
if (adt == IPSET_TEST ||
!(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip_to = ip = ntohl(data.ip);
ip_to = ip = ntohl(e.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
@ -225,7 +196,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
ip_set_mask_from_to(ip, ip_to, cidr);
}
port_to = port = ntohs(data.port);
port_to = port = ntohs(e.port);
if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
@ -238,9 +209,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
: port;
for (; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout, flags);
e.ip = htonl(ip);
e.port = htons(p);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -251,18 +222,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static bool
hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct ip_set_hash *x = a->data;
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
/* IPv6 variants */
struct hash_ipport6_elem {
union nf_inet_addr ip;
@ -271,7 +231,7 @@ struct hash_ipport6_elem {
u8 padding;
};
struct hash_ipport6_telem {
struct hash_ipport6t_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
@ -279,6 +239,25 @@ struct hash_ipport6_telem {
unsigned long timeout;
};
struct hash_ipport6c_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
u8 padding;
struct ip_set_counter counter;
};
struct hash_ipport6ct_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
u8 padding;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
const struct hash_ipport6_elem *ip2,
@ -289,25 +268,6 @@ hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
ip1->proto == ip2->proto;
}
static inline bool
hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipport6_data_copy(struct hash_ipport6_elem *dst,
const struct hash_ipport6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem)
{
elem->proto = 0;
}
static bool
hash_ipport6_data_list(struct sk_buff *skb,
const struct hash_ipport6_elem *data)
@ -322,66 +282,52 @@ nla_put_failure:
return 1;
}
static bool
hash_ipport6_data_tlist(struct sk_buff *skb,
const struct hash_ipport6_elem *data)
{
const struct hash_ipport6_telem *e =
(const struct hash_ipport6_telem *)data;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#undef PF
#undef HOST_MASK
#define PF 6
#define HOST_MASK 128
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_ipport6_data_next(struct ip_set_hash *h,
hash_ipport6_data_next(struct hash_ipport4_elem *next,
const struct hash_ipport6_elem *d)
{
h->next.port = d->port;
next->port = d->port;
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#undef HKEY_DATALEN
#define MTYPE hash_ipport6
#define PF 6
#define HOST_MASK 128
#define HKEY_DATALEN sizeof(struct hash_ipport6_elem)
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { };
struct hash_ipport6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
&e.port, &e.proto))
return -EINVAL;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem data = { };
struct hash_ipport6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
@ -389,6 +335,8 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
@ -396,39 +344,34 @@ hash_ipport6_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_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (data.proto == 0)
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || data.proto == IPPROTO_ICMPV6))
data.port = 0;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (!(with_ports || e.proto == IPPROTO_ICMPV6))
e.port = 0;
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port = ntohs(e.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
@ -436,8 +379,8 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
port = ntohs(h->next.port);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout, flags);
e.port = htons(port);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -447,78 +390,6 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
/* Create hash:ip type of sets */
static int
hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
size_t hsize;
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
hsize = htable_size(hbits);
if (hsize == 0) {
kfree(h);
return -ENOMEM;
}
h->table = ip_set_alloc(hsize);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
set->data = h;
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == NFPROTO_IPV4
? &hash_ipport4_tvariant : &hash_ipport6_tvariant;
if (set->family == NFPROTO_IPV4)
hash_ipport4_gc_init(set);
else
hash_ipport6_gc_init(set);
} else {
set->variant = set->family == NFPROTO_IPV4
? &hash_ipport4_variant : &hash_ipport6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
return 0;
}
static struct ip_set_type hash_ipport_type __read_mostly = {
.name = "hash:ip,port",
.protocol = IPSET_PROTOCOL,
@ -535,6 +406,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -545,6 +417,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2013 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
@ -21,12 +21,12 @@
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0
#define REVISION_MAX 1 /* SCTP and UDPLITE support added */
/* 1 SCTP and UDPLITE support added */
#define REVISION_MAX 2 /* Counters support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@ -34,17 +34,11 @@ IP_SET_MODULE_DESC("hash:ip,port,ip", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_hash:ip,port,ip");
/* Type specific function prefix */
#define TYPE hash_ipportip
#define HTYPE hash_ipportip
static bool
hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b);
/* IPv4 variants */
#define hash_ipportip4_same_set hash_ipportip_same_set
#define hash_ipportip6_same_set hash_ipportip_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
/* Member elements */
struct hash_ipportip4_elem {
__be32 ip;
__be32 ip2;
@ -53,8 +47,7 @@ struct hash_ipportip4_elem {
u8 padding;
};
/* Member elements with timeout support */
struct hash_ipportip4_telem {
struct hash_ipportip4t_elem {
__be32 ip;
__be32 ip2;
__be16 port;
@ -63,6 +56,25 @@ struct hash_ipportip4_telem {
unsigned long timeout;
};
struct hash_ipportip4c_elem {
__be32 ip;
__be32 ip2;
__be16 port;
u8 proto;
u8 padding;
struct ip_set_counter counter;
};
struct hash_ipportip4ct_elem {
__be32 ip;
__be32 ip2;
__be16 port;
u8 proto;
u8 padding;
struct ip_set_counter counter;
unsigned long timeout;
};
static inline bool
hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
const struct hash_ipportip4_elem *ip2,
@ -74,25 +86,6 @@ hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
ip1->proto == ip2->proto;
}
static inline bool
hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst,
const struct hash_ipportip4_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem)
{
elem->proto = 0;
}
static bool
hash_ipportip4_data_list(struct sk_buff *skb,
const struct hash_ipportip4_elem *data)
@ -108,117 +101,96 @@ nla_put_failure:
return 1;
}
static bool
hash_ipportip4_data_tlist(struct sk_buff *skb,
const struct hash_ipportip4_elem *data)
{
const struct hash_ipportip4_telem *tdata =
(const struct hash_ipportip4_telem *)data;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
nla_put_ipaddr4(skb, IPSET_ATTR_IP2, tdata->ip2) ||
nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_ipportip4_data_next(struct ip_set_hash *h,
hash_ipportip4_data_next(struct hash_ipportip4_elem *next,
const struct hash_ipportip4_elem *d)
{
h->next.ip = d->ip;
h->next.port = d->port;
next->ip = d->ip;
next->port = d->port;
}
/* Common functions */
#define MTYPE hash_ipportip4
#define PF 4
#define HOST_MASK 32
#include "ip_set_hash_gen.h"
static int
hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipportip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
struct hash_ipportip4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
&e.port, &e.proto))
return -EINVAL;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipportip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
struct hash_ipportip4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 ip, ip_to, p = 0, port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2);
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &e.ip2);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (data.proto == 0)
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || data.proto == IPPROTO_ICMP))
data.port = 0;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (!(with_ports || e.proto == IPPROTO_ICMP))
e.port = 0;
if (adt == IPSET_TEST ||
!(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
tb[IPSET_ATTR_PORT_TO])) {
ret = adtfn(set, &data, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
ip_to = ip = ntohl(data.ip);
ip_to = ip = ntohl(e.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
@ -233,7 +205,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
ip_set_mask_from_to(ip, ip_to, cidr);
}
port_to = port = ntohs(data.port);
port_to = port = ntohs(e.port);
if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
@ -246,9 +218,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
: port;
for (; p <= port_to; p++) {
data.ip = htonl(ip);
data.port = htons(p);
ret = adtfn(set, &data, timeout, flags);
e.ip = htonl(ip);
e.port = htons(p);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -259,18 +231,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static bool
hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct ip_set_hash *x = a->data;
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
/* IPv6 variants */
struct hash_ipportip6_elem {
union nf_inet_addr ip;
@ -280,7 +241,7 @@ struct hash_ipportip6_elem {
u8 padding;
};
struct hash_ipportip6_telem {
struct hash_ipportip6t_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
@ -289,6 +250,27 @@ struct hash_ipportip6_telem {
unsigned long timeout;
};
struct hash_ipportip6c_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
u8 proto;
u8 padding;
struct ip_set_counter counter;
};
struct hash_ipportip6ct_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
u8 proto;
u8 padding;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
const struct hash_ipportip6_elem *ip2,
@ -300,25 +282,6 @@ hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
ip1->proto == ip2->proto;
}
static inline bool
hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst,
const struct hash_ipportip6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem)
{
elem->proto = 0;
}
static bool
hash_ipportip6_data_list(struct sk_buff *skb,
const struct hash_ipportip6_elem *data)
@ -334,68 +297,51 @@ nla_put_failure:
return 1;
}
static bool
hash_ipportip6_data_tlist(struct sk_buff *skb,
const struct hash_ipportip6_elem *data)
static inline void
hash_ipportip6_data_next(struct hash_ipportip4_elem *next,
const struct hash_ipportip6_elem *d)
{
const struct hash_ipportip6_telem *e =
(const struct hash_ipportip6_telem *)data;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) ||
nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
next->port = d->port;
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#define MTYPE hash_ipportip6
#define PF 6
#define HOST_MASK 128
#include <linux/netfilter/ipset/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 = d->port;
}
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipportip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { };
struct hash_ipportip6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
&e.port, &e.proto))
return -EINVAL;
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, opt_timeout(opt, h), opt->cmdflags);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipportip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem data = { };
struct hash_ipportip6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
@ -403,6 +349,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
@ -410,43 +358,38 @@ hash_ipportip6_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_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2);
if (ret)
return ret;
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (data.proto == 0)
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || data.proto == IPPROTO_ICMPV6))
data.port = 0;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (!(with_ports || e.proto == IPPROTO_ICMPV6))
e.port = 0;
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port = ntohs(e.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
@ -454,8 +397,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
port = ntohs(h->next.port);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout, flags);
e.port = htons(port);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -465,78 +408,6 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
/* Create hash:ip type of sets */
static int
hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
size_t hsize;
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
hsize = htable_size(hbits);
if (hsize == 0) {
kfree(h);
return -ENOMEM;
}
h->table = ip_set_alloc(hsize);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
set->data = h;
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == NFPROTO_IPV4
? &hash_ipportip4_tvariant : &hash_ipportip6_tvariant;
if (set->family == NFPROTO_IPV4)
hash_ipportip4_gc_init(set);
else
hash_ipportip6_gc_init(set);
} else {
set->variant = set->family == NFPROTO_IPV4
? &hash_ipportip4_variant : &hash_ipportip6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
return 0;
}
static struct ip_set_type hash_ipportip_type __read_mostly = {
.name = "hash:ip,port,ip",
.protocol = IPSET_PROTOCOL,
@ -552,6 +423,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -563,6 +435,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2013 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
@ -21,14 +21,14 @@
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0
/* 1 SCTP and UDPLITE support added */
/* 2 Range as input support for IPv4 added */
#define REVISION_MAX 3 /* nomatch flag support added */
/* 3 nomatch flag support added */
#define REVISION_MAX 4 /* Counters support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@ -36,23 +36,19 @@ IP_SET_MODULE_DESC("hash:ip,port,net", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_hash:ip,port,net");
/* Type specific function prefix */
#define TYPE hash_ipportnet
static bool
hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_ipportnet4_same_set hash_ipportnet_same_set
#define hash_ipportnet6_same_set hash_ipportnet_same_set
/* The type variant functions: IPv4 */
#define HTYPE hash_ipportnet
/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
* However this way we have to store internally cidr - 1,
* dancing back and forth.
*/
#define IP_SET_HASH_WITH_NETS_PACKED
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
/* Member elements without timeout */
/* IPv4 variants */
/* Member elements */
struct hash_ipportnet4_elem {
__be32 ip;
__be32 ip2;
@ -62,8 +58,7 @@ struct hash_ipportnet4_elem {
u8 proto;
};
/* Member elements with timeout support */
struct hash_ipportnet4_telem {
struct hash_ipportnet4t_elem {
__be32 ip;
__be32 ip2;
__be16 port;
@ -73,6 +68,29 @@ struct hash_ipportnet4_telem {
unsigned long timeout;
};
struct hash_ipportnet4c_elem {
__be32 ip;
__be32 ip2;
__be16 port;
u8 cidr:7;
u8 nomatch:1;
u8 proto;
struct ip_set_counter counter;
};
struct hash_ipportnet4ct_elem {
__be32 ip;
__be32 ip2;
__be16 port;
u8 cidr:7;
u8 nomatch:1;
u8 proto;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
const struct hash_ipportnet4_elem *ip2,
@ -85,40 +103,24 @@ hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
ip1->proto == ip2->proto;
}
static inline bool
hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
const struct hash_ipportnet4_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportnet4_data_flags(struct hash_ipportnet4_elem *dst, u32 flags)
{
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int
hash_ipportnet4_data_match(const struct hash_ipportnet4_elem *elem)
hash_ipportnet4_do_data_match(const struct hash_ipportnet4_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_ipportnet4_data_set_flags(struct hash_ipportnet4_elem *elem, u32 flags)
{
elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
}
static inline void
hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *elem, u8 *flags)
{
swap(*flags, elem->nomatch);
}
static inline void
hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
{
@ -126,12 +128,6 @@ hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
elem->cidr = cidr - 1;
}
static inline void
hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem)
{
elem->proto = 0;
}
static bool
hash_ipportnet4_data_list(struct sk_buff *skb,
const struct hash_ipportnet4_elem *data)
@ -152,81 +148,56 @@ nla_put_failure:
return 1;
}
static bool
hash_ipportnet4_data_tlist(struct sk_buff *skb,
const struct hash_ipportnet4_elem *data)
{
const struct hash_ipportnet4_telem *tdata =
(const struct hash_ipportnet4_telem *)data;
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
nla_put_ipaddr4(skb, IPSET_ATTR_IP2, tdata->ip2) ||
nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) ||
nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout))) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_ipportnet4_data_next(struct ip_set_hash *h,
hash_ipportnet4_data_next(struct hash_ipportnet4_elem *next,
const struct hash_ipportnet4_elem *d)
{
h->next.ip = d->ip;
h->next.port = d->port;
h->next.ip2 = d->ip2;
next->ip = d->ip;
next->port = d->port;
next->ip2 = d->ip2;
}
#define MTYPE hash_ipportnet4
#define PF 4
#define HOST_MASK 32
#include "ip_set_hash_gen.h"
static int
hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = {
struct hash_ipportnet4_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (adt == IPSET_TEST)
data.cidr = HOST_MASK - 1;
e.cidr = HOST_MASK - 1;
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
&e.port, &e.proto))
return -EINVAL;
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 + 1);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
e.ip2 &= ip_set_netmask(e.cidr + 1);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = { .cidr = HOST_MASK - 1 };
struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 ip, ip_to, p = 0, port, port_to;
u32 ip2_from, ip2_to, ip2_last, ip2;
u32 timeout = h->timeout;
bool with_ports = false;
u8 cidr;
int ret;
@ -235,13 +206,16 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
@ -253,46 +227,41 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
data.cidr = cidr - 1;
e.cidr = cidr - 1;
}
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (data.proto == 0)
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || data.proto == IPPROTO_ICMP))
data.port = 0;
if (!(with_ports || e.proto == IPPROTO_ICMP))
e.port = 0;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_NOMATCH)
flags |= (cadt_flags << 16);
flags |= (IPSET_FLAG_NOMATCH << 16);
}
with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
if (adt == IPSET_TEST ||
!(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 + 1));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
e.ip = htonl(ip);
e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1));
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt) ? 1 :
ip_set_eexist(ret, flags) ? 0 : ret;
}
ip_to = ip;
@ -310,7 +279,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
ip_set_mask_from_to(ip, ip_to, cidr);
}
port_to = port = ntohs(data.port);
port_to = port = ntohs(e.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
@ -326,28 +295,27 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
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 + 1);
}
} else
ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1);
if (retried)
ip = ntohl(h->next.ip);
for (; !before(ip_to, ip); ip++) {
data.ip = htonl(ip);
e.ip = htonl(ip);
p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
: port;
for (; p <= port_to; p++) {
data.port = htons(p);
e.port = htons(p);
ip2 = retried
&& ip == ntohl(h->next.ip)
&& p == ntohs(h->next.port)
? ntohl(h->next.ip2) : ip2_from;
while (!after(ip2, ip2_to)) {
data.ip2 = htonl(ip2);
e.ip2 = htonl(ip2);
ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
&cidr);
data.cidr = cidr - 1;
ret = adtfn(set, &data, timeout, flags);
e.cidr = cidr - 1;
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -360,18 +328,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static bool
hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct ip_set_hash *x = a->data;
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
/* IPv6 variants */
struct hash_ipportnet6_elem {
union nf_inet_addr ip;
@ -382,7 +339,7 @@ struct hash_ipportnet6_elem {
u8 proto;
};
struct hash_ipportnet6_telem {
struct hash_ipportnet6t_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
@ -392,6 +349,29 @@ struct hash_ipportnet6_telem {
unsigned long timeout;
};
struct hash_ipportnet6c_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
u8 cidr:7;
u8 nomatch:1;
u8 proto;
struct ip_set_counter counter;
};
struct hash_ipportnet6ct_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
u8 cidr:7;
u8 nomatch:1;
u8 proto;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
const struct hash_ipportnet6_elem *ip2,
@ -404,53 +384,22 @@ hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
ip1->proto == ip2->proto;
}
static inline bool
hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
const struct hash_ipportnet6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_ipportnet6_data_flags(struct hash_ipportnet6_elem *dst, u32 flags)
{
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int
hash_ipportnet6_data_match(const struct hash_ipportnet6_elem *elem)
hash_ipportnet6_do_data_match(const struct hash_ipportnet6_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
hash_ipportnet6_data_set_flags(struct hash_ipportnet6_elem *elem, u32 flags)
{
elem->proto = 0;
elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *elem, u8 *flags)
{
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
swap(*flags, elem->nomatch);
}
static inline void
@ -480,78 +429,58 @@ nla_put_failure:
return 1;
}
static bool
hash_ipportnet6_data_tlist(struct sk_buff *skb,
const struct hash_ipportnet6_elem *data)
static inline void
hash_ipportnet6_data_next(struct hash_ipportnet4_elem *next,
const struct hash_ipportnet6_elem *d)
{
const struct hash_ipportnet6_telem *e =
(const struct hash_ipportnet6_telem *)data;
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) ||
nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout))) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
next->port = d->port;
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#define MTYPE hash_ipportnet6
#define PF 6
#define HOST_MASK 128
#include <linux/netfilter/ipset/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 = d->port;
}
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data = {
struct hash_ipportnet6_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (adt == IPSET_TEST)
data.cidr = HOST_MASK - 1;
e.cidr = HOST_MASK - 1;
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
&e.port, &e.proto))
return -EINVAL;
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 + 1);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
ip6_netmask(&e.ip2, e.cidr + 1);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_ipportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data = { .cidr = HOST_MASK - 1 };
struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
u8 cidr;
int ret;
@ -561,6 +490,8 @@ hash_ipportnet6_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) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
@ -570,11 +501,12 @@ hash_ipportnet6_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_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2);
if (ret)
return ret;
@ -582,46 +514,41 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
data.cidr = cidr - 1;
e.cidr = cidr - 1;
}
ip6_netmask(&data.ip2, data.cidr + 1);
ip6_netmask(&e.ip2, e.cidr + 1);
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (data.proto == 0)
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || data.proto == IPPROTO_ICMPV6))
data.port = 0;
if (!(with_ports || e.proto == IPPROTO_ICMPV6))
e.port = 0;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_NOMATCH)
flags |= (cadt_flags << 16);
flags |= (IPSET_FLAG_NOMATCH << 16);
}
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt) ? 1 :
ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port = ntohs(e.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
@ -629,8 +556,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
port = ntohs(h->next.port);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout, flags);
e.port = htons(port);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -640,81 +567,6 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
/* Create hash:ip type of sets */
static int
hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
size_t hsize;
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
* (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
hsize = htable_size(hbits);
if (hsize == 0) {
kfree(h);
return -ENOMEM;
}
h->table = ip_set_alloc(hsize);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
set->data = h;
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == NFPROTO_IPV4
? &hash_ipportnet4_tvariant
: &hash_ipportnet6_tvariant;
if (set->family == NFPROTO_IPV4)
hash_ipportnet4_gc_init(set);
else
hash_ipportnet6_gc_init(set);
} else {
set->variant = set->family == NFPROTO_IPV4
? &hash_ipportnet4_variant : &hash_ipportnet6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
return 0;
}
static struct ip_set_type hash_ipportnet_type __read_mostly = {
.name = "hash:ip,port,net",
.protocol = IPSET_PROTOCOL,
@ -731,6 +583,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -745,6 +598,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2013 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
@ -20,12 +20,12 @@
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0
/* 1 Range as input support for IPv4 added */
#define REVISION_MAX 2 /* nomatch flag support added */
/* 2 nomatch flag support added */
#define REVISION_MAX 3 /* Counters support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@ -33,17 +33,12 @@ IP_SET_MODULE_DESC("hash:net", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_hash:net");
/* Type specific function prefix */
#define TYPE hash_net
#define HTYPE hash_net
#define IP_SET_HASH_WITH_NETS
static bool
hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
/* IPv4 variants */
#define hash_net4_same_set hash_net_same_set
#define hash_net6_same_set hash_net_same_set
/* The type variant functions: IPv4 */
/* Member elements without timeout */
/* Member elements */
struct hash_net4_elem {
__be32 ip;
u16 padding0;
@ -51,8 +46,7 @@ struct hash_net4_elem {
u8 cidr;
};
/* Member elements with timeout support */
struct hash_net4_telem {
struct hash_net4t_elem {
__be32 ip;
u16 padding0;
u8 nomatch;
@ -60,6 +54,25 @@ struct hash_net4_telem {
unsigned long timeout;
};
struct hash_net4c_elem {
__be32 ip;
u16 padding0;
u8 nomatch;
u8 cidr;
struct ip_set_counter counter;
};
struct hash_net4ct_elem {
__be32 ip;
u16 padding0;
u8 nomatch;
u8 cidr;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_net4_data_equal(const struct hash_net4_elem *ip1,
const struct hash_net4_elem *ip2,
@ -69,42 +82,24 @@ hash_net4_data_equal(const struct hash_net4_elem *ip1,
ip1->cidr == ip2->cidr;
}
static inline bool
hash_net4_data_isnull(const struct hash_net4_elem *elem)
{
return elem->cidr == 0;
}
static inline void
hash_net4_data_copy(struct hash_net4_elem *dst,
const struct hash_net4_elem *src)
{
dst->ip = src->ip;
dst->cidr = src->cidr;
dst->nomatch = src->nomatch;
}
static inline void
hash_net4_data_flags(struct hash_net4_elem *dst, u32 flags)
{
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_net4_data_reset_flags(struct hash_net4_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int
hash_net4_data_match(const struct hash_net4_elem *elem)
hash_net4_do_data_match(const struct hash_net4_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_net4_data_set_flags(struct hash_net4_elem *elem, u32 flags)
{
elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
}
static inline void
hash_net4_data_reset_flags(struct hash_net4_elem *elem, u8 *flags)
{
swap(*flags, elem->nomatch);
}
static inline void
hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr)
{
@ -112,13 +107,6 @@ hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr)
elem->cidr = cidr;
}
/* Zero CIDR values cannot be stored */
static inline void
hash_net4_data_zero_out(struct hash_net4_elem *elem)
{
elem->cidr = 0;
}
static bool
hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
{
@ -135,106 +123,84 @@ nla_put_failure:
return 1;
}
static bool
hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
{
const struct hash_net4_telem *tdata =
(const struct hash_net4_telem *)data;
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, tdata->cidr) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout))) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#define IP_SET_HASH_WITH_NETS
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_net4_data_next(struct ip_set_hash *h,
hash_net4_data_next(struct hash_net4_elem *next,
const struct hash_net4_elem *d)
{
h->next.ip = d->ip;
next->ip = d->ip;
}
#define MTYPE hash_net4
#define PF 4
#define HOST_MASK 32
#include "ip_set_hash_gen.h"
static int
hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_net *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = {
struct hash_net4_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (data.cidr == 0)
if (e.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
e.cidr = HOST_MASK;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
e.ip &= ip_set_netmask(e.cidr);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_net *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
struct hash_net4_elem e = { .cidr = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 ip = 0, ip_to, last;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!data.cidr || data.cidr > HOST_MASK)
e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!e.cidr || e.cidr > HOST_MASK)
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 (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_NOMATCH)
flags |= (cadt_flags << 16);
flags |= (IPSET_FLAG_NOMATCH << 16);
}
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;
e.ip = htonl(ip & ip_set_hostmask(e.cidr));
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt) ? 1 :
ip_set_eexist(ret, flags) ? 0 : ret;
}
ip_to = ip;
@ -250,9 +216,9 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
ip = ntohl(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);
e.ip = htonl(ip);
last = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
@ -262,18 +228,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static bool
hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct ip_set_hash *x = a->data;
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
/* IPv6 variants */
struct hash_net6_elem {
union nf_inet_addr ip;
@ -282,7 +237,7 @@ struct hash_net6_elem {
u8 cidr;
};
struct hash_net6_telem {
struct hash_net6t_elem {
union nf_inet_addr ip;
u16 padding0;
u8 nomatch;
@ -290,6 +245,25 @@ struct hash_net6_telem {
unsigned long timeout;
};
struct hash_net6c_elem {
union nf_inet_addr ip;
u16 padding0;
u8 nomatch;
u8 cidr;
struct ip_set_counter counter;
};
struct hash_net6ct_elem {
union nf_inet_addr ip;
u16 padding0;
u8 nomatch;
u8 cidr;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_net6_data_equal(const struct hash_net6_elem *ip1,
const struct hash_net6_elem *ip2,
@ -299,55 +273,22 @@ hash_net6_data_equal(const struct hash_net6_elem *ip1,
ip1->cidr == ip2->cidr;
}
static inline bool
hash_net6_data_isnull(const struct hash_net6_elem *elem)
{
return elem->cidr == 0;
}
static inline void
hash_net6_data_copy(struct hash_net6_elem *dst,
const struct hash_net6_elem *src)
{
dst->ip.in6 = src->ip.in6;
dst->cidr = src->cidr;
dst->nomatch = src->nomatch;
}
static inline void
hash_net6_data_flags(struct hash_net6_elem *dst, u32 flags)
{
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_net6_data_reset_flags(struct hash_net6_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int
hash_net6_data_match(const struct hash_net6_elem *elem)
hash_net6_do_data_match(const struct hash_net6_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_net6_data_zero_out(struct hash_net6_elem *elem)
hash_net6_data_set_flags(struct hash_net6_elem *elem, u32 flags)
{
elem->cidr = 0;
elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
hash_net6_data_reset_flags(struct hash_net6_elem *elem, u8 *flags)
{
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
swap(*flags, elem->nomatch);
}
static inline void
@ -373,74 +314,60 @@ nla_put_failure:
return 1;
}
static bool
hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
{
const struct hash_net6_telem *e =
(const struct hash_net6_telem *)data;
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, e->cidr) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout))) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#undef PF
#undef HOST_MASK
#define PF 6
#define HOST_MASK 128
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_net6_data_next(struct ip_set_hash *h,
hash_net6_data_next(struct hash_net4_elem *next,
const struct hash_net6_elem *d)
{
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#define MTYPE hash_net6
#define PF 6
#define HOST_MASK 128
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_net *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem data = {
struct hash_net6_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (data.cidr == 0)
if (e.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
e.cidr = HOST_MASK;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
ip6_netmask(&e.ip, e.cidr);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_net *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
struct hash_net6_elem e = { .cidr = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@ -448,107 +375,29 @@ hash_net6_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_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR])
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!data.cidr || data.cidr > HOST_MASK)
if (!e.cidr || e.cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
ip6_netmask(&data.ip, data.cidr);
ip6_netmask(&e.ip, e.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 (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_NOMATCH)
flags |= (cadt_flags << 16);
flags |= (IPSET_FLAG_NOMATCH << 16);
}
ret = adtfn(set, &data, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
static int
hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
struct ip_set_hash *h;
u8 hbits;
size_t hsize;
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
* (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
hsize = htable_size(hbits);
if (hsize == 0) {
kfree(h);
return -ENOMEM;
}
h->table = ip_set_alloc(hsize);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
set->data = h;
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == NFPROTO_IPV4
? &hash_net4_tvariant : &hash_net6_tvariant;
if (set->family == NFPROTO_IPV4)
hash_net4_gc_init(set);
else
hash_net6_gc_init(set);
} else {
set->variant = set->family == NFPROTO_IPV4
? &hash_net4_variant : &hash_net6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
return 0;
return ip_set_enomatch(ret, flags, adt) ? 1 :
ip_set_eexist(ret, flags) ? 0 : ret;
}
static struct ip_set_type hash_net_type __read_mostly = {
@ -566,6 +415,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -573,6 +423,8 @@ static struct ip_set_type hash_net_type __read_mostly = {
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2011-2013 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
@ -21,12 +21,12 @@
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0
/* 1 nomatch flag support added */
#define REVISION_MAX 2 /* /0 support added */
/* 2 /0 support added */
#define REVISION_MAX 3 /* Counters support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@ -127,17 +127,14 @@ iface_add(struct rb_root *root, const char **iface)
}
/* Type specific function prefix */
#define TYPE hash_netiface
static bool
hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_netiface4_same_set hash_netiface_same_set
#define hash_netiface6_same_set hash_netiface_same_set
#define HTYPE hash_netiface
#define IP_SET_HASH_WITH_NETS
#define IP_SET_HASH_WITH_RBTREE
#define IP_SET_HASH_WITH_MULTI
#define STREQ(a, b) (strcmp(a, b) == 0)
/* The type variant functions: IPv4 */
/* IPv4 variants */
struct hash_netiface4_elem_hashed {
__be32 ip;
@ -147,8 +144,6 @@ struct hash_netiface4_elem_hashed {
u8 elem;
};
#define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed)
/* Member elements without timeout */
struct hash_netiface4_elem {
__be32 ip;
@ -159,8 +154,7 @@ struct hash_netiface4_elem {
const char *iface;
};
/* Member elements with timeout support */
struct hash_netiface4_telem {
struct hash_netiface4t_elem {
__be32 ip;
u8 physdev;
u8 cidr;
@ -170,6 +164,29 @@ struct hash_netiface4_telem {
unsigned long timeout;
};
struct hash_netiface4c_elem {
__be32 ip;
u8 physdev;
u8 cidr;
u8 nomatch;
u8 elem;
const char *iface;
struct ip_set_counter counter;
};
struct hash_netiface4ct_elem {
__be32 ip;
u8 physdev;
u8 cidr;
u8 nomatch;
u8 elem;
const char *iface;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1,
const struct hash_netiface4_elem *ip2,
@ -182,40 +199,24 @@ hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1,
ip1->iface == ip2->iface;
}
static inline bool
hash_netiface4_data_isnull(const struct hash_netiface4_elem *elem)
{
return elem->elem == 0;
}
static inline void
hash_netiface4_data_copy(struct hash_netiface4_elem *dst,
const struct hash_netiface4_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_netiface4_data_flags(struct hash_netiface4_elem *dst, u32 flags)
{
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_netiface4_data_reset_flags(struct hash_netiface4_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int
hash_netiface4_data_match(const struct hash_netiface4_elem *elem)
hash_netiface4_do_data_match(const struct hash_netiface4_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_netiface4_data_set_flags(struct hash_netiface4_elem *elem, u32 flags)
{
elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
}
static inline void
hash_netiface4_data_reset_flags(struct hash_netiface4_elem *elem, u8 *flags)
{
swap(*flags, elem->nomatch);
}
static inline void
hash_netiface4_data_netmask(struct hash_netiface4_elem *elem, u8 cidr)
{
@ -223,12 +224,6 @@ hash_netiface4_data_netmask(struct hash_netiface4_elem *elem, u8 cidr)
elem->cidr = cidr;
}
static inline void
hash_netiface4_data_zero_out(struct hash_netiface4_elem *elem)
{
elem->elem = 0;
}
static bool
hash_netiface4_data_list(struct sk_buff *skb,
const struct hash_netiface4_elem *data)
@ -249,66 +244,40 @@ nla_put_failure:
return 1;
}
static bool
hash_netiface4_data_tlist(struct sk_buff *skb,
const struct hash_netiface4_elem *data)
{
const struct hash_netiface4_telem *tdata =
(const struct hash_netiface4_telem *)data;
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
if (data->nomatch)
flags |= IPSET_FLAG_NOMATCH;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) ||
nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#define IP_SET_HASH_WITH_NETS
#define IP_SET_HASH_WITH_RBTREE
#define IP_SET_HASH_WITH_MULTI
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_netiface4_data_next(struct ip_set_hash *h,
hash_netiface4_data_next(struct hash_netiface4_elem *next,
const struct hash_netiface4_elem *d)
{
h->next.ip = d->ip;
next->ip = d->ip;
}
#define MTYPE hash_netiface4
#define PF 4
#define HOST_MASK 32
#define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed)
#include "ip_set_hash_gen.h"
static int
hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
struct ip_set_hash *h = set->data;
struct hash_netiface *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem data = {
struct hash_netiface4_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK,
.elem = 1,
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
int ret;
if (data.cidr == 0)
if (e.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
e.cidr = HOST_MASK;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
e.ip &= ip_set_netmask(e.cidr);
#define IFACE(dir) (par->dir ? par->dir->name : NULL)
#define PHYSDEV(dir) (nf_bridge->dir ? nf_bridge->dir->name : NULL)
@ -320,72 +289,69 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
if (!nf_bridge)
return -EINVAL;
data.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
data.physdev = 1;
e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
e.physdev = 1;
#else
data.iface = NULL;
e.iface = NULL;
#endif
} else
data.iface = SRCDIR ? IFACE(in) : IFACE(out);
e.iface = SRCDIR ? IFACE(in) : IFACE(out);
if (!data.iface)
if (!e.iface)
return -EINVAL;
ret = iface_test(&h->rbtree, &data.iface);
ret = iface_test(&h->rbtree, &e.iface);
if (adt == IPSET_ADD) {
if (!ret) {
ret = iface_add(&h->rbtree, &data.iface);
ret = iface_add(&h->rbtree, &e.iface);
if (ret)
return ret;
}
} else if (!ret)
return ret;
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct ip_set_hash *h = set->data;
struct hash_netiface *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem data = { .cidr = HOST_MASK, .elem = 1 };
struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 ip = 0, ip_to, last;
u32 timeout = h->timeout;
char iface[IFNAMSIZ];
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!tb[IPSET_ATTR_IFACE] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (data.cidr > HOST_MASK)
e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (e.cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE]));
data.iface = iface;
ret = iface_test(&h->rbtree, &data.iface);
e.iface = iface;
ret = iface_test(&h->rbtree, &e.iface);
if (adt == IPSET_ADD) {
if (!ret) {
ret = iface_add(&h->rbtree, &data.iface);
ret = iface_add(&h->rbtree, &e.iface);
if (ret)
return ret;
}
@ -395,14 +361,15 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_PHYSDEV)
data.physdev = 1;
if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH))
flags |= (cadt_flags << 16);
e.physdev = 1;
if (cadt_flags & IPSET_FLAG_NOMATCH)
flags |= (IPSET_FLAG_NOMATCH << 16);
}
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;
e.ip = htonl(ip & ip_set_hostmask(e.cidr));
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt) ? 1 :
ip_set_eexist(ret, flags) ? 0 : ret;
}
if (tb[IPSET_ATTR_IP_TO]) {
@ -413,16 +380,15 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
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);
}
} else
ip_set_mask_from_to(ip, ip_to, e.cidr);
if (retried)
ip = ntohl(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);
e.ip = htonl(ip);
last = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -433,18 +399,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static bool
hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct ip_set_hash *x = a->data;
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
/* IPv6 variants */
struct hash_netiface6_elem_hashed {
union nf_inet_addr ip;
@ -454,8 +409,6 @@ struct hash_netiface6_elem_hashed {
u8 elem;
};
#define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed)
struct hash_netiface6_elem {
union nf_inet_addr ip;
u8 physdev;
@ -465,7 +418,7 @@ struct hash_netiface6_elem {
const char *iface;
};
struct hash_netiface6_telem {
struct hash_netiface6t_elem {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
@ -475,6 +428,29 @@ struct hash_netiface6_telem {
unsigned long timeout;
};
struct hash_netiface6c_elem {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
u8 nomatch;
u8 elem;
const char *iface;
struct ip_set_counter counter;
};
struct hash_netiface6ct_elem {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
u8 nomatch;
u8 elem;
const char *iface;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1,
const struct hash_netiface6_elem *ip2,
@ -487,53 +463,22 @@ hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1,
ip1->iface == ip2->iface;
}
static inline bool
hash_netiface6_data_isnull(const struct hash_netiface6_elem *elem)
{
return elem->elem == 0;
}
static inline void
hash_netiface6_data_copy(struct hash_netiface6_elem *dst,
const struct hash_netiface6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_netiface6_data_flags(struct hash_netiface6_elem *dst, u32 flags)
{
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline int
hash_netiface6_data_match(const struct hash_netiface6_elem *elem)
hash_netiface6_do_data_match(const struct hash_netiface6_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_netiface6_data_reset_flags(struct hash_netiface6_elem *dst, u32 *flags)
hash_netiface6_data_set_flags(struct hash_netiface6_elem *elem, u32 flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
}
static inline void
hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem)
hash_netiface6_data_reset_flags(struct hash_netiface6_elem *elem, u8 *flags)
{
elem->elem = 0;
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
swap(*flags, elem->nomatch);
}
static inline void
@ -563,63 +508,45 @@ nla_put_failure:
return 1;
}
static bool
hash_netiface6_data_tlist(struct sk_buff *skb,
const struct hash_netiface6_elem *data)
{
const struct hash_netiface6_telem *e =
(const struct hash_netiface6_telem *)data;
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
if (data->nomatch)
flags |= IPSET_FLAG_NOMATCH;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) ||
nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#undef PF
#undef HOST_MASK
#define PF 6
#define HOST_MASK 128
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_netiface6_data_next(struct ip_set_hash *h,
hash_netiface6_data_next(struct hash_netiface4_elem *next,
const struct hash_netiface6_elem *d)
{
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#undef HKEY_DATALEN
#define MTYPE hash_netiface6
#define PF 6
#define HOST_MASK 128
#define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed)
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
struct ip_set_hash *h = set->data;
struct hash_netiface *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface6_elem data = {
struct hash_netiface6_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK,
.elem = 1,
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
int ret;
if (data.cidr == 0)
if (e.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
e.cidr = HOST_MASK;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
ip6_netmask(&e.ip, e.cidr);
if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
#ifdef CONFIG_BRIDGE_NETFILTER
@ -627,44 +554,46 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
if (!nf_bridge)
return -EINVAL;
data.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
data.physdev = 1;
e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
e.physdev = 1;
#else
data.iface = NULL;
e.iface = NULL;
#endif
} else
data.iface = SRCDIR ? IFACE(in) : IFACE(out);
e.iface = SRCDIR ? IFACE(in) : IFACE(out);
if (!data.iface)
if (!e.iface)
return -EINVAL;
ret = iface_test(&h->rbtree, &data.iface);
ret = iface_test(&h->rbtree, &e.iface);
if (adt == IPSET_ADD) {
if (!ret) {
ret = iface_add(&h->rbtree, &data.iface);
ret = iface_add(&h->rbtree, &e.iface);
if (ret)
return ret;
}
} else if (!ret)
return ret;
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct ip_set_hash *h = set->data;
struct hash_netiface *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface6_elem data = { .cidr = HOST_MASK, .elem = 1 };
u32 timeout = h->timeout;
struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
char iface[IFNAMSIZ];
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!tb[IPSET_ATTR_IFACE] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@ -672,28 +601,23 @@ hash_netiface6_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_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR])
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (data.cidr > HOST_MASK)
e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (e.cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
ip6_netmask(&data.ip, data.cidr);
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
ip6_netmask(&e.ip, e.cidr);
strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE]));
data.iface = iface;
ret = iface_test(&h->rbtree, &data.iface);
e.iface = iface;
ret = iface_test(&h->rbtree, &e.iface);
if (adt == IPSET_ADD) {
if (!ret) {
ret = iface_add(&h->rbtree, &data.iface);
ret = iface_add(&h->rbtree, &e.iface);
if (ret)
return ret;
}
@ -703,90 +627,15 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_PHYSDEV)
data.physdev = 1;
if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH))
flags |= (cadt_flags << 16);
e.physdev = 1;
if (cadt_flags & IPSET_FLAG_NOMATCH)
flags |= (IPSET_FLAG_NOMATCH << 16);
}
ret = adtfn(set, &data, timeout, flags);
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
static int
hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
size_t hsize;
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
* (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
h->ahash_max = AHASH_MAX_SIZE;
hbits = htable_bits(hashsize);
hsize = htable_size(hbits);
if (hsize == 0) {
kfree(h);
return -ENOMEM;
}
h->table = ip_set_alloc(hsize);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
h->rbtree = RB_ROOT;
set->data = h;
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == NFPROTO_IPV4
? &hash_netiface4_tvariant : &hash_netiface6_tvariant;
if (set->family == NFPROTO_IPV4)
hash_netiface4_gc_init(set);
else
hash_netiface6_gc_init(set);
} else {
set->variant = set->family == NFPROTO_IPV4
? &hash_netiface4_variant : &hash_netiface6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
return 0;
return ip_set_enomatch(ret, flags, adt) ? 1 :
ip_set_eexist(ret, flags) ? 0 : ret;
}
static struct ip_set_type hash_netiface_type __read_mostly = {
@ -806,6 +655,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -816,6 +666,8 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2003-2013 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
@ -20,14 +20,14 @@
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0
/* 1 SCTP and UDPLITE support added */
/* 2 Range as input support for IPv4 added */
#define REVISION_MAX 3 /* nomatch flag support added */
/* 3 nomatch flag support added */
#define REVISION_MAX 4 /* Counters support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@ -35,15 +35,9 @@ IP_SET_MODULE_DESC("hash:net,port", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_hash:net,port");
/* Type specific function prefix */
#define TYPE hash_netport
static bool
hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
#define hash_netport4_same_set hash_netport_same_set
#define hash_netport6_same_set hash_netport_same_set
/* The type variant functions: IPv4 */
#define HTYPE hash_netport
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
* However this way we have to store internally cidr - 1,
@ -51,7 +45,9 @@ hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
*/
#define IP_SET_HASH_WITH_NETS_PACKED
/* Member elements without timeout */
/* IPv4 variants */
/* Member elements */
struct hash_netport4_elem {
__be32 ip;
__be16 port;
@ -60,8 +56,7 @@ struct hash_netport4_elem {
u8 nomatch:1;
};
/* Member elements with timeout support */
struct hash_netport4_telem {
struct hash_netport4t_elem {
__be32 ip;
__be16 port;
u8 proto;
@ -70,6 +65,27 @@ struct hash_netport4_telem {
unsigned long timeout;
};
struct hash_netport4c_elem {
__be32 ip;
__be16 port;
u8 proto;
u8 cidr:7;
u8 nomatch:1;
struct ip_set_counter counter;
};
struct hash_netport4ct_elem {
__be32 ip;
__be16 port;
u8 proto;
u8 cidr:7;
u8 nomatch:1;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
const struct hash_netport4_elem *ip2,
@ -81,44 +97,24 @@ hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
ip1->cidr == ip2->cidr;
}
static inline bool
hash_netport4_data_isnull(const struct hash_netport4_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_netport4_data_copy(struct hash_netport4_elem *dst,
const struct hash_netport4_elem *src)
{
dst->ip = src->ip;
dst->port = src->port;
dst->proto = src->proto;
dst->cidr = src->cidr;
dst->nomatch = src->nomatch;
}
static inline void
hash_netport4_data_flags(struct hash_netport4_elem *dst, u32 flags)
{
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_netport4_data_reset_flags(struct hash_netport4_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int
hash_netport4_data_match(const struct hash_netport4_elem *elem)
hash_netport4_do_data_match(const struct hash_netport4_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_netport4_data_set_flags(struct hash_netport4_elem *elem, u32 flags)
{
elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
}
static inline void
hash_netport4_data_reset_flags(struct hash_netport4_elem *elem, u8 *flags)
{
swap(*flags, elem->nomatch);
}
static inline void
hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr)
{
@ -126,12 +122,6 @@ hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr)
elem->cidr = cidr - 1;
}
static inline void
hash_netport4_data_zero_out(struct hash_netport4_elem *elem)
{
elem->proto = 0;
}
static bool
hash_netport4_data_list(struct sk_buff *skb,
const struct hash_netport4_elem *data)
@ -151,77 +141,53 @@ nla_put_failure:
return 1;
}
static bool
hash_netport4_data_tlist(struct sk_buff *skb,
const struct hash_netport4_elem *data)
{
const struct hash_netport4_telem *tdata =
(const struct hash_netport4_telem *)data;
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr + 1) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout))) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ipset/ip_set_ahash.h>
static inline void
hash_netport4_data_next(struct ip_set_hash *h,
hash_netport4_data_next(struct hash_netport4_elem *next,
const struct hash_netport4_elem *d)
{
h->next.ip = d->ip;
h->next.port = d->port;
next->ip = d->ip;
next->port = d->port;
}
#define MTYPE hash_netport4
#define PF 4
#define HOST_MASK 32
#include "ip_set_hash_gen.h"
static int
hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_netport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = {
struct hash_netport4_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (adt == IPSET_TEST)
data.cidr = HOST_MASK - 1;
e.cidr = HOST_MASK - 1;
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
&e.port, &e.proto))
return -EINVAL;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
data.ip &= ip_set_netmask(data.cidr + 1);
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
e.ip &= ip_set_netmask(e.cidr + 1);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_netport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = { .cidr = HOST_MASK - 1 };
struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 port, port_to, p = 0, ip = 0, ip_to, last;
u32 timeout = h->timeout;
bool with_ports = false;
u8 cidr;
int ret;
@ -230,13 +196,16 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
@ -244,47 +213,42 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
data.cidr = cidr - 1;
e.cidr = cidr - 1;
}
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (data.proto == 0)
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || data.proto == IPPROTO_ICMP))
data.port = 0;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (!(with_ports || e.proto == IPPROTO_ICMP))
e.port = 0;
with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_NOMATCH)
flags |= (cadt_flags << 16);
flags |= (IPSET_FLAG_NOMATCH << 16);
}
if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
data.ip = htonl(ip & ip_set_hostmask(data.cidr + 1));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
e.ip = htonl(ip & ip_set_hostmask(e.cidr + 1));
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt) ? 1 :
ip_set_eexist(ret, flags) ? 0 : ret;
}
port = port_to = ntohs(data.port);
port = port_to = ntohs(e.port);
if (tb[IPSET_ATTR_PORT_TO]) {
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port_to < port)
@ -298,21 +262,20 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
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 + 1);
}
} else
ip_set_mask_from_to(ip, ip_to, e.cidr + 1);
if (retried)
ip = ntohl(h->next.ip);
while (!after(ip, ip_to)) {
data.ip = htonl(ip);
e.ip = htonl(ip);
last = ip_set_range_to_cidr(ip, ip_to, &cidr);
data.cidr = cidr - 1;
e.cidr = cidr - 1;
p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
: port;
for (; p <= port_to; p++) {
data.port = htons(p);
ret = adtfn(set, &data, timeout, flags);
e.port = htons(p);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -324,18 +287,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
static bool
hash_netport_same_set(const struct ip_set *a, const struct ip_set *b)
{
const struct ip_set_hash *x = a->data;
const struct ip_set_hash *y = b->data;
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
x->timeout == y->timeout;
}
/* The type variant functions: IPv6 */
/* IPv6 variants */
struct hash_netport6_elem {
union nf_inet_addr ip;
@ -345,7 +297,7 @@ struct hash_netport6_elem {
u8 nomatch:1;
};
struct hash_netport6_telem {
struct hash_netport6t_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
@ -354,6 +306,27 @@ struct hash_netport6_telem {
unsigned long timeout;
};
struct hash_netport6c_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
u8 cidr:7;
u8 nomatch:1;
struct ip_set_counter counter;
};
struct hash_netport6ct_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
u8 cidr:7;
u8 nomatch:1;
struct ip_set_counter counter;
unsigned long timeout;
};
/* Common functions */
static inline bool
hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
const struct hash_netport6_elem *ip2,
@ -365,53 +338,22 @@ hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
ip1->cidr == ip2->cidr;
}
static inline bool
hash_netport6_data_isnull(const struct hash_netport6_elem *elem)
{
return elem->proto == 0;
}
static inline void
hash_netport6_data_copy(struct hash_netport6_elem *dst,
const struct hash_netport6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
}
static inline void
hash_netport6_data_flags(struct hash_netport6_elem *dst, u32 flags)
{
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_netport6_data_reset_flags(struct hash_netport6_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int
hash_netport6_data_match(const struct hash_netport6_elem *elem)
hash_netport6_do_data_match(const struct hash_netport6_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
hash_netport6_data_set_flags(struct hash_netport6_elem *elem, u32 flags)
{
elem->proto = 0;
elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
}
static inline void
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
hash_netport6_data_reset_flags(struct hash_netport6_elem *elem, u8 *flags)
{
ip->ip6[0] &= ip_set_netmask6(prefix)[0];
ip->ip6[1] &= ip_set_netmask6(prefix)[1];
ip->ip6[2] &= ip_set_netmask6(prefix)[2];
ip->ip6[3] &= ip_set_netmask6(prefix)[3];
swap(*flags, elem->nomatch);
}
static inline void
@ -440,76 +382,57 @@ nla_put_failure:
return 1;
}
static bool
hash_netport6_data_tlist(struct sk_buff *skb,
const struct hash_netport6_elem *data)
static inline void
hash_netport6_data_next(struct hash_netport4_elem *next,
const struct hash_netport6_elem *d)
{
const struct hash_netport6_telem *e =
(const struct hash_netport6_telem *)data;
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr + 1) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout))) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
next->port = d->port;
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#define MTYPE hash_netport6
#define PF 6
#define HOST_MASK 128
#include <linux/netfilter/ipset/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 = d->port;
}
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct ip_set_hash *h = set->data;
const struct hash_netport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = {
struct hash_netport6_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1,
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
if (adt == IPSET_TEST)
data.cidr = HOST_MASK - 1;
e.cidr = HOST_MASK - 1;
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
&e.port, &e.proto))
return -EINVAL;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6_netmask(&data.ip, data.cidr + 1);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
ip6_netmask(&e.ip, e.cidr + 1);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct ip_set_hash *h = set->data;
const struct hash_netport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = { .cidr = HOST_MASK - 1 };
struct hash_netport6_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
u8 cidr;
int ret;
@ -518,7 +441,9 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@ -526,7 +451,8 @@ hash_netport6_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_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
@ -534,45 +460,40 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
data.cidr = cidr - 1;
e.cidr = cidr - 1;
}
ip6_netmask(&data.ip, data.cidr + 1);
ip6_netmask(&e.ip, e.cidr + 1);
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(data.proto);
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (data.proto == 0)
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || data.proto == IPPROTO_ICMPV6))
data.port = 0;
if (!(with_ports || e.proto == IPPROTO_ICMPV6))
e.port = 0;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_NOMATCH)
flags |= (cadt_flags << 16);
flags |= (IPSET_FLAG_NOMATCH << 16);
}
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt) ? 1 :
ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(data.port);
port = ntohs(e.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
@ -580,8 +501,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
port = ntohs(h->next.port);
for (; port <= port_to; port++) {
data.port = htons(port);
ret = adtfn(set, &data, timeout, flags);
e.port = htons(port);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
@ -591,80 +512,6 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
/* Create hash:ip type of sets */
static int
hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct ip_set_hash *h;
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
size_t hsize;
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
if (hashsize < IPSET_MIMINAL_HASHSIZE)
hashsize = IPSET_MIMINAL_HASHSIZE;
}
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
* (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize);
hsize = htable_size(hbits);
if (hsize == 0) {
kfree(h);
return -ENOMEM;
}
h->table = ip_set_alloc(hsize);
if (!h->table) {
kfree(h);
return -ENOMEM;
}
h->table->htable_bits = hbits;
set->data = h;
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = set->family == NFPROTO_IPV4
? &hash_netport4_tvariant : &hash_netport6_tvariant;
if (set->family == NFPROTO_IPV4)
hash_netport4_gc_init(set);
else
hash_netport6_gc_init(set);
} else {
set->variant = set->family == NFPROTO_IPV4
? &hash_netport4_variant : &hash_netport6_variant;
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table);
return 0;
}
static struct ip_set_type hash_netport_type __read_mostly = {
.name = "hash:net,port",
.protocol = IPSET_PROTOCOL,
@ -681,6 +528,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -692,6 +540,8 @@ static struct ip_set_type hash_netport_type __read_mostly = {
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2008-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
/* Copyright (C) 2008-2013 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
@ -13,30 +13,53 @@
#include <linux/errno.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_list.h>
#define REVISION_MIN 0
#define REVISION_MAX 0
#define REVISION_MAX 1 /* Counters support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX);
MODULE_ALIAS("ip_set_list:set");
/* Member elements without and with timeout */
/* Member elements */
struct set_elem {
ip_set_id_t id;
};
struct set_telem {
ip_set_id_t id;
struct sett_elem {
struct {
ip_set_id_t id;
} __attribute__ ((aligned));
unsigned long timeout;
};
struct setc_elem {
struct {
ip_set_id_t id;
} __attribute__ ((aligned));
struct ip_set_counter counter;
};
struct setct_elem {
struct {
ip_set_id_t id;
} __attribute__ ((aligned));
struct ip_set_counter counter;
unsigned long timeout;
};
struct set_adt_elem {
ip_set_id_t id;
ip_set_id_t refid;
int before;
};
/* Type structure */
struct list_set {
size_t dsize; /* element size */
size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
u32 size; /* size of set list array */
u32 timeout; /* timeout value */
struct timer_list gc; /* garbage collection */
@ -49,206 +72,339 @@ list_set_elem(const struct list_set *map, u32 id)
return (struct set_elem *)((void *)map->members + id * map->dsize);
}
static inline struct set_telem *
list_set_telem(const struct list_set *map, u32 id)
{
return (struct set_telem *)((void *)map->members + id * map->dsize);
}
static inline bool
list_set_timeout(const struct list_set *map, u32 id)
{
const struct set_telem *elem = list_set_telem(map, id);
return ip_set_timeout_test(elem->timeout);
}
static inline bool
list_set_expired(const struct list_set *map, u32 id)
{
const struct set_telem *elem = list_set_telem(map, id);
return ip_set_timeout_expired(elem->timeout);
}
/* Set list without and with timeout */
#define ext_timeout(e, m) \
(unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
#define ext_counter(e, m) \
(struct ip_set_counter *)((void *)(e) + (m)->offset[IPSET_OFFSET_COUNTER])
static int
list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, const struct ip_set_adt_opt *opt)
list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
{
struct list_set *map = set->data;
struct set_elem *elem;
struct set_elem *e;
u32 i, cmdflags = opt->cmdflags;
int ret;
/* Don't lookup sub-counters at all */
opt->cmdflags &= ~IPSET_FLAG_MATCH_COUNTERS;
if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE;
for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
return 0;
if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))
continue;
ret = ip_set_test(e->id, skb, par, opt);
if (ret > 0) {
if (SET_WITH_COUNTER(set))
ip_set_update_counter(ext_counter(e, map),
ext, &opt->ext,
cmdflags);
return ret;
}
}
return 0;
}
static int
list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
{
struct list_set *map = set->data;
struct set_elem *e;
u32 i;
int ret;
for (i = 0; i < map->size; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID)
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
return 0;
if (with_timeout(map->timeout) && list_set_expired(map, i))
if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))
continue;
switch (adt) {
case IPSET_TEST:
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, par, opt);
if (ret == 0)
return ret;
break;
case IPSET_DEL:
ret = ip_set_del(elem->id, skb, par, opt);
if (ret == 0)
return ret;
break;
default:
break;
}
ret = ip_set_add(e->id, skb, par, opt);
if (ret == 0)
return ret;
}
return 0;
}
static int
list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
{
struct list_set *map = set->data;
struct set_elem *e;
u32 i;
int ret;
for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
return 0;
if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))
continue;
ret = ip_set_del(e->id, skb, par, opt);
if (ret == 0)
return ret;
}
return 0;
}
static int
list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
struct list_set *map = set->data;
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
switch (adt) {
case IPSET_TEST:
return list_set_ktest(set, skb, par, opt, &ext);
case IPSET_ADD:
return list_set_kadd(set, skb, par, opt, &ext);
case IPSET_DEL:
return list_set_kdel(set, skb, par, opt, &ext);
default:
break;
}
return -EINVAL;
}
static bool
id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
id_eq(const struct ip_set *set, u32 i, ip_set_id_t id)
{
const struct set_elem *elem;
const struct list_set *map = set->data;
const struct set_elem *e;
if (i < map->size) {
elem = list_set_elem(map, i);
return elem->id == id;
}
if (i >= map->size)
return 0;
return 0;
}
static bool
id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id)
{
const struct set_elem *elem;
if (i < map->size) {
elem = list_set_elem(map, i);
return !!(elem->id == id &&
!(with_timeout(map->timeout) &&
list_set_expired(map, i)));
}
return 0;
}
static void
list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
{
struct set_elem *e;
for (; i < map->size; i++) {
e = list_set_elem(map, i);
swap(e->id, id);
if (e->id == IPSET_INVALID_ID)
break;
}
}
static void
list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
unsigned long timeout)
{
struct set_telem *e;
for (; i < map->size; i++) {
e = list_set_telem(map, i);
swap(e->id, id);
swap(e->timeout, timeout);
if (e->id == IPSET_INVALID_ID)
break;
}
e = list_set_elem(map, i);
return !!(e->id == id &&
!(SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map))));
}
static int
list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
unsigned long timeout)
list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d,
const struct ip_set_ext *ext)
{
const struct set_elem *e = list_set_elem(map, i);
struct list_set *map = set->data;
struct set_elem *e = list_set_elem(map, i);
if (e->id != IPSET_INVALID_ID) {
const struct set_elem *x = list_set_elem(map, map->size - 1);
if (i == map->size - 1)
/* Last element replaced: e.g. add new,before,last */
ip_set_put_byindex(e->id);
else {
struct set_elem *x = list_set_elem(map, map->size - 1);
/* Last element replaced or pushed off */
if (x->id != IPSET_INVALID_ID)
ip_set_put_byindex(x->id);
/* Last element pushed off */
if (x->id != IPSET_INVALID_ID)
ip_set_put_byindex(x->id);
memmove(list_set_elem(map, i + 1), e,
map->dsize * (map->size - (i + 1)));
}
}
if (with_timeout(map->timeout))
list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
else
list_elem_add(map, i, id);
e->id = d->id;
if (SET_WITH_TIMEOUT(set))
ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(e, map), ext);
return 0;
}
static int
list_set_del(struct list_set *map, u32 i)
list_set_del(struct ip_set *set, u32 i)
{
struct set_elem *a = list_set_elem(map, i), *b;
struct list_set *map = set->data;
struct set_elem *e = list_set_elem(map, i);
ip_set_put_byindex(a->id);
ip_set_put_byindex(e->id);
if (i < map->size - 1)
memmove(e, list_set_elem(map, i + 1),
map->dsize * (map->size - (i + 1)));
for (; i < map->size - 1; i++) {
b = list_set_elem(map, i + 1);
a->id = b->id;
if (with_timeout(map->timeout))
((struct set_telem *)a)->timeout =
((struct set_telem *)b)->timeout;
a = b;
if (a->id == IPSET_INVALID_ID)
break;
}
/* Last element */
a->id = IPSET_INVALID_ID;
e = list_set_elem(map, map->size - 1);
e->id = IPSET_INVALID_ID;
return 0;
}
static void
cleanup_entries(struct list_set *map)
set_cleanup_entries(struct ip_set *set)
{
struct set_telem *e;
struct list_set *map = set->data;
struct set_elem *e;
u32 i;
for (i = 0; i < map->size; i++) {
e = list_set_telem(map, i);
if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
list_set_del(map, i);
e = list_set_elem(map, i);
if (e->id != IPSET_INVALID_ID &&
ip_set_timeout_expired(ext_timeout(e, map)))
list_set_del(set, i);
}
}
static int
list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags)
{
struct list_set *map = set->data;
struct set_adt_elem *d = value;
struct set_elem *e;
u32 i;
int ret;
for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
return 0;
else if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))
continue;
else if (e->id != d->id)
continue;
if (d->before == 0)
return 1;
else if (d->before > 0)
ret = id_eq(set, i + 1, d->refid);
else
ret = i > 0 && id_eq(set, i - 1, d->refid);
return ret;
}
return 0;
}
static int
list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags)
{
struct list_set *map = set->data;
struct set_adt_elem *d = value;
struct set_elem *e;
bool flag_exist = flags & IPSET_FLAG_EXIST;
u32 i, ret = 0;
/* Check already added element */
for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
goto insert;
else if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))
continue;
else if (e->id != d->id)
continue;
if ((d->before > 1 && !id_eq(set, i + 1, d->refid)) ||
(d->before < 0 &&
(i == 0 || !id_eq(set, i - 1, d->refid))))
/* Before/after doesn't match */
return -IPSET_ERR_REF_EXIST;
if (!flag_exist)
/* Can't re-add */
return -IPSET_ERR_EXIST;
/* Update extensions */
if (SET_WITH_TIMEOUT(set))
ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(e, map), ext);
/* Set is already added to the list */
ip_set_put_byindex(d->id);
return 0;
}
insert:
ret = -IPSET_ERR_LIST_FULL;
for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
ret = d->before != 0 ? -IPSET_ERR_REF_EXIST
: list_set_add(set, i, d, ext);
else if (e->id != d->refid)
continue;
else if (d->before > 0)
ret = list_set_add(set, i, d, ext);
else if (i + 1 < map->size)
ret = list_set_add(set, i + 1, d, ext);
}
return ret;
}
static int
list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags)
{
struct list_set *map = set->data;
struct set_adt_elem *d = value;
struct set_elem *e;
u32 i;
for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
return d->before != 0 ? -IPSET_ERR_REF_EXIST
: -IPSET_ERR_EXIST;
else if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))
continue;
else if (e->id != d->id)
continue;
if (d->before == 0)
return list_set_del(set, i);
else if (d->before > 0) {
if (!id_eq(set, i + 1, d->refid))
return -IPSET_ERR_REF_EXIST;
return list_set_del(set, i);
} else if (i == 0 || !id_eq(set, i - 1, d->refid))
return -IPSET_ERR_REF_EXIST;
else
return list_set_del(set, i);
}
return -IPSET_ERR_EXIST;
}
static int
list_set_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
struct list_set *map = set->data;
bool with_timeout = with_timeout(map->timeout);
bool flag_exist = flags & IPSET_FLAG_EXIST;
int before = 0;
u32 timeout = map->timeout;
ip_set_id_t id, refid = IPSET_INVALID_ID;
const struct set_elem *elem;
ipset_adtfn adtfn = set->variant->adt[adt];
struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
struct ip_set *s;
u32 i;
int ret = 0;
if (unlikely(!tb[IPSET_ATTR_NAME] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
if (id == IPSET_INVALID_ID)
ret = ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
if (e.id == IPSET_INVALID_ID)
return -IPSET_ERR_NAME;
/* "Loop detection" */
if (s->type->features & IPSET_TYPE_NAME) {
@ -258,115 +414,34 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
before = f & IPSET_FLAG_BEFORE;
e.before = f & IPSET_FLAG_BEFORE;
}
if (before && !tb[IPSET_ATTR_NAMEREF]) {
if (e.before && !tb[IPSET_ATTR_NAMEREF]) {
ret = -IPSET_ERR_BEFORE;
goto finish;
}
if (tb[IPSET_ATTR_NAMEREF]) {
refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
&s);
if (refid == IPSET_INVALID_ID) {
e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
&s);
if (e.refid == IPSET_INVALID_ID) {
ret = -IPSET_ERR_NAMEREF;
goto finish;
}
if (!before)
before = -1;
if (!e.before)
e.before = -1;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout) {
ret = -IPSET_ERR_TIMEOUT;
goto finish;
}
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
if (with_timeout && adt != IPSET_TEST)
cleanup_entries(map);
if (adt != IPSET_TEST && SET_WITH_TIMEOUT(set))
set_cleanup_entries(set);
switch (adt) {
case IPSET_TEST:
for (i = 0; i < map->size && !ret; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID ||
(before != 0 && i + 1 >= map->size))
break;
else if (with_timeout && list_set_expired(map, i))
continue;
else if (before > 0 && elem->id == id)
ret = id_eq_timeout(map, i + 1, refid);
else if (before < 0 && elem->id == refid)
ret = id_eq_timeout(map, i + 1, id);
else if (before == 0 && elem->id == id)
ret = 1;
}
break;
case IPSET_ADD:
for (i = 0; i < map->size; i++) {
elem = list_set_elem(map, i);
if (elem->id != id)
continue;
if (!(with_timeout && flag_exist)) {
ret = -IPSET_ERR_EXIST;
goto finish;
} else {
struct set_telem *e = list_set_telem(map, i);
if ((before > 1 &&
!id_eq(map, i + 1, refid)) ||
(before < 0 &&
(i == 0 || !id_eq(map, i - 1, refid)))) {
ret = -IPSET_ERR_EXIST;
goto finish;
}
e->timeout = ip_set_timeout_set(timeout);
ip_set_put_byindex(id);
ret = 0;
goto finish;
}
}
ret = -IPSET_ERR_LIST_FULL;
for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID)
ret = before != 0 ? -IPSET_ERR_REF_EXIST
: list_set_add(map, i, id, timeout);
else if (elem->id != refid)
continue;
else if (before > 0)
ret = list_set_add(map, i, id, timeout);
else if (i + 1 < map->size)
ret = list_set_add(map, i + 1, id, timeout);
}
break;
case IPSET_DEL:
ret = -IPSET_ERR_EXIST;
for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
elem = list_set_elem(map, i);
if (elem->id == IPSET_INVALID_ID) {
ret = before != 0 ? -IPSET_ERR_REF_EXIST
: -IPSET_ERR_EXIST;
break;
} else if (elem->id == id &&
(before == 0 ||
(before > 0 && id_eq(map, i + 1, refid))))
ret = list_set_del(map, i);
else if (elem->id == refid &&
before < 0 && id_eq(map, i + 1, id))
ret = list_set_del(map, i + 1);
}
break;
default:
break;
}
ret = adtfn(set, &e, &ext, &ext, flags);
finish:
if (refid != IPSET_INVALID_ID)
ip_set_put_byindex(refid);
if (e.refid != IPSET_INVALID_ID)
ip_set_put_byindex(e.refid);
if (adt != IPSET_ADD || ret)
ip_set_put_byindex(id);
ip_set_put_byindex(e.id);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
@ -375,14 +450,14 @@ static void
list_set_flush(struct ip_set *set)
{
struct list_set *map = set->data;
struct set_elem *elem;
struct set_elem *e;
u32 i;
for (i = 0; i < map->size; i++) {
elem = list_set_elem(map, i);
if (elem->id != IPSET_INVALID_ID) {
ip_set_put_byindex(elem->id);
elem->id = IPSET_INVALID_ID;
e = list_set_elem(map, i);
if (e->id != IPSET_INVALID_ID) {
ip_set_put_byindex(e->id);
e->id = IPSET_INVALID_ID;
}
}
}
@ -392,7 +467,7 @@ list_set_destroy(struct ip_set *set)
{
struct list_set *map = set->data;
if (with_timeout(map->timeout))
if (SET_WITH_TIMEOUT(set))
del_timer_sync(&map->gc);
list_set_flush(set);
kfree(map);
@ -410,8 +485,11 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
if (!nested)
goto nla_put_failure;
if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
(with_timeout(map->timeout) &&
(SET_WITH_TIMEOUT(set) &&
nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
(SET_WITH_COUNTER(set) &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
htonl(IPSET_FLAG_WITH_COUNTERS))) ||
nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->size * map->dsize)))
@ -440,7 +518,8 @@ list_set_list(const struct ip_set *set,
e = list_set_elem(map, i);
if (e->id == IPSET_INVALID_ID)
goto finish;
if (with_timeout(map->timeout) && list_set_expired(map, i))
if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))
continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) {
@ -453,13 +532,14 @@ list_set_list(const struct ip_set *set,
if (nla_put_string(skb, IPSET_ATTR_NAME,
ip_set_name_byindex(e->id)))
goto nla_put_failure;
if (with_timeout(map->timeout)) {
const struct set_telem *te =
(const struct set_telem *) e;
__be32 to = htonl(ip_set_timeout_get(te->timeout));
if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, to))
goto nla_put_failure;
}
if (SET_WITH_TIMEOUT(set) &&
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(
ext_timeout(e, map)))))
goto nla_put_failure;
if (SET_WITH_COUNTER(set) &&
ip_set_put_counter(skb, ext_counter(e, map)))
goto nla_put_failure;
ipset_nest_end(skb, nested);
}
finish:
@ -485,12 +565,18 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b)
const struct list_set *y = b->data;
return x->size == y->size &&
x->timeout == y->timeout;
x->timeout == y->timeout &&
a->extensions == b->extensions;
}
static const struct ip_set_type_variant list_set = {
static const struct ip_set_type_variant set_variant = {
.kadt = list_set_kadt,
.uadt = list_set_uadt,
.adt = {
[IPSET_ADD] = list_set_uadd,
[IPSET_DEL] = list_set_udel,
[IPSET_TEST] = list_set_utest,
},
.destroy = list_set_destroy,
.flush = list_set_flush,
.head = list_set_head,
@ -505,7 +591,7 @@ list_set_gc(unsigned long ul_set)
struct list_set *map = set->data;
write_lock_bh(&set->lock);
cleanup_entries(map);
set_cleanup_entries(set);
write_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
@ -513,20 +599,20 @@ list_set_gc(unsigned long ul_set)
}
static void
list_set_gc_init(struct ip_set *set)
list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
{
struct list_set *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
map->gc.function = list_set_gc;
map->gc.function = gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
/* Create list:set type of sets */
static bool
static struct list_set *
init_list_set(struct ip_set *set, u32 size, size_t dsize,
unsigned long timeout)
{
@ -536,7 +622,7 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize,
map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
if (!map)
return false;
return NULL;
map->size = size;
map->dsize = dsize;
@ -548,16 +634,19 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize,
e->id = IPSET_INVALID_ID;
}
return true;
return map;
}
static int
list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
u32 size = IP_SET_LIST_DEFAULT_SIZE;
struct list_set *map;
u32 size = IP_SET_LIST_DEFAULT_SIZE, cadt_flags = 0;
unsigned long timeout = 0;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_SIZE])
@ -565,18 +654,46 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (size < IP_SET_LIST_MIN_SIZE)
size = IP_SET_LIST_MIN_SIZE;
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!init_list_set(set, size, sizeof(struct set_telem),
ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (tb[IPSET_ATTR_TIMEOUT])
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &set_variant;
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
set->extensions |= IPSET_EXT_COUNTER;
if (tb[IPSET_ATTR_TIMEOUT]) {
map = init_list_set(set, size,
sizeof(struct setct_elem), timeout);
if (!map)
return -ENOMEM;
set->extensions |= IPSET_EXT_TIMEOUT;
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct setct_elem, timeout);
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct setct_elem, counter);
list_set_gc_init(set, list_set_gc);
} else {
map = init_list_set(set, size,
sizeof(struct setc_elem), 0);
if (!map)
return -ENOMEM;
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct setc_elem, counter);
}
} else if (tb[IPSET_ATTR_TIMEOUT]) {
map = init_list_set(set, size,
sizeof(struct sett_elem), timeout);
if (!map)
return -ENOMEM;
list_set_gc_init(set);
set->extensions |= IPSET_EXT_TIMEOUT;
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct sett_elem, timeout);
list_set_gc_init(set, list_set_gc);
} else {
if (!init_list_set(set, size, sizeof(struct set_elem),
IPSET_NO_TIMEOUT))
map = init_list_set(set, size, sizeof(struct set_elem), 0);
if (!map)
return -ENOMEM;
}
set->variant = &list_set;
return 0;
}
@ -592,6 +709,7 @@ static struct ip_set_type list_set_type __read_mostly = {
.create_policy = {
[IPSET_ATTR_SIZE] = { .type = NLA_U32 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_NAME] = { .type = NLA_STRING,
@ -601,6 +719,8 @@ static struct ip_set_type list_set_type __read_mostly = {
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
},
.me = THIS_MODULE,
};

View File

@ -36,7 +36,7 @@ sctp_manip_pkt(struct sk_buff *skb,
{
struct sk_buff *frag;
sctp_sctphdr_t *hdr;
__be32 crc32;
__u32 crc32;
if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
return false;
@ -55,8 +55,7 @@ sctp_manip_pkt(struct sk_buff *skb,
skb_walk_frags(skb, frag)
crc32 = sctp_update_cksum((u8 *)frag->data, skb_headlen(frag),
crc32);
crc32 = sctp_end_cksum(crc32);
hdr->checksum = crc32;
hdr->checksum = sctp_end_cksum(crc32);
return true;
}

View File

@ -45,7 +45,7 @@ void nf_unregister_queue_handler(void)
}
EXPORT_SYMBOL(nf_unregister_queue_handler);
static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
{
/* Release those devices we held, or Alexey will kill me. */
if (entry->indev)
@ -65,12 +65,41 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
/* Drop reference to owner of hook which queued us. */
module_put(entry->elem->owner);
}
EXPORT_SYMBOL_GPL(nf_queue_entry_release_refs);
/* Bump dev refs so they don't vanish while packet is out */
bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
{
if (!try_module_get(entry->elem->owner))
return false;
if (entry->indev)
dev_hold(entry->indev);
if (entry->outdev)
dev_hold(entry->outdev);
#ifdef CONFIG_BRIDGE_NETFILTER
if (entry->skb->nf_bridge) {
struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge;
struct net_device *physdev;
physdev = nf_bridge->physindev;
if (physdev)
dev_hold(physdev);
physdev = nf_bridge->physoutdev;
if (physdev)
dev_hold(physdev);
}
#endif
return true;
}
EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
/*
* Any packet that leaves via this function must come back
* through nf_reinject().
*/
static int __nf_queue(struct sk_buff *skb,
int nf_queue(struct sk_buff *skb,
struct nf_hook_ops *elem,
u_int8_t pf, unsigned int hook,
struct net_device *indev,
@ -80,10 +109,6 @@ static int __nf_queue(struct sk_buff *skb,
{
int status = -ENOENT;
struct nf_queue_entry *entry = NULL;
#ifdef CONFIG_BRIDGE_NETFILTER
struct net_device *physindev;
struct net_device *physoutdev;
#endif
const struct nf_afinfo *afinfo;
const struct nf_queue_handler *qh;
@ -114,28 +139,13 @@ static int __nf_queue(struct sk_buff *skb,
.indev = indev,
.outdev = outdev,
.okfn = okfn,
.size = sizeof(*entry) + afinfo->route_key_size,
};
/* If it's going away, ignore hook. */
if (!try_module_get(entry->elem->owner)) {
if (!nf_queue_entry_get_refs(entry)) {
status = -ECANCELED;
goto err_unlock;
}
/* Bump dev refs so they don't vanish while packet is out */
if (indev)
dev_hold(indev);
if (outdev)
dev_hold(outdev);
#ifdef CONFIG_BRIDGE_NETFILTER
if (skb->nf_bridge) {
physindev = skb->nf_bridge->physindev;
if (physindev)
dev_hold(physindev);
physoutdev = skb->nf_bridge->physoutdev;
if (physoutdev)
dev_hold(physoutdev);
}
#endif
skb_dst_force(skb);
afinfo->saveroute(skb, entry);
status = qh->outfn(entry, queuenum);
@ -156,87 +166,6 @@ err:
return status;
}
#ifdef CONFIG_BRIDGE_NETFILTER
/* When called from bridge netfilter, skb->data must point to MAC header
* before calling skb_gso_segment(). Else, original MAC header is lost
* and segmented skbs will be sent to wrong destination.
*/
static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
{
if (skb->nf_bridge)
__skb_push(skb, skb->network_header - skb->mac_header);
}
static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
{
if (skb->nf_bridge)
__skb_pull(skb, skb->network_header - skb->mac_header);
}
#else
#define nf_bridge_adjust_skb_data(s) do {} while (0)
#define nf_bridge_adjust_segmented_data(s) do {} while (0)
#endif
int nf_queue(struct sk_buff *skb,
struct nf_hook_ops *elem,
u_int8_t pf, unsigned int hook,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *),
unsigned int queuenum)
{
struct sk_buff *segs;
int err = -EINVAL;
unsigned int queued;
if (!skb_is_gso(skb))
return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
queuenum);
switch (pf) {
case NFPROTO_IPV4:
skb->protocol = htons(ETH_P_IP);
break;
case NFPROTO_IPV6:
skb->protocol = htons(ETH_P_IPV6);
break;
}
nf_bridge_adjust_skb_data(skb);
segs = skb_gso_segment(skb, 0);
/* Does not use PTR_ERR to limit the number of error codes that can be
* returned by nf_queue. For instance, callers rely on -ECANCELED to mean
* 'ignore this hook'.
*/
if (IS_ERR(segs))
goto out_err;
queued = 0;
err = 0;
do {
struct sk_buff *nskb = segs->next;
segs->next = NULL;
if (err == 0) {
nf_bridge_adjust_segmented_data(segs);
err = __nf_queue(segs, elem, pf, hook, indev,
outdev, okfn, queuenum);
}
if (err == 0)
queued++;
else
kfree_skb(segs);
segs = nskb;
} while (segs);
if (queued) {
kfree_skb(skb);
return 0;
}
out_err:
nf_bridge_adjust_segmented_data(skb);
return err;
}
void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
{
struct sk_buff *skb = entry->skb;
@ -276,9 +205,9 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
local_bh_enable();
break;
case NF_QUEUE:
err = __nf_queue(skb, elem, entry->pf, entry->hook,
entry->indev, entry->outdev, entry->okfn,
verdict >> NF_VERDICT_QBITS);
err = nf_queue(skb, elem, entry->pf, entry->hook,
entry->indev, entry->outdev, entry->okfn,
verdict >> NF_VERDICT_QBITS);
if (err < 0) {
if (err == -ECANCELED)
goto next_hook;

View File

@ -272,6 +272,18 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
skb_shinfo(to)->nr_frags = j;
}
static int nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet)
{
__u32 flags = 0;
if (packet->ip_summed == CHECKSUM_PARTIAL)
flags = NFQA_SKB_CSUMNOTREADY;
if (skb_is_gso(packet))
flags |= NFQA_SKB_GSO;
return flags ? nla_put_be32(nlskb, NFQA_SKB_INFO, htonl(flags)) : 0;
}
static struct sk_buff *
nfqnl_build_packet_message(struct nfqnl_instance *queue,
struct nf_queue_entry *entry,
@ -301,6 +313,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
#endif
+ nla_total_size(sizeof(u_int32_t)) /* mark */
+ nla_total_size(sizeof(struct nfqnl_msg_packet_hw))
+ nla_total_size(sizeof(u_int32_t)) /* skbinfo */
+ nla_total_size(sizeof(u_int32_t)); /* cap_len */
if (entskb->tstamp.tv64)
@ -314,7 +327,8 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
break;
case NFQNL_COPY_PACKET:
if (entskb->ip_summed == CHECKSUM_PARTIAL &&
if (!(queue->flags & NFQA_CFG_F_GSO) &&
entskb->ip_summed == CHECKSUM_PARTIAL &&
skb_checksum_help(entskb))
return NULL;
@ -454,6 +468,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
if (cap_len > 0 && nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len)))
goto nla_put_failure;
if (nfqnl_put_packet_info(skb, entskb))
goto nla_put_failure;
if (data_len) {
struct nlattr *nla;
@ -477,28 +494,13 @@ nla_put_failure:
}
static int
nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
__nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue,
struct nf_queue_entry *entry)
{
struct sk_buff *nskb;
struct nfqnl_instance *queue;
int err = -ENOBUFS;
__be32 *packet_id_ptr;
int failopen = 0;
struct net *net = dev_net(entry->indev ?
entry->indev : entry->outdev);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
/* rcu_read_lock()ed by nf_hook_slow() */
queue = instance_lookup(q, queuenum);
if (!queue) {
err = -ESRCH;
goto err_out;
}
if (queue->copy_mode == NFQNL_COPY_NONE) {
err = -EINVAL;
goto err_out;
}
nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr);
if (nskb == NULL) {
@ -547,6 +549,141 @@ err_out:
return err;
}
static struct nf_queue_entry *
nf_queue_entry_dup(struct nf_queue_entry *e)
{
struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC);
if (entry) {
if (nf_queue_entry_get_refs(entry))
return entry;
kfree(entry);
}
return NULL;
}
#ifdef CONFIG_BRIDGE_NETFILTER
/* When called from bridge netfilter, skb->data must point to MAC header
* before calling skb_gso_segment(). Else, original MAC header is lost
* and segmented skbs will be sent to wrong destination.
*/
static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
{
if (skb->nf_bridge)
__skb_push(skb, skb->network_header - skb->mac_header);
}
static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
{
if (skb->nf_bridge)
__skb_pull(skb, skb->network_header - skb->mac_header);
}
#else
#define nf_bridge_adjust_skb_data(s) do {} while (0)
#define nf_bridge_adjust_segmented_data(s) do {} while (0)
#endif
static void free_entry(struct nf_queue_entry *entry)
{
nf_queue_entry_release_refs(entry);
kfree(entry);
}
static int
__nfqnl_enqueue_packet_gso(struct net *net, struct nfqnl_instance *queue,
struct sk_buff *skb, struct nf_queue_entry *entry)
{
int ret = -ENOMEM;
struct nf_queue_entry *entry_seg;
nf_bridge_adjust_segmented_data(skb);
if (skb->next == NULL) { /* last packet, no need to copy entry */
struct sk_buff *gso_skb = entry->skb;
entry->skb = skb;
ret = __nfqnl_enqueue_packet(net, queue, entry);
if (ret)
entry->skb = gso_skb;
return ret;
}
skb->next = NULL;
entry_seg = nf_queue_entry_dup(entry);
if (entry_seg) {
entry_seg->skb = skb;
ret = __nfqnl_enqueue_packet(net, queue, entry_seg);
if (ret)
free_entry(entry_seg);
}
return ret;
}
static int
nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
{
unsigned int queued;
struct nfqnl_instance *queue;
struct sk_buff *skb, *segs;
int err = -ENOBUFS;
struct net *net = dev_net(entry->indev ?
entry->indev : entry->outdev);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
/* rcu_read_lock()ed by nf_hook_slow() */
queue = instance_lookup(q, queuenum);
if (!queue)
return -ESRCH;
if (queue->copy_mode == NFQNL_COPY_NONE)
return -EINVAL;
if ((queue->flags & NFQA_CFG_F_GSO) || !skb_is_gso(entry->skb))
return __nfqnl_enqueue_packet(net, queue, entry);
skb = entry->skb;
switch (entry->pf) {
case NFPROTO_IPV4:
skb->protocol = htons(ETH_P_IP);
break;
case NFPROTO_IPV6:
skb->protocol = htons(ETH_P_IPV6);
break;
}
nf_bridge_adjust_skb_data(skb);
segs = skb_gso_segment(skb, 0);
/* Does not use PTR_ERR to limit the number of error codes that can be
* returned by nf_queue. For instance, callers rely on -ECANCELED to
* mean 'ignore this hook'.
*/
if (IS_ERR(segs))
goto out_err;
queued = 0;
err = 0;
do {
struct sk_buff *nskb = segs->next;
if (err == 0)
err = __nfqnl_enqueue_packet_gso(net, queue,
segs, entry);
if (err == 0)
queued++;
else
kfree_skb(segs);
segs = nskb;
} while (segs);
if (queued) {
if (err) /* some segments are already queued */
free_entry(entry);
kfree_skb(skb);
return 0;
}
out_err:
nf_bridge_adjust_segmented_data(skb);
return err;
}
static int
nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff)
{

View File

@ -1,7 +1,7 @@
/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2003-2013 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
@ -30,7 +30,7 @@ MODULE_ALIAS("ip6t_SET");
static inline int
match_set(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt, int inv)
struct ip_set_adt_opt *opt, int inv)
{
if (ip_set_test(index, skb, par, opt))
inv = !inv;
@ -38,20 +38,12 @@ match_set(ip_set_id_t index, const struct sk_buff *skb,
}
#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, \
}
#define ADT_MOPT(n, f, d, fs, cfs, t) \
struct ip_set_adt_opt n = { \
.family = f, \
.dim = d, \
.flags = fs, \
.cmdflags = cfs, \
.timeout = t, \
.ext.timeout = t, \
}
/* Revision 0 interface: backward compatible with netfilter/iptables */
@ -197,6 +189,9 @@ set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
ADT_OPT(opt, par->family, info->match_set.dim,
info->match_set.flags, 0, UINT_MAX);
if (opt.flags & IPSET_RETURN_NOMATCH)
opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
return match_set(info->match_set.index, skb, par, &opt,
info->match_set.flags & IPSET_INV_MATCH);
}
@ -305,15 +300,15 @@ 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_MOPT(add_opt, par->family, info->add_set.dim,
info->add_set.flags, info->flags, info->timeout);
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);
/* Normalize to fit into jiffies */
if (add_opt.timeout != IPSET_NO_TIMEOUT &&
add_opt.timeout > UINT_MAX/MSEC_PER_SEC)
add_opt.timeout = UINT_MAX/MSEC_PER_SEC;
if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
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)
@ -325,6 +320,52 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
#define set_target_v2_checkentry set_target_v1_checkentry
#define set_target_v2_destroy set_target_v1_destroy
/* Revision 3 match */
static bool
match_counter(u64 counter, const struct ip_set_counter_match *info)
{
switch (info->op) {
case IPSET_COUNTER_NONE:
return true;
case IPSET_COUNTER_EQ:
return counter == info->value;
case IPSET_COUNTER_NE:
return counter != info->value;
case IPSET_COUNTER_LT:
return counter < info->value;
case IPSET_COUNTER_GT:
return counter > info->value;
}
return false;
}
static bool
set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_set_info_match_v3 *info = par->matchinfo;
ADT_OPT(opt, par->family, info->match_set.dim,
info->match_set.flags, info->flags, UINT_MAX);
int ret;
if (info->packets.op != IPSET_COUNTER_NONE ||
info->bytes.op != IPSET_COUNTER_NONE)
opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
ret = match_set(info->match_set.index, skb, par, &opt,
info->match_set.flags & IPSET_INV_MATCH);
if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
return ret;
if (!match_counter(opt.ext.packets, &info->packets))
return 0;
return match_counter(opt.ext.bytes, &info->bytes);
}
#define set_match_v3_checkentry set_match_v1_checkentry
#define set_match_v3_destroy set_match_v1_destroy
static struct xt_match set_matches[] __read_mostly = {
{
.name = "set",
@ -377,6 +418,27 @@ static struct xt_match set_matches[] __read_mostly = {
.destroy = set_match_v1_destroy,
.me = THIS_MODULE
},
/* counters support: update, match */
{
.name = "set",
.family = NFPROTO_IPV4,
.revision = 3,
.match = set_match_v3,
.matchsize = sizeof(struct xt_set_info_match_v3),
.checkentry = set_match_v3_checkentry,
.destroy = set_match_v3_destroy,
.me = THIS_MODULE
},
{
.name = "set",
.family = NFPROTO_IPV6,
.revision = 3,
.match = set_match_v3,
.matchsize = sizeof(struct xt_set_info_match_v3),
.checkentry = set_match_v3_checkentry,
.destroy = set_match_v3_destroy,
.me = THIS_MODULE
},
};
static struct xt_target set_targets[] __read_mostly = {

View File

@ -83,7 +83,7 @@ static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em,
opt.dim = set->dim;
opt.flags = set->flags;
opt.cmdflags = 0;
opt.timeout = ~0u;
opt.ext.timeout = ~0u;
network_offset = skb_network_offset(skb);
skb_pull(skb, network_offset);