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

Pablo Neira Ayuso says:

====================
The following patchset contains Netfilter updates for your net-next tree,
mostly ipset improvements and enhancements features, they are:

* Don't call ip_nest_end needlessly in the error path from me, suggested
  by Pablo Neira Ayuso, from Jozsef Kadlecsik.

* Fixed sparse warnings about shadowed variable and missing rcu annotation
  and fix of "may be used uninitialized" warnings, also from Jozsef.

* Renamed simple macro names to avoid namespace issues, reported by David
  Laight, again from Jozsef.

* Use fix sized type for timeout in the extension part, and cosmetic
  ordering of matches and targets separatedly in xt_set.c, from Jozsef.

* Support package fragments for IPv4 protos without ports from Anders K.
  Pedersen. For example this allows a hash:ip,port ipset containing the
  entry 192.168.0.1,gre:0 to match all package fragments for PPTP VPN
  tunnels to/from the host. Without this patch only the first package
  fragment (with fragment offset 0) was matched.

* Introduced a new operation to get both setname and family, from Jozsef.
  ip[6]tables set match and SET target need to know the family of the set
  in order to reject adding rules which refer to a set with a non-mathcing
  family. Currently such rules are silently accepted and then ignored
  instead of generating an error message to the user.

* Reworked extensions support in ipset types from Jozsef. The approach of
  defining structures with all variations is not manageable as the
  number of extensions grows. Therefore a blob for the extensions is
  introduced, somewhat similar to conntrack. The support of extensions
  which need a per data destroy function is added as well.

* When an element timed out in a list:set type of set, the garbage
  collector skipped the checking of the next element. So the purging
  was delayed to the next run of the gc, fixed by Jozsef.

* A small Kconfig fix: NETFILTER_NETLINK cannot be selected and
  ipset requires it.

* hash:net,net type from Oliver Smith. The type provides the ability to
  store pairs of subnets in a set.

* Comment for ipset entries from Oliver Smith. This makes possible to
  annotate entries in a set with comments, for example:

  ipset n foo hash:net,net comment
  ipset a foo 10.0.0.0/21,192.168.1.0/24 comment "office nets A and B"

* Fix of hash types resizing with comment extension from Jozsef.

* Fix of new extensions for list:set type when an element is added
  into a slot from where another element was pushed away from Jozsef.

* Introduction of a common function for the listing of the element
  extensions from Jozsef.

* Net namespace support for ipset from Vitaly Lavrov.

* hash:net,port,net type from Oliver Smith, which makes possible
  to store the triples of two subnets and a protocol, port pair in
  a set.

* Get xt_TCPMSS working with net namespace, by Gao feng.

* Use the proper net netnamespace to allocate skbs, also by Gao feng.

* A couple of cleanups for the conntrack SIP helper, by Holger
  Eitzenberger.

* Extend cttimeout to allow setting default conntrack timeouts via
  nfnetlink, so we can get rid of all our sysctl/proc interfaces in
  the future for timeout tuning, from me.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2013-10-04 13:26:38 -04:00
commit d639feaaf3
33 changed files with 2743 additions and 1797 deletions

View file

@ -49,31 +49,68 @@ enum ip_set_feature {
/* Set extensions */ /* Set extensions */
enum ip_set_extension { enum ip_set_extension {
IPSET_EXT_NONE = 0, IPSET_EXT_BIT_TIMEOUT = 0,
IPSET_EXT_BIT_TIMEOUT = 1,
IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT), IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT),
IPSET_EXT_BIT_COUNTER = 2, IPSET_EXT_BIT_COUNTER = 1,
IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER), IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER),
}; IPSET_EXT_BIT_COMMENT = 2,
IPSET_EXT_COMMENT = (1 << IPSET_EXT_BIT_COMMENT),
/* Extension offsets */ /* Mark set with an extension which needs to call destroy */
enum ip_set_offset { IPSET_EXT_BIT_DESTROY = 7,
IPSET_OFFSET_TIMEOUT = 0, IPSET_EXT_DESTROY = (1 << IPSET_EXT_BIT_DESTROY),
IPSET_OFFSET_COUNTER,
IPSET_OFFSET_MAX,
}; };
#define SET_WITH_TIMEOUT(s) ((s)->extensions & IPSET_EXT_TIMEOUT) #define SET_WITH_TIMEOUT(s) ((s)->extensions & IPSET_EXT_TIMEOUT)
#define SET_WITH_COUNTER(s) ((s)->extensions & IPSET_EXT_COUNTER) #define SET_WITH_COUNTER(s) ((s)->extensions & IPSET_EXT_COUNTER)
#define SET_WITH_COMMENT(s) ((s)->extensions & IPSET_EXT_COMMENT)
/* Extension id, in size order */
enum ip_set_ext_id {
IPSET_EXT_ID_COUNTER = 0,
IPSET_EXT_ID_TIMEOUT,
IPSET_EXT_ID_COMMENT,
IPSET_EXT_ID_MAX,
};
/* Extension type */
struct ip_set_ext_type {
/* Destroy extension private data (can be NULL) */
void (*destroy)(void *ext);
enum ip_set_extension type;
enum ipset_cadt_flags flag;
/* Size and minimal alignment */
u8 len;
u8 align;
};
extern const struct ip_set_ext_type ip_set_extensions[];
struct ip_set_ext { struct ip_set_ext {
unsigned long timeout;
u64 packets; u64 packets;
u64 bytes; u64 bytes;
u32 timeout;
char *comment;
};
struct ip_set_counter {
atomic64_t bytes;
atomic64_t packets;
};
struct ip_set_comment {
char *str;
}; };
struct ip_set; struct ip_set;
#define ext_timeout(e, s) \
(unsigned long *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_TIMEOUT])
#define ext_counter(e, s) \
(struct ip_set_counter *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COUNTER])
#define ext_comment(e, s) \
(struct ip_set_comment *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COMMENT])
typedef int (*ipset_adtfn)(struct ip_set *set, void *value, typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
const struct ip_set_ext *ext, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 cmdflags); struct ip_set_ext *mext, u32 cmdflags);
@ -147,7 +184,8 @@ struct ip_set_type {
u8 revision_min, revision_max; u8 revision_min, revision_max;
/* Create set */ /* Create set */
int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags); int (*create)(struct net *net, struct ip_set *set,
struct nlattr *tb[], u32 flags);
/* Attribute policies */ /* Attribute policies */
const struct nla_policy create_policy[IPSET_ATTR_CREATE_MAX + 1]; const struct nla_policy create_policy[IPSET_ATTR_CREATE_MAX + 1];
@ -179,14 +217,45 @@ struct ip_set {
u8 revision; u8 revision;
/* Extensions */ /* Extensions */
u8 extensions; u8 extensions;
/* Default timeout value, if enabled */
u32 timeout;
/* Element data size */
size_t dsize;
/* Offsets to extensions in elements */
size_t offset[IPSET_EXT_ID_MAX];
/* The type specific data */ /* The type specific data */
void *data; void *data;
}; };
struct ip_set_counter { static inline void
atomic64_t bytes; ip_set_ext_destroy(struct ip_set *set, void *data)
atomic64_t packets; {
}; /* Check that the extension is enabled for the set and
* call it's destroy function for its extension part in data.
*/
if (SET_WITH_COMMENT(set))
ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy(
ext_comment(data, set));
}
static inline int
ip_set_put_flags(struct sk_buff *skb, struct ip_set *set)
{
u32 cadt_flags = 0;
if (SET_WITH_TIMEOUT(set))
if (unlikely(nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(set->timeout))))
return -EMSGSIZE;
if (SET_WITH_COUNTER(set))
cadt_flags |= IPSET_FLAG_WITH_COUNTERS;
if (SET_WITH_COMMENT(set))
cadt_flags |= IPSET_FLAG_WITH_COMMENT;
if (!cadt_flags)
return 0;
return nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(cadt_flags));
}
static inline void static inline void
ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter) ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter)
@ -248,12 +317,13 @@ ip_set_init_counter(struct ip_set_counter *counter,
} }
/* register and unregister set references */ /* register and unregister set references */
extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set); extern ip_set_id_t ip_set_get_byname(struct net *net,
extern void ip_set_put_byindex(ip_set_id_t index); const char *name, struct ip_set **set);
extern const char *ip_set_name_byindex(ip_set_id_t index); extern void ip_set_put_byindex(struct net *net, ip_set_id_t index);
extern ip_set_id_t ip_set_nfnl_get(const char *name); extern const char *ip_set_name_byindex(struct net *net, ip_set_id_t index);
extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); extern ip_set_id_t ip_set_nfnl_get(struct net *net, const char *name);
extern void ip_set_nfnl_put(ip_set_id_t index); extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index);
extern void ip_set_nfnl_put(struct net *net, ip_set_id_t index);
/* API for iptables set match, and SET target */ /* API for iptables set match, and SET target */
@ -272,6 +342,8 @@ extern void *ip_set_alloc(size_t size);
extern void ip_set_free(void *members); extern void ip_set_free(void *members);
extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr); 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_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr);
extern size_t ip_set_elem_len(struct ip_set *set, struct nlattr *tb[],
size_t len);
extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
struct ip_set_ext *ext); struct ip_set_ext *ext);
@ -389,13 +461,40 @@ bitmap_bytes(u32 a, u32 b)
} }
#include <linux/netfilter/ipset/ip_set_timeout.h> #include <linux/netfilter/ipset/ip_set_timeout.h>
#include <linux/netfilter/ipset/ip_set_comment.h>
#define IP_SET_INIT_KEXT(skb, opt, map) \ static inline int
ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set,
const void *e, bool active)
{
if (SET_WITH_TIMEOUT(set)) {
unsigned long *timeout = ext_timeout(e, set);
if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(active ? ip_set_timeout_get(timeout)
: *timeout)))
return -EMSGSIZE;
}
if (SET_WITH_COUNTER(set) &&
ip_set_put_counter(skb, ext_counter(e, set)))
return -EMSGSIZE;
if (SET_WITH_COMMENT(set) &&
ip_set_put_comment(skb, ext_comment(e, set)))
return -EMSGSIZE;
return 0;
}
#define IP_SET_INIT_KEXT(skb, opt, set) \
{ .bytes = (skb)->len, .packets = 1, \ { .bytes = (skb)->len, .packets = 1, \
.timeout = ip_set_adt_opt_timeout(opt, map) } .timeout = ip_set_adt_opt_timeout(opt, set) }
#define IP_SET_INIT_UEXT(map) \ #define IP_SET_INIT_UEXT(set) \
{ .bytes = ULLONG_MAX, .packets = ULLONG_MAX, \ { .bytes = ULLONG_MAX, .packets = ULLONG_MAX, \
.timeout = (map)->timeout } .timeout = (set)->timeout }
#define IP_SET_INIT_CIDR(a, b) ((a) ? (a) : (b))
#define IPSET_CONCAT(a, b) a##b
#define IPSET_TOKEN(a, b) IPSET_CONCAT(a, b)
#endif /*_IP_SET_H */ #endif /*_IP_SET_H */

View file

@ -0,0 +1,57 @@
#ifndef _IP_SET_COMMENT_H
#define _IP_SET_COMMENT_H
/* Copyright (C) 2013 Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifdef __KERNEL__
static inline char*
ip_set_comment_uget(struct nlattr *tb)
{
return nla_data(tb);
}
static inline void
ip_set_init_comment(struct ip_set_comment *comment,
const struct ip_set_ext *ext)
{
size_t len = ext->comment ? strlen(ext->comment) : 0;
if (unlikely(comment->str)) {
kfree(comment->str);
comment->str = NULL;
}
if (!len)
return;
if (unlikely(len > IPSET_MAX_COMMENT_SIZE))
len = IPSET_MAX_COMMENT_SIZE;
comment->str = kzalloc(len + 1, GFP_ATOMIC);
if (unlikely(!comment->str))
return;
strlcpy(comment->str, ext->comment, len + 1);
}
static inline int
ip_set_put_comment(struct sk_buff *skb, struct ip_set_comment *comment)
{
if (!comment->str)
return 0;
return nla_put_string(skb, IPSET_ATTR_COMMENT, comment->str);
}
static inline void
ip_set_comment_free(struct ip_set_comment *comment)
{
if (unlikely(!comment->str))
return;
kfree(comment->str);
comment->str = NULL;
}
#endif
#endif

View file

@ -23,8 +23,8 @@
/* Set is defined with timeout support: timeout value may be 0 */ /* Set is defined with timeout support: timeout value may be 0 */
#define IPSET_NO_TIMEOUT UINT_MAX #define IPSET_NO_TIMEOUT UINT_MAX
#define ip_set_adt_opt_timeout(opt, map) \ #define ip_set_adt_opt_timeout(opt, set) \
((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (map)->timeout) ((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (set)->timeout)
static inline unsigned int static inline unsigned int
ip_set_timeout_uget(struct nlattr *tb) ip_set_timeout_uget(struct nlattr *tb)

View file

@ -107,55 +107,64 @@ enum sdp_header_types {
SDP_HDR_MEDIA, SDP_HDR_MEDIA,
}; };
extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, struct nf_nat_sip_hooks {
unsigned int protoff, unsigned int (*msg)(struct sk_buff *skb,
unsigned int dataoff, unsigned int protoff,
const char **dptr, unsigned int dataoff,
unsigned int *datalen); const char **dptr,
extern void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int *datalen);
unsigned int protoff, s16 off);
extern unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, void (*seq_adjust)(struct sk_buff *skb,
unsigned int protoff, unsigned int protoff, s16 off);
unsigned int dataoff,
const char **dptr, unsigned int (*expect)(struct sk_buff *skb,
unsigned int *datalen, unsigned int protoff,
struct nf_conntrack_expect *exp, unsigned int dataoff,
unsigned int matchoff, const char **dptr,
unsigned int matchlen); unsigned int *datalen,
extern unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, struct nf_conntrack_expect *exp,
unsigned int protoff, unsigned int matchoff,
unsigned int dataoff, unsigned int matchlen);
const char **dptr,
unsigned int *datalen, unsigned int (*sdp_addr)(struct sk_buff *skb,
unsigned int sdpoff, unsigned int protoff,
enum sdp_header_types type, unsigned int dataoff,
enum sdp_header_types term, const char **dptr,
const union nf_inet_addr *addr); unsigned int *datalen,
extern unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int sdpoff,
unsigned int protoff, enum sdp_header_types type,
unsigned int dataoff, enum sdp_header_types term,
const char **dptr, const union nf_inet_addr *addr);
unsigned int *datalen,
unsigned int matchoff, unsigned int (*sdp_port)(struct sk_buff *skb,
unsigned int matchlen, unsigned int protoff,
u_int16_t port); unsigned int dataoff,
extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, const char **dptr,
unsigned int protoff, unsigned int *datalen,
unsigned int dataoff, unsigned int matchoff,
const char **dptr, unsigned int matchlen,
unsigned int *datalen, u_int16_t port);
unsigned int sdpoff,
const union nf_inet_addr *addr); unsigned int (*sdp_session)(struct sk_buff *skb,
extern unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff,
unsigned int protoff, unsigned int dataoff,
unsigned int dataoff, const char **dptr,
const char **dptr, unsigned int *datalen,
unsigned int *datalen, unsigned int sdpoff,
struct nf_conntrack_expect *rtp_exp, const union nf_inet_addr *addr);
struct nf_conntrack_expect *rtcp_exp,
unsigned int mediaoff, unsigned int (*sdp_media)(struct sk_buff *skb,
unsigned int medialen, unsigned int protoff,
union nf_inet_addr *rtp_addr); unsigned int dataoff,
const char **dptr,
unsigned int *datalen,
struct nf_conntrack_expect *rtp_exp,
struct nf_conntrack_expect *rtcp_exp,
unsigned int mediaoff,
unsigned int medialen,
union nf_inet_addr *rtp_addr);
};
extern const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
int ct_sip_parse_request(const struct nf_conn *ct, const char *dptr, int ct_sip_parse_request(const struct nf_conn *ct, const char *dptr,
unsigned int datalen, unsigned int *matchoff, unsigned int datalen, unsigned int *matchoff,

View file

@ -10,12 +10,14 @@
#ifndef _UAPI_IP_SET_H #ifndef _UAPI_IP_SET_H
#define _UAPI_IP_SET_H #define _UAPI_IP_SET_H
#include <linux/types.h> #include <linux/types.h>
/* The protocol version */ /* The protocol version */
#define IPSET_PROTOCOL 6 #define IPSET_PROTOCOL 6
/* The maximum permissible comment length we will accept over netlink */
#define IPSET_MAX_COMMENT_SIZE 255
/* The max length of strings including NUL: set and type identifiers */ /* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32 #define IPSET_MAXNAMELEN 32
@ -110,6 +112,7 @@ enum {
IPSET_ATTR_IFACE, IPSET_ATTR_IFACE,
IPSET_ATTR_BYTES, IPSET_ATTR_BYTES,
IPSET_ATTR_PACKETS, IPSET_ATTR_PACKETS,
IPSET_ATTR_COMMENT,
__IPSET_ATTR_ADT_MAX, __IPSET_ATTR_ADT_MAX,
}; };
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) #define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
@ -140,6 +143,7 @@ enum ipset_errno {
IPSET_ERR_IPADDR_IPV4, IPSET_ERR_IPADDR_IPV4,
IPSET_ERR_IPADDR_IPV6, IPSET_ERR_IPADDR_IPV6,
IPSET_ERR_COUNTER, IPSET_ERR_COUNTER,
IPSET_ERR_COMMENT,
/* Type specific error codes */ /* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 4352, IPSET_ERR_TYPE_SPECIFIC = 4352,
@ -176,6 +180,8 @@ enum ipset_cadt_flags {
IPSET_FLAG_NOMATCH = (1 << IPSET_FLAG_BIT_NOMATCH), IPSET_FLAG_NOMATCH = (1 << IPSET_FLAG_BIT_NOMATCH),
IPSET_FLAG_BIT_WITH_COUNTERS = 3, IPSET_FLAG_BIT_WITH_COUNTERS = 3,
IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS), IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
IPSET_FLAG_BIT_WITH_COMMENT = 4,
IPSET_FLAG_WITH_COMMENT = (1 << IPSET_FLAG_BIT_WITH_COMMENT),
IPSET_FLAG_CADT_MAX = 15, IPSET_FLAG_CADT_MAX = 15,
}; };
@ -250,6 +256,14 @@ struct ip_set_req_get_set {
#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ #define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */
/* Uses ip_set_req_get_set */ /* Uses ip_set_req_get_set */
#define IP_SET_OP_GET_FNAME 0x00000008 /* Get set index and family */
struct ip_set_req_get_set_family {
unsigned int op;
unsigned int version;
unsigned int family;
union ip_set_name_index set;
};
#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ #define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
struct ip_set_req_version { struct ip_set_req_version {
unsigned int op; unsigned int op;

View file

@ -6,6 +6,8 @@ enum ctnl_timeout_msg_types {
IPCTNL_MSG_TIMEOUT_NEW, IPCTNL_MSG_TIMEOUT_NEW,
IPCTNL_MSG_TIMEOUT_GET, IPCTNL_MSG_TIMEOUT_GET,
IPCTNL_MSG_TIMEOUT_DELETE, IPCTNL_MSG_TIMEOUT_DELETE,
IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
IPCTNL_MSG_TIMEOUT_DEFAULT_GET,
IPCTNL_MSG_TIMEOUT_MAX IPCTNL_MSG_TIMEOUT_MAX
}; };

View file

@ -1,7 +1,7 @@
menuconfig IP_SET menuconfig IP_SET
tristate "IP set support" tristate "IP set support"
depends on INET && NETFILTER depends on INET && NETFILTER
depends on NETFILTER_NETLINK select NETFILTER_NETLINK
help help
This option adds IP set support to the kernel. This option adds IP set support to the kernel.
In order to define and use the sets, you need the userspace utility In order to define and use the sets, you need the userspace utility
@ -90,6 +90,15 @@ config IP_SET_HASH_IPPORTNET
To compile it as a module, choose M here. If unsure, say N. To compile it as a module, choose M here. If unsure, say N.
config IP_SET_HASH_NETPORTNET
tristate "hash:net,port,net set support"
depends on IP_SET
help
This option adds the hash:net,port,net set type support, by which
one can store two IPv4/IPv6 subnets, and a protocol/port in a set.
To compile it as a module, choose M here. If unsure, say N.
config IP_SET_HASH_NET config IP_SET_HASH_NET
tristate "hash:net set support" tristate "hash:net set support"
depends on IP_SET depends on IP_SET
@ -99,6 +108,15 @@ config IP_SET_HASH_NET
To compile it as a module, choose M here. If unsure, say N. To compile it as a module, choose M here. If unsure, say N.
config IP_SET_HASH_NETNET
tristate "hash:net,net set support"
depends on IP_SET
help
This option adds the hash:net,net set type support, by which
one can store IPv4/IPv6 network address/prefix pairs in a set.
To compile it as a module, choose M here. If unsure, say N.
config IP_SET_HASH_NETPORT config IP_SET_HASH_NETPORT
tristate "hash:net,port set support" tristate "hash:net,port set support"
depends on IP_SET depends on IP_SET

View file

@ -20,6 +20,8 @@ obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o
obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o
obj-$(CONFIG_IP_SET_HASH_NETIFACE) += ip_set_hash_netiface.o obj-$(CONFIG_IP_SET_HASH_NETIFACE) += ip_set_hash_netiface.o
obj-$(CONFIG_IP_SET_HASH_NETNET) += ip_set_hash_netnet.o
obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o
# list types # list types
obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o

View file

@ -8,38 +8,32 @@
#ifndef __IP_SET_BITMAP_IP_GEN_H #ifndef __IP_SET_BITMAP_IP_GEN_H
#define __IP_SET_BITMAP_IP_GEN_H #define __IP_SET_BITMAP_IP_GEN_H
#define CONCAT(a, b) a##b #define mtype_do_test IPSET_TOKEN(MTYPE, _do_test)
#define TOKEN(a,b) CONCAT(a, b) #define mtype_gc_test IPSET_TOKEN(MTYPE, _gc_test)
#define mtype_is_filled IPSET_TOKEN(MTYPE, _is_filled)
#define mtype_do_test TOKEN(MTYPE, _do_test) #define mtype_do_add IPSET_TOKEN(MTYPE, _do_add)
#define mtype_gc_test TOKEN(MTYPE, _gc_test) #define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup)
#define mtype_is_filled TOKEN(MTYPE, _is_filled) #define mtype_do_del IPSET_TOKEN(MTYPE, _do_del)
#define mtype_do_add TOKEN(MTYPE, _do_add) #define mtype_do_list IPSET_TOKEN(MTYPE, _do_list)
#define mtype_do_del TOKEN(MTYPE, _do_del) #define mtype_do_head IPSET_TOKEN(MTYPE, _do_head)
#define mtype_do_list TOKEN(MTYPE, _do_list) #define mtype_adt_elem IPSET_TOKEN(MTYPE, _adt_elem)
#define mtype_do_head TOKEN(MTYPE, _do_head) #define mtype_add_timeout IPSET_TOKEN(MTYPE, _add_timeout)
#define mtype_adt_elem TOKEN(MTYPE, _adt_elem) #define mtype_gc_init IPSET_TOKEN(MTYPE, _gc_init)
#define mtype_add_timeout TOKEN(MTYPE, _add_timeout) #define mtype_kadt IPSET_TOKEN(MTYPE, _kadt)
#define mtype_gc_init TOKEN(MTYPE, _gc_init) #define mtype_uadt IPSET_TOKEN(MTYPE, _uadt)
#define mtype_kadt TOKEN(MTYPE, _kadt) #define mtype_destroy IPSET_TOKEN(MTYPE, _destroy)
#define mtype_uadt TOKEN(MTYPE, _uadt) #define mtype_flush IPSET_TOKEN(MTYPE, _flush)
#define mtype_destroy TOKEN(MTYPE, _destroy) #define mtype_head IPSET_TOKEN(MTYPE, _head)
#define mtype_flush TOKEN(MTYPE, _flush) #define mtype_same_set IPSET_TOKEN(MTYPE, _same_set)
#define mtype_head TOKEN(MTYPE, _head) #define mtype_elem IPSET_TOKEN(MTYPE, _elem)
#define mtype_same_set TOKEN(MTYPE, _same_set) #define mtype_test IPSET_TOKEN(MTYPE, _test)
#define mtype_elem TOKEN(MTYPE, _elem) #define mtype_add IPSET_TOKEN(MTYPE, _add)
#define mtype_test TOKEN(MTYPE, _test) #define mtype_del IPSET_TOKEN(MTYPE, _del)
#define mtype_add TOKEN(MTYPE, _add) #define mtype_list IPSET_TOKEN(MTYPE, _list)
#define mtype_del TOKEN(MTYPE, _del) #define mtype_gc IPSET_TOKEN(MTYPE, _gc)
#define mtype_list TOKEN(MTYPE, _list)
#define mtype_gc TOKEN(MTYPE, _gc)
#define mtype MTYPE #define mtype MTYPE
#define ext_timeout(e, m) \ #define get_ext(set, map, id) ((map)->extensions + (set)->dsize * (id))
(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 static void
mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
@ -49,10 +43,21 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
init_timer(&map->gc); init_timer(&map->gc);
map->gc.data = (unsigned long) set; map->gc.data = (unsigned long) set;
map->gc.function = gc; map->gc.function = gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
add_timer(&map->gc); add_timer(&map->gc);
} }
static void
mtype_ext_cleanup(struct ip_set *set)
{
struct mtype *map = set->data;
u32 id;
for (id = 0; id < map->elements; id++)
if (test_bit(id, map->members))
ip_set_ext_destroy(set, get_ext(set, map, id));
}
static void static void
mtype_destroy(struct ip_set *set) mtype_destroy(struct ip_set *set)
{ {
@ -62,8 +67,11 @@ mtype_destroy(struct ip_set *set)
del_timer_sync(&map->gc); del_timer_sync(&map->gc);
ip_set_free(map->members); ip_set_free(map->members);
if (map->dsize) if (set->dsize) {
if (set->extensions & IPSET_EXT_DESTROY)
mtype_ext_cleanup(set);
ip_set_free(map->extensions); ip_set_free(map->extensions);
}
kfree(map); kfree(map);
set->data = NULL; set->data = NULL;
@ -74,6 +82,8 @@ mtype_flush(struct ip_set *set)
{ {
struct mtype *map = set->data; struct mtype *map = set->data;
if (set->extensions & IPSET_EXT_DESTROY)
mtype_ext_cleanup(set);
memset(map->members, 0, map->memsize); memset(map->members, 0, map->memsize);
} }
@ -91,12 +101,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
nla_put_net32(skb, IPSET_ATTR_MEMSIZE, nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + htonl(sizeof(*map) +
map->memsize + map->memsize +
map->dsize * map->elements)) || set->dsize * map->elements)))
(SET_WITH_TIMEOUT(set) && goto nla_put_failure;
nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) || if (unlikely(ip_set_put_flags(skb, set)))
(SET_WITH_COUNTER(set) &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
htonl(IPSET_FLAG_WITH_COUNTERS))))
goto nla_put_failure; goto nla_put_failure;
ipset_nest_end(skb, nested); ipset_nest_end(skb, nested);
@ -111,16 +118,16 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
{ {
struct mtype *map = set->data; struct mtype *map = set->data;
const struct mtype_adt_elem *e = value; const struct mtype_adt_elem *e = value;
void *x = get_ext(map, e->id); void *x = get_ext(set, map, e->id);
int ret = mtype_do_test(e, map); int ret = mtype_do_test(e, map, set->dsize);
if (ret <= 0) if (ret <= 0)
return ret; return ret;
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(x, map))) ip_set_timeout_expired(ext_timeout(x, set)))
return 0; return 0;
if (SET_WITH_COUNTER(set)) if (SET_WITH_COUNTER(set))
ip_set_update_counter(ext_counter(x, map), ext, mext, flags); ip_set_update_counter(ext_counter(x, set), ext, mext, flags);
return 1; return 1;
} }
@ -130,26 +137,30 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
{ {
struct mtype *map = set->data; struct mtype *map = set->data;
const struct mtype_adt_elem *e = value; const struct mtype_adt_elem *e = value;
void *x = get_ext(map, e->id); void *x = get_ext(set, map, e->id);
int ret = mtype_do_add(e, map, flags); int ret = mtype_do_add(e, map, flags, set->dsize);
if (ret == IPSET_ADD_FAILED) { if (ret == IPSET_ADD_FAILED) {
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(x, map))) ip_set_timeout_expired(ext_timeout(x, set)))
ret = 0; ret = 0;
else if (!(flags & IPSET_FLAG_EXIST)) else if (!(flags & IPSET_FLAG_EXIST))
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
/* Element is re-added, cleanup extensions */
ip_set_ext_destroy(set, x);
} }
if (SET_WITH_TIMEOUT(set)) if (SET_WITH_TIMEOUT(set))
#ifdef IP_SET_BITMAP_STORED_TIMEOUT #ifdef IP_SET_BITMAP_STORED_TIMEOUT
mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret); mtype_add_timeout(ext_timeout(x, set), e, ext, set, map, ret);
#else #else
ip_set_timeout_set(ext_timeout(x, map), ext->timeout); ip_set_timeout_set(ext_timeout(x, set), ext->timeout);
#endif #endif
if (SET_WITH_COUNTER(set)) if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(x, map), ext); ip_set_init_counter(ext_counter(x, set), ext);
if (SET_WITH_COMMENT(set))
ip_set_init_comment(ext_comment(x, set), ext);
return 0; return 0;
} }
@ -159,16 +170,27 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
{ {
struct mtype *map = set->data; struct mtype *map = set->data;
const struct mtype_adt_elem *e = value; const struct mtype_adt_elem *e = value;
const void *x = get_ext(map, e->id); void *x = get_ext(set, map, e->id);
if (mtype_do_del(e, map) || if (mtype_do_del(e, map))
(SET_WITH_TIMEOUT(set) && return -IPSET_ERR_EXIST;
ip_set_timeout_expired(ext_timeout(x, map))))
ip_set_ext_destroy(set, x);
if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(x, set)))
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
return 0; return 0;
} }
#ifndef IP_SET_BITMAP_STORED_TIMEOUT
static inline bool
mtype_is_filled(const struct mtype_elem *x)
{
return true;
}
#endif
static int static int
mtype_list(const struct ip_set *set, mtype_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb) struct sk_buff *skb, struct netlink_callback *cb)
@ -183,13 +205,13 @@ mtype_list(const struct ip_set *set,
return -EMSGSIZE; return -EMSGSIZE;
for (; cb->args[2] < map->elements; cb->args[2]++) { for (; cb->args[2] < map->elements; cb->args[2]++) {
id = cb->args[2]; id = cb->args[2];
x = get_ext(map, id); x = get_ext(set, map, id);
if (!test_bit(id, map->members) || if (!test_bit(id, map->members) ||
(SET_WITH_TIMEOUT(set) && (SET_WITH_TIMEOUT(set) &&
#ifdef IP_SET_BITMAP_STORED_TIMEOUT #ifdef IP_SET_BITMAP_STORED_TIMEOUT
mtype_is_filled((const struct mtype_elem *) x) && mtype_is_filled((const struct mtype_elem *) x) &&
#endif #endif
ip_set_timeout_expired(ext_timeout(x, map)))) ip_set_timeout_expired(ext_timeout(x, set))))
continue; continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA); nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) { if (!nested) {
@ -199,23 +221,10 @@ mtype_list(const struct ip_set *set,
} else } else
goto nla_put_failure; goto nla_put_failure;
} }
if (mtype_do_list(skb, map, id)) if (mtype_do_list(skb, map, id, set->dsize))
goto nla_put_failure; goto nla_put_failure;
if (SET_WITH_TIMEOUT(set)) { if (ip_set_put_extensions(skb, set, x,
#ifdef IP_SET_BITMAP_STORED_TIMEOUT mtype_is_filled((const struct mtype_elem *) x)))
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; goto nla_put_failure;
ipset_nest_end(skb, nested); ipset_nest_end(skb, nested);
} }
@ -228,11 +237,11 @@ mtype_list(const struct ip_set *set,
nla_put_failure: nla_put_failure:
nla_nest_cancel(skb, nested); nla_nest_cancel(skb, nested);
ipset_nest_end(skb, adt);
if (unlikely(id == first)) { if (unlikely(id == first)) {
cb->args[2] = 0; cb->args[2] = 0;
return -EMSGSIZE; return -EMSGSIZE;
} }
ipset_nest_end(skb, adt);
return 0; return 0;
} }
@ -241,21 +250,23 @@ mtype_gc(unsigned long ul_set)
{ {
struct ip_set *set = (struct ip_set *) ul_set; struct ip_set *set = (struct ip_set *) ul_set;
struct mtype *map = set->data; struct mtype *map = set->data;
const void *x; void *x;
u32 id; u32 id;
/* We run parallel with other readers (test element) /* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */ * but adding/deleting new entries is locked out */
read_lock_bh(&set->lock); read_lock_bh(&set->lock);
for (id = 0; id < map->elements; id++) for (id = 0; id < map->elements; id++)
if (mtype_gc_test(id, map)) { if (mtype_gc_test(id, map, set->dsize)) {
x = get_ext(map, id); x = get_ext(set, map, id);
if (ip_set_timeout_expired(ext_timeout(x, map))) if (ip_set_timeout_expired(ext_timeout(x, set))) {
clear_bit(id, map->members); clear_bit(id, map->members);
ip_set_ext_destroy(set, x);
}
} }
read_unlock_bh(&set->lock); read_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
add_timer(&map->gc); add_timer(&map->gc);
} }

View file

@ -25,12 +25,13 @@
#include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_bitmap.h> #include <linux/netfilter/ipset/ip_set_bitmap.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
#define REVISION_MAX 1 /* Counter support added */ /* 1 Counter support added */
#define IPSET_TYPE_REV_MAX 2 /* Comment support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("bitmap:ip", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("bitmap:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_bitmap:ip"); MODULE_ALIAS("ip_set_bitmap:ip");
#define MTYPE bitmap_ip #define MTYPE bitmap_ip
@ -44,10 +45,7 @@ struct bitmap_ip {
u32 elements; /* number of max elements in the set */ u32 elements; /* number of max elements in the set */
u32 hosts; /* number of hosts in a subnet */ u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */ size_t memsize; /* members size */
size_t dsize; /* extensions struct size */
size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
u8 netmask; /* subnet netmask */ u8 netmask; /* subnet netmask */
u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */ struct timer_list gc; /* garbage collection */
}; };
@ -65,20 +63,21 @@ ip_to_id(const struct bitmap_ip *m, u32 ip)
/* Common functions */ /* Common functions */
static inline int static inline int
bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map) bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e,
struct bitmap_ip *map, size_t dsize)
{ {
return !!test_bit(e->id, map->members); return !!test_bit(e->id, map->members);
} }
static inline int static inline int
bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map) bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map, size_t dsize)
{ {
return !!test_bit(id, map->members); return !!test_bit(id, map->members);
} }
static inline int static inline int
bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map, bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map,
u32 flags) u32 flags, size_t dsize)
{ {
return !!test_and_set_bit(e->id, map->members); return !!test_and_set_bit(e->id, map->members);
} }
@ -90,7 +89,8 @@ bitmap_ip_do_del(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
} }
static inline int static inline int
bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id) bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id,
size_t dsize)
{ {
return nla_put_ipaddr4(skb, IPSET_ATTR_IP, return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts)); htonl(map->first_ip + id * map->hosts));
@ -113,7 +113,7 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
struct bitmap_ip *map = set->data; struct bitmap_ip *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct bitmap_ip_adt_elem e = { }; struct bitmap_ip_adt_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
u32 ip; u32 ip;
ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC)); ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
@ -131,9 +131,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
{ {
struct bitmap_ip *map = set->data; struct bitmap_ip *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip, ip_to; u32 ip = 0, ip_to = 0;
struct bitmap_ip_adt_elem e = { }; struct bitmap_ip_adt_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(map); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
int ret = 0; int ret = 0;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
@ -200,7 +200,7 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
return x->first_ip == y->first_ip && return x->first_ip == y->first_ip &&
x->last_ip == y->last_ip && x->last_ip == y->last_ip &&
x->netmask == y->netmask && x->netmask == y->netmask &&
x->timeout == y->timeout && a->timeout == b->timeout &&
a->extensions == b->extensions; a->extensions == b->extensions;
} }
@ -209,25 +209,6 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
struct bitmap_ip_elem { struct bitmap_ip_elem {
}; };
/* Timeout variant */
struct bitmap_ipt_elem {
unsigned long timeout;
};
/* Plain variant with counter */
struct bitmap_ipc_elem {
struct ip_set_counter counter;
};
/* Timeout variant with counter */
struct bitmap_ipct_elem {
unsigned long timeout;
struct ip_set_counter counter;
};
#include "ip_set_bitmap_gen.h" #include "ip_set_bitmap_gen.h"
/* Create bitmap:ip type of sets */ /* Create bitmap:ip type of sets */
@ -240,8 +221,8 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
map->members = ip_set_alloc(map->memsize); map->members = ip_set_alloc(map->memsize);
if (!map->members) if (!map->members)
return false; return false;
if (map->dsize) { if (set->dsize) {
map->extensions = ip_set_alloc(map->dsize * elements); map->extensions = ip_set_alloc(set->dsize * elements);
if (!map->extensions) { if (!map->extensions) {
kfree(map->members); kfree(map->members);
return false; return false;
@ -252,7 +233,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
map->elements = elements; map->elements = elements;
map->hosts = hosts; map->hosts = hosts;
map->netmask = netmask; map->netmask = netmask;
map->timeout = IPSET_NO_TIMEOUT; set->timeout = IPSET_NO_TIMEOUT;
set->data = map; set->data = map;
set->family = NFPROTO_IPV4; set->family = NFPROTO_IPV4;
@ -261,10 +242,11 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
} }
static int static int
bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
u32 flags)
{ {
struct bitmap_ip *map; struct bitmap_ip *map;
u32 first_ip, last_ip, hosts, cadt_flags = 0; u32 first_ip = 0, last_ip = 0, hosts;
u64 elements; u64 elements;
u8 netmask = 32; u8 netmask = 32;
int ret; int ret;
@ -336,61 +318,15 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
map->memsize = bitmap_bytes(0, elements - 1); map->memsize = bitmap_bytes(0, elements - 1);
set->variant = &bitmap_ip; set->variant = &bitmap_ip;
if (tb[IPSET_ATTR_CADT_FLAGS]) set->dsize = ip_set_elem_len(set, tb, 0);
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); if (!init_map_ip(set, map, first_ip, last_ip,
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { elements, hosts, netmask)) {
set->extensions |= IPSET_EXT_COUNTER; kfree(map);
if (tb[IPSET_ATTR_TIMEOUT]) { return -ENOMEM;
map->dsize = sizeof(struct bitmap_ipct_elem); }
map->offset[IPSET_OFFSET_TIMEOUT] = if (tb[IPSET_ATTR_TIMEOUT]) {
offsetof(struct bitmap_ipct_elem, timeout); set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_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)) {
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); bitmap_ip_gc_init(set, bitmap_ip_gc);
} else {
map->dsize = 0;
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
} }
return 0; return 0;
} }
@ -401,8 +337,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
.features = IPSET_TYPE_IP, .features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = bitmap_ip_create, .create = bitmap_ip_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -420,6 +356,7 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
[IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -25,12 +25,13 @@
#include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_bitmap.h> #include <linux/netfilter/ipset/ip_set_bitmap.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
#define REVISION_MAX 1 /* Counter support added */ /* 1 Counter support added */
#define IPSET_TYPE_REV_MAX 2 /* Comment support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("bitmap:ip,mac", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("bitmap:ip,mac", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_bitmap:ip,mac"); MODULE_ALIAS("ip_set_bitmap:ip,mac");
#define MTYPE bitmap_ipmac #define MTYPE bitmap_ipmac
@ -48,11 +49,8 @@ struct bitmap_ipmac {
u32 first_ip; /* host byte order, included in range */ u32 first_ip; /* host byte order, included in range */
u32 last_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 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 memsize; /* members size */
size_t dsize; /* size of element */ struct timer_list gc; /* garbage collector */
size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
}; };
/* ADT structure for generic function args */ /* ADT structure for generic function args */
@ -82,13 +80,13 @@ get_elem(void *extensions, u16 id, size_t dsize)
static inline int static inline int
bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e,
const struct bitmap_ipmac *map) const struct bitmap_ipmac *map, size_t dsize)
{ {
const struct bitmap_ipmac_elem *elem; const struct bitmap_ipmac_elem *elem;
if (!test_bit(e->id, map->members)) if (!test_bit(e->id, map->members))
return 0; return 0;
elem = get_elem(map->extensions, e->id, map->dsize); elem = get_elem(map->extensions, e->id, dsize);
if (elem->filled == MAC_FILLED) if (elem->filled == MAC_FILLED)
return e->ether == NULL || return e->ether == NULL ||
ether_addr_equal(e->ether, elem->ether); ether_addr_equal(e->ether, elem->ether);
@ -97,13 +95,13 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e,
} }
static inline int static inline int
bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map) bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map, size_t dsize)
{ {
const struct bitmap_ipmac_elem *elem; const struct bitmap_ipmac_elem *elem;
if (!test_bit(id, map->members)) if (!test_bit(id, map->members))
return 0; return 0;
elem = get_elem(map->extensions, id, map->dsize); elem = get_elem(map->extensions, id, dsize);
/* Timer not started for the incomplete elements */ /* Timer not started for the incomplete elements */
return elem->filled == MAC_FILLED; return elem->filled == MAC_FILLED;
} }
@ -117,13 +115,13 @@ bitmap_ipmac_is_filled(const struct bitmap_ipmac_elem *elem)
static inline int static inline int
bitmap_ipmac_add_timeout(unsigned long *timeout, bitmap_ipmac_add_timeout(unsigned long *timeout,
const struct bitmap_ipmac_adt_elem *e, const struct bitmap_ipmac_adt_elem *e,
const struct ip_set_ext *ext, const struct ip_set_ext *ext, struct ip_set *set,
struct bitmap_ipmac *map, int mode) struct bitmap_ipmac *map, int mode)
{ {
u32 t = ext->timeout; u32 t = ext->timeout;
if (mode == IPSET_ADD_START_STORED_TIMEOUT) { if (mode == IPSET_ADD_START_STORED_TIMEOUT) {
if (t == map->timeout) if (t == set->timeout)
/* Timeout was not specified, get stored one */ /* Timeout was not specified, get stored one */
t = *timeout; t = *timeout;
ip_set_timeout_set(timeout, t); ip_set_timeout_set(timeout, t);
@ -142,11 +140,11 @@ bitmap_ipmac_add_timeout(unsigned long *timeout,
static inline int static inline int
bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e,
struct bitmap_ipmac *map, u32 flags) struct bitmap_ipmac *map, u32 flags, size_t dsize)
{ {
struct bitmap_ipmac_elem *elem; struct bitmap_ipmac_elem *elem;
elem = get_elem(map->extensions, e->id, map->dsize); elem = get_elem(map->extensions, e->id, dsize);
if (test_and_set_bit(e->id, map->members)) { if (test_and_set_bit(e->id, map->members)) {
if (elem->filled == MAC_FILLED) { if (elem->filled == MAC_FILLED) {
if (e->ether && (flags & IPSET_FLAG_EXIST)) if (e->ether && (flags & IPSET_FLAG_EXIST))
@ -178,22 +176,12 @@ bitmap_ipmac_do_del(const struct bitmap_ipmac_adt_elem *e,
return !test_and_clear_bit(e->id, map->members); return !test_and_clear_bit(e->id, map->members);
} }
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 elem->filled == MAC_FILLED ? ip_set_timeout_get(timeout) :
*timeout;
}
static inline int static inline int
bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map, bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map,
u32 id) u32 id, size_t dsize)
{ {
const struct bitmap_ipmac_elem *elem = const struct bitmap_ipmac_elem *elem =
get_elem(map->extensions, id, map->dsize); get_elem(map->extensions, id, dsize);
return nla_put_ipaddr4(skb, IPSET_ATTR_IP, return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id)) || htonl(map->first_ip + id)) ||
@ -216,7 +204,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
struct bitmap_ipmac *map = set->data; struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct bitmap_ipmac_adt_elem e = {}; struct bitmap_ipmac_adt_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
u32 ip; u32 ip;
/* MAC can be src only */ /* MAC can be src only */
@ -245,8 +233,8 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
const struct bitmap_ipmac *map = set->data; const struct bitmap_ipmac *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct bitmap_ipmac_adt_elem e = {}; struct bitmap_ipmac_adt_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_UEXT(map); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip; u32 ip = 0;
int ret = 0; int ret = 0;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
@ -285,43 +273,12 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
return x->first_ip == y->first_ip && return x->first_ip == y->first_ip &&
x->last_ip == y->last_ip && x->last_ip == y->last_ip &&
x->timeout == y->timeout && a->timeout == b->timeout &&
a->extensions == b->extensions; a->extensions == b->extensions;
} }
/* Plain variant */ /* Plain variant */
/* Timeout variant */
struct bitmap_ipmact_elem {
struct {
unsigned char ether[ETH_ALEN];
unsigned char filled;
} __attribute__ ((aligned));
unsigned long timeout;
};
/* Plain variant with counter */
struct bitmap_ipmacc_elem {
struct {
unsigned char ether[ETH_ALEN];
unsigned char filled;
} __attribute__ ((aligned));
struct ip_set_counter counter;
};
/* Timeout variant with counter */
struct bitmap_ipmacct_elem {
struct {
unsigned char ether[ETH_ALEN];
unsigned char filled;
} __attribute__ ((aligned));
unsigned long timeout;
struct ip_set_counter counter;
};
#include "ip_set_bitmap_gen.h" #include "ip_set_bitmap_gen.h"
/* Create bitmap:ip,mac type of sets */ /* Create bitmap:ip,mac type of sets */
@ -330,11 +287,11 @@ static bool
init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
u32 first_ip, u32 last_ip, u32 elements) u32 first_ip, u32 last_ip, u32 elements)
{ {
map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize); map->members = ip_set_alloc(map->memsize);
if (!map->members) if (!map->members)
return false; return false;
if (map->dsize) { if (set->dsize) {
map->extensions = ip_set_alloc(map->dsize * elements); map->extensions = ip_set_alloc(set->dsize * elements);
if (!map->extensions) { if (!map->extensions) {
kfree(map->members); kfree(map->members);
return false; return false;
@ -343,7 +300,7 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
map->first_ip = first_ip; map->first_ip = first_ip;
map->last_ip = last_ip; map->last_ip = last_ip;
map->elements = elements; map->elements = elements;
map->timeout = IPSET_NO_TIMEOUT; set->timeout = IPSET_NO_TIMEOUT;
set->data = map; set->data = map;
set->family = NFPROTO_IPV4; set->family = NFPROTO_IPV4;
@ -352,10 +309,10 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
} }
static int static int
bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[], bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
u32 flags) u32 flags)
{ {
u32 first_ip, last_ip, cadt_flags = 0; u32 first_ip = 0, last_ip = 0;
u64 elements; u64 elements;
struct bitmap_ipmac *map; struct bitmap_ipmac *map;
int ret; int ret;
@ -399,57 +356,15 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
map->memsize = bitmap_bytes(0, elements - 1); map->memsize = bitmap_bytes(0, elements - 1);
set->variant = &bitmap_ipmac; set->variant = &bitmap_ipmac;
if (tb[IPSET_ATTR_CADT_FLAGS]) set->dsize = ip_set_elem_len(set, tb,
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); sizeof(struct bitmap_ipmac_elem));
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
set->extensions |= IPSET_EXT_COUNTER; kfree(map);
if (tb[IPSET_ATTR_TIMEOUT]) { return -ENOMEM;
map->dsize = sizeof(struct bitmap_ipmacct_elem); }
map->offset[IPSET_OFFSET_TIMEOUT] = if (tb[IPSET_ATTR_TIMEOUT]) {
offsetof(struct bitmap_ipmacct_elem, timeout); set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
map->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct bitmap_ipmacct_elem, counter);
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->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ipmac_gc_init(set, bitmap_ipmac_gc); bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
} else {
map->dsize = sizeof(struct bitmap_ipmac_elem);
if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
kfree(map);
return -ENOMEM;
}
set->variant = &bitmap_ipmac;
} }
return 0; return 0;
} }
@ -460,8 +375,8 @@ static struct ip_set_type bitmap_ipmac_type = {
.features = IPSET_TYPE_IP | IPSET_TYPE_MAC, .features = IPSET_TYPE_IP | IPSET_TYPE_MAC,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = bitmap_ipmac_create, .create = bitmap_ipmac_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_IP] = { .type = NLA_NESTED },
@ -478,6 +393,7 @@ static struct ip_set_type bitmap_ipmac_type = {
[IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -20,12 +20,13 @@
#include <linux/netfilter/ipset/ip_set_bitmap.h> #include <linux/netfilter/ipset/ip_set_bitmap.h>
#include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_getport.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
#define REVISION_MAX 1 /* Counter support added */ /* 1 Counter support added */
#define IPSET_TYPE_REV_MAX 2 /* Comment support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("bitmap:port", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("bitmap:port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_bitmap:port"); MODULE_ALIAS("ip_set_bitmap:port");
#define MTYPE bitmap_port #define MTYPE bitmap_port
@ -38,9 +39,6 @@ struct bitmap_port {
u16 last_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 */ u32 elements; /* number of max elements in the set */
size_t memsize; /* members size */ 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 */ struct timer_list gc; /* garbage collection */
}; };
@ -59,20 +57,20 @@ port_to_id(const struct bitmap_port *m, u16 port)
static inline int static inline int
bitmap_port_do_test(const struct bitmap_port_adt_elem *e, bitmap_port_do_test(const struct bitmap_port_adt_elem *e,
const struct bitmap_port *map) const struct bitmap_port *map, size_t dsize)
{ {
return !!test_bit(e->id, map->members); return !!test_bit(e->id, map->members);
} }
static inline int static inline int
bitmap_port_gc_test(u16 id, const struct bitmap_port *map) bitmap_port_gc_test(u16 id, const struct bitmap_port *map, size_t dsize)
{ {
return !!test_bit(id, map->members); return !!test_bit(id, map->members);
} }
static inline int static inline int
bitmap_port_do_add(const struct bitmap_port_adt_elem *e, bitmap_port_do_add(const struct bitmap_port_adt_elem *e,
struct bitmap_port *map, u32 flags) struct bitmap_port *map, u32 flags, size_t dsize)
{ {
return !!test_and_set_bit(e->id, map->members); return !!test_and_set_bit(e->id, map->members);
} }
@ -85,7 +83,8 @@ bitmap_port_do_del(const struct bitmap_port_adt_elem *e,
} }
static inline int static inline int
bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id) bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id,
size_t dsize)
{ {
return nla_put_net16(skb, IPSET_ATTR_PORT, return nla_put_net16(skb, IPSET_ATTR_PORT,
htons(map->first_port + id)); htons(map->first_port + id));
@ -106,7 +105,7 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
struct bitmap_port *map = set->data; struct bitmap_port *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct bitmap_port_adt_elem e = {}; struct bitmap_port_adt_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
__be16 __port; __be16 __port;
u16 port = 0; u16 port = 0;
@ -131,7 +130,7 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
struct bitmap_port *map = set->data; struct bitmap_port *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct bitmap_port_adt_elem e = {}; struct bitmap_port_adt_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_UEXT(map); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 port; /* wraparound */ u32 port; /* wraparound */
u16 port_to; u16 port_to;
int ret = 0; int ret = 0;
@ -191,7 +190,7 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
return x->first_port == y->first_port && return x->first_port == y->first_port &&
x->last_port == y->last_port && x->last_port == y->last_port &&
x->timeout == y->timeout && a->timeout == b->timeout &&
a->extensions == b->extensions; a->extensions == b->extensions;
} }
@ -200,25 +199,6 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
struct bitmap_port_elem { struct bitmap_port_elem {
}; };
/* Timeout variant */
struct bitmap_portt_elem {
unsigned long timeout;
};
/* Plain variant with counter */
struct bitmap_portc_elem {
struct ip_set_counter counter;
};
/* Timeout variant with counter */
struct bitmap_portct_elem {
unsigned long timeout;
struct ip_set_counter counter;
};
#include "ip_set_bitmap_gen.h" #include "ip_set_bitmap_gen.h"
/* Create bitmap:ip type of sets */ /* Create bitmap:ip type of sets */
@ -230,8 +210,8 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
map->members = ip_set_alloc(map->memsize); map->members = ip_set_alloc(map->memsize);
if (!map->members) if (!map->members)
return false; return false;
if (map->dsize) { if (set->dsize) {
map->extensions = ip_set_alloc(map->dsize * map->elements); map->extensions = ip_set_alloc(set->dsize * map->elements);
if (!map->extensions) { if (!map->extensions) {
kfree(map->members); kfree(map->members);
return false; return false;
@ -239,7 +219,7 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
} }
map->first_port = first_port; map->first_port = first_port;
map->last_port = last_port; map->last_port = last_port;
map->timeout = IPSET_NO_TIMEOUT; set->timeout = IPSET_NO_TIMEOUT;
set->data = map; set->data = map;
set->family = NFPROTO_UNSPEC; set->family = NFPROTO_UNSPEC;
@ -248,11 +228,11 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
} }
static int static int
bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags) bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
u32 flags)
{ {
struct bitmap_port *map; struct bitmap_port *map;
u16 first_port, last_port; u16 first_port, last_port;
u32 cadt_flags = 0;
if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) ||
@ -276,53 +256,14 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
map->elements = last_port - first_port + 1; map->elements = last_port - first_port + 1;
map->memsize = map->elements * sizeof(unsigned long); map->memsize = map->elements * sizeof(unsigned long);
set->variant = &bitmap_port; set->variant = &bitmap_port;
if (tb[IPSET_ATTR_CADT_FLAGS]) set->dsize = ip_set_elem_len(set, tb, 0);
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); if (!init_map_port(set, map, first_port, last_port)) {
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { kfree(map);
set->extensions |= IPSET_EXT_COUNTER; return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) { }
map->dsize = sizeof(struct bitmap_portct_elem); if (tb[IPSET_ATTR_TIMEOUT]) {
map->offset[IPSET_OFFSET_TIMEOUT] = set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_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->extensions |= IPSET_EXT_TIMEOUT;
bitmap_port_gc_init(set, bitmap_port_gc); bitmap_port_gc_init(set, bitmap_port_gc);
} else {
map->dsize = 0;
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
} }
return 0; return 0;
} }
@ -333,8 +274,8 @@ static struct ip_set_type bitmap_port_type = {
.features = IPSET_TYPE_PORT, .features = IPSET_TYPE_PORT,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = bitmap_port_create, .create = bitmap_port_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_PORT] = { .type = NLA_U16 },
@ -349,6 +290,7 @@ static struct ip_set_type bitmap_port_type = {
[IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -17,6 +17,8 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
@ -27,8 +29,17 @@ static LIST_HEAD(ip_set_type_list); /* all registered set types */
static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */
static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */ static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */
static struct ip_set * __rcu *ip_set_list; /* all individual sets */ struct ip_set_net {
static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ struct ip_set * __rcu *ip_set_list; /* all individual sets */
ip_set_id_t ip_set_max; /* max number of sets */
int is_deleted; /* deleted by ip_set_net_exit */
};
static int ip_set_net_id __read_mostly;
static inline struct ip_set_net *ip_set_pernet(struct net *net)
{
return net_generic(net, ip_set_net_id);
}
#define IP_SET_INC 64 #define IP_SET_INC 64
#define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0) #define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0)
@ -45,8 +56,8 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET);
/* When the nfnl mutex is held: */ /* When the nfnl mutex is held: */
#define nfnl_dereference(p) \ #define nfnl_dereference(p) \
rcu_dereference_protected(p, 1) rcu_dereference_protected(p, 1)
#define nfnl_set(id) \ #define nfnl_set(inst, id) \
nfnl_dereference(ip_set_list)[id] nfnl_dereference((inst)->ip_set_list)[id]
/* /*
* The set types are implemented in modules and registered set types * The set types are implemented in modules and registered set types
@ -315,6 +326,60 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr)
} }
EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6); EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
typedef void (*destroyer)(void *);
/* ipset data extension types, in size order */
const struct ip_set_ext_type ip_set_extensions[] = {
[IPSET_EXT_ID_COUNTER] = {
.type = IPSET_EXT_COUNTER,
.flag = IPSET_FLAG_WITH_COUNTERS,
.len = sizeof(struct ip_set_counter),
.align = __alignof__(struct ip_set_counter),
},
[IPSET_EXT_ID_TIMEOUT] = {
.type = IPSET_EXT_TIMEOUT,
.len = sizeof(unsigned long),
.align = __alignof__(unsigned long),
},
[IPSET_EXT_ID_COMMENT] = {
.type = IPSET_EXT_COMMENT | IPSET_EXT_DESTROY,
.flag = IPSET_FLAG_WITH_COMMENT,
.len = sizeof(struct ip_set_comment),
.align = __alignof__(struct ip_set_comment),
.destroy = (destroyer) ip_set_comment_free,
},
};
EXPORT_SYMBOL_GPL(ip_set_extensions);
static inline bool
add_extension(enum ip_set_ext_id id, u32 flags, struct nlattr *tb[])
{
return ip_set_extensions[id].flag ?
(flags & ip_set_extensions[id].flag) :
!!tb[IPSET_ATTR_TIMEOUT];
}
size_t
ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len)
{
enum ip_set_ext_id id;
size_t offset = 0;
u32 cadt_flags = 0;
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
for (id = 0; id < IPSET_EXT_ID_MAX; id++) {
if (!add_extension(id, cadt_flags, tb))
continue;
offset += ALIGN(len + offset, ip_set_extensions[id].align);
set->offset[id] = offset;
set->extensions |= ip_set_extensions[id].type;
offset += ip_set_extensions[id].len;
}
return len + offset;
}
EXPORT_SYMBOL_GPL(ip_set_elem_len);
int int
ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
struct ip_set_ext *ext) struct ip_set_ext *ext)
@ -334,6 +399,12 @@ ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
ext->packets = be64_to_cpu(nla_get_be64( ext->packets = be64_to_cpu(nla_get_be64(
tb[IPSET_ATTR_PACKETS])); tb[IPSET_ATTR_PACKETS]));
} }
if (tb[IPSET_ATTR_COMMENT]) {
if (!(set->extensions & IPSET_EXT_COMMENT))
return -IPSET_ERR_COMMENT;
ext->comment = ip_set_comment_uget(tb[IPSET_ATTR_COMMENT]);
}
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ip_set_get_extensions); EXPORT_SYMBOL_GPL(ip_set_get_extensions);
@ -374,13 +445,14 @@ __ip_set_put(struct ip_set *set)
*/ */
static inline struct ip_set * static inline struct ip_set *
ip_set_rcu_get(ip_set_id_t index) ip_set_rcu_get(struct net *net, ip_set_id_t index)
{ {
struct ip_set *set; struct ip_set *set;
struct ip_set_net *inst = ip_set_pernet(net);
rcu_read_lock(); rcu_read_lock();
/* ip_set_list itself needs to be protected */ /* ip_set_list itself needs to be protected */
set = rcu_dereference(ip_set_list)[index]; set = rcu_dereference(inst->ip_set_list)[index];
rcu_read_unlock(); rcu_read_unlock();
return set; return set;
@ -390,7 +462,8 @@ int
ip_set_test(ip_set_id_t index, const struct sk_buff *skb, ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par, 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); struct ip_set *set = ip_set_rcu_get(
dev_net(par->in ? par->in : par->out), index);
int ret = 0; int ret = 0;
BUG_ON(set == NULL); BUG_ON(set == NULL);
@ -428,7 +501,8 @@ int
ip_set_add(ip_set_id_t index, const struct sk_buff *skb, ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par, 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); struct ip_set *set = ip_set_rcu_get(
dev_net(par->in ? par->in : par->out), index);
int ret; int ret;
BUG_ON(set == NULL); BUG_ON(set == NULL);
@ -450,7 +524,8 @@ int
ip_set_del(ip_set_id_t index, const struct sk_buff *skb, ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par, 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); struct ip_set *set = ip_set_rcu_get(
dev_net(par->in ? par->in : par->out), index);
int ret = 0; int ret = 0;
BUG_ON(set == NULL); BUG_ON(set == NULL);
@ -474,14 +549,15 @@ EXPORT_SYMBOL_GPL(ip_set_del);
* *
*/ */
ip_set_id_t ip_set_id_t
ip_set_get_byname(const char *name, struct ip_set **set) ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
{ {
ip_set_id_t i, index = IPSET_INVALID_ID; ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s; struct ip_set *s;
struct ip_set_net *inst = ip_set_pernet(net);
rcu_read_lock(); rcu_read_lock();
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < inst->ip_set_max; i++) {
s = rcu_dereference(ip_set_list)[i]; s = rcu_dereference(inst->ip_set_list)[i];
if (s != NULL && STREQ(s->name, name)) { if (s != NULL && STREQ(s->name, name)) {
__ip_set_get(s); __ip_set_get(s);
index = i; index = i;
@ -501,17 +577,26 @@ EXPORT_SYMBOL_GPL(ip_set_get_byname);
* to be valid, after calling this function. * to be valid, after calling this function.
* *
*/ */
void
ip_set_put_byindex(ip_set_id_t index) static inline void
__ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index)
{ {
struct ip_set *set; struct ip_set *set;
rcu_read_lock(); rcu_read_lock();
set = rcu_dereference(ip_set_list)[index]; set = rcu_dereference(inst->ip_set_list)[index];
if (set != NULL) if (set != NULL)
__ip_set_put(set); __ip_set_put(set);
rcu_read_unlock(); rcu_read_unlock();
} }
void
ip_set_put_byindex(struct net *net, ip_set_id_t index)
{
struct ip_set_net *inst = ip_set_pernet(net);
__ip_set_put_byindex(inst, index);
}
EXPORT_SYMBOL_GPL(ip_set_put_byindex); EXPORT_SYMBOL_GPL(ip_set_put_byindex);
/* /*
@ -522,9 +607,9 @@ EXPORT_SYMBOL_GPL(ip_set_put_byindex);
* *
*/ */
const char * const char *
ip_set_name_byindex(ip_set_id_t index) ip_set_name_byindex(struct net *net, ip_set_id_t index)
{ {
const struct ip_set *set = ip_set_rcu_get(index); const struct ip_set *set = ip_set_rcu_get(net, index);
BUG_ON(set == NULL); BUG_ON(set == NULL);
BUG_ON(set->ref == 0); BUG_ON(set->ref == 0);
@ -546,14 +631,15 @@ EXPORT_SYMBOL_GPL(ip_set_name_byindex);
* The nfnl mutex is used in the function. * The nfnl mutex is used in the function.
*/ */
ip_set_id_t ip_set_id_t
ip_set_nfnl_get(const char *name) ip_set_nfnl_get(struct net *net, const char *name)
{ {
ip_set_id_t i, index = IPSET_INVALID_ID; ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s; struct ip_set *s;
struct ip_set_net *inst = ip_set_pernet(net);
nfnl_lock(NFNL_SUBSYS_IPSET); nfnl_lock(NFNL_SUBSYS_IPSET);
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < inst->ip_set_max; i++) {
s = nfnl_set(i); s = nfnl_set(inst, i);
if (s != NULL && STREQ(s->name, name)) { if (s != NULL && STREQ(s->name, name)) {
__ip_set_get(s); __ip_set_get(s);
index = i; index = i;
@ -573,15 +659,16 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get);
* The nfnl mutex is used in the function. * The nfnl mutex is used in the function.
*/ */
ip_set_id_t ip_set_id_t
ip_set_nfnl_get_byindex(ip_set_id_t index) ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index)
{ {
struct ip_set *set; struct ip_set *set;
struct ip_set_net *inst = ip_set_pernet(net);
if (index > ip_set_max) if (index > inst->ip_set_max)
return IPSET_INVALID_ID; return IPSET_INVALID_ID;
nfnl_lock(NFNL_SUBSYS_IPSET); nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(index); set = nfnl_set(inst, index);
if (set) if (set)
__ip_set_get(set); __ip_set_get(set);
else else
@ -600,13 +687,17 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex);
* The nfnl mutex is used in the function. * The nfnl mutex is used in the function.
*/ */
void void
ip_set_nfnl_put(ip_set_id_t index) ip_set_nfnl_put(struct net *net, ip_set_id_t index)
{ {
struct ip_set *set; struct ip_set *set;
struct ip_set_net *inst = ip_set_pernet(net);
nfnl_lock(NFNL_SUBSYS_IPSET); nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(index); if (!inst->is_deleted) { /* already deleted from ip_set_net_exit() */
if (set != NULL) set = nfnl_set(inst, index);
__ip_set_put(set); if (set != NULL)
__ip_set_put(set);
}
nfnl_unlock(NFNL_SUBSYS_IPSET); nfnl_unlock(NFNL_SUBSYS_IPSET);
} }
EXPORT_SYMBOL_GPL(ip_set_nfnl_put); EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
@ -664,14 +755,14 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = {
}; };
static struct ip_set * static struct ip_set *
find_set_and_id(const char *name, ip_set_id_t *id) find_set_and_id(struct ip_set_net *inst, const char *name, ip_set_id_t *id)
{ {
struct ip_set *set = NULL; struct ip_set *set = NULL;
ip_set_id_t i; ip_set_id_t i;
*id = IPSET_INVALID_ID; *id = IPSET_INVALID_ID;
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < inst->ip_set_max; i++) {
set = nfnl_set(i); set = nfnl_set(inst, i);
if (set != NULL && STREQ(set->name, name)) { if (set != NULL && STREQ(set->name, name)) {
*id = i; *id = i;
break; break;
@ -681,22 +772,23 @@ find_set_and_id(const char *name, ip_set_id_t *id)
} }
static inline struct ip_set * static inline struct ip_set *
find_set(const char *name) find_set(struct ip_set_net *inst, const char *name)
{ {
ip_set_id_t id; ip_set_id_t id;
return find_set_and_id(name, &id); return find_set_and_id(inst, name, &id);
} }
static int static int
find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set) find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index,
struct ip_set **set)
{ {
struct ip_set *s; struct ip_set *s;
ip_set_id_t i; ip_set_id_t i;
*index = IPSET_INVALID_ID; *index = IPSET_INVALID_ID;
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < inst->ip_set_max; i++) {
s = nfnl_set(i); s = nfnl_set(inst, i);
if (s == NULL) { if (s == NULL) {
if (*index == IPSET_INVALID_ID) if (*index == IPSET_INVALID_ID)
*index = i; *index = i;
@ -725,6 +817,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct net *net = sock_net(ctnl);
struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set, *clash = NULL; struct ip_set *set, *clash = NULL;
ip_set_id_t index = IPSET_INVALID_ID; ip_set_id_t index = IPSET_INVALID_ID;
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {}; struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {};
@ -783,7 +877,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
goto put_out; goto put_out;
} }
ret = set->type->create(set, tb, flags); ret = set->type->create(net, set, tb, flags);
if (ret != 0) if (ret != 0)
goto put_out; goto put_out;
@ -794,7 +888,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
* by the nfnl mutex. Find the first free index in ip_set_list * by the nfnl mutex. Find the first free index in ip_set_list
* and check clashing. * and check clashing.
*/ */
ret = find_free_id(set->name, &index, &clash); ret = find_free_id(inst, set->name, &index, &clash);
if (ret == -EEXIST) { if (ret == -EEXIST) {
/* If this is the same set and requested, ignore error */ /* If this is the same set and requested, ignore error */
if ((flags & IPSET_FLAG_EXIST) && if ((flags & IPSET_FLAG_EXIST) &&
@ -807,9 +901,9 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
goto cleanup; goto cleanup;
} else if (ret == -IPSET_ERR_MAX_SETS) { } else if (ret == -IPSET_ERR_MAX_SETS) {
struct ip_set **list, **tmp; struct ip_set **list, **tmp;
ip_set_id_t i = ip_set_max + IP_SET_INC; ip_set_id_t i = inst->ip_set_max + IP_SET_INC;
if (i < ip_set_max || i == IPSET_INVALID_ID) if (i < inst->ip_set_max || i == IPSET_INVALID_ID)
/* Wraparound */ /* Wraparound */
goto cleanup; goto cleanup;
@ -817,14 +911,14 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
if (!list) if (!list)
goto cleanup; goto cleanup;
/* nfnl mutex is held, both lists are valid */ /* nfnl mutex is held, both lists are valid */
tmp = nfnl_dereference(ip_set_list); tmp = nfnl_dereference(inst->ip_set_list);
memcpy(list, tmp, sizeof(struct ip_set *) * ip_set_max); memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max);
rcu_assign_pointer(ip_set_list, list); rcu_assign_pointer(inst->ip_set_list, list);
/* Make sure all current packets have passed through */ /* Make sure all current packets have passed through */
synchronize_net(); synchronize_net();
/* Use new list */ /* Use new list */
index = ip_set_max; index = inst->ip_set_max;
ip_set_max = i; inst->ip_set_max = i;
kfree(tmp); kfree(tmp);
ret = 0; ret = 0;
} else if (ret) } else if (ret)
@ -834,7 +928,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
* Finally! Add our shiny new set to the list, and be done. * Finally! Add our shiny new set to the list, and be done.
*/ */
pr_debug("create: '%s' created with index %u!\n", set->name, index); pr_debug("create: '%s' created with index %u!\n", set->name, index);
nfnl_set(index) = set; nfnl_set(inst, index) = set;
return ret; return ret;
@ -857,12 +951,12 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = {
}; };
static void static void
ip_set_destroy_set(ip_set_id_t index) ip_set_destroy_set(struct ip_set_net *inst, ip_set_id_t index)
{ {
struct ip_set *set = nfnl_set(index); struct ip_set *set = nfnl_set(inst, index);
pr_debug("set: %s\n", set->name); pr_debug("set: %s\n", set->name);
nfnl_set(index) = NULL; nfnl_set(inst, index) = NULL;
/* Must call it without holding any lock */ /* Must call it without holding any lock */
set->variant->destroy(set); set->variant->destroy(set);
@ -875,6 +969,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *s; struct ip_set *s;
ip_set_id_t i; ip_set_id_t i;
int ret = 0; int ret = 0;
@ -894,21 +989,22 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
*/ */
read_lock_bh(&ip_set_ref_lock); read_lock_bh(&ip_set_ref_lock);
if (!attr[IPSET_ATTR_SETNAME]) { if (!attr[IPSET_ATTR_SETNAME]) {
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < inst->ip_set_max; i++) {
s = nfnl_set(i); s = nfnl_set(inst, i);
if (s != NULL && s->ref) { if (s != NULL && s->ref) {
ret = -IPSET_ERR_BUSY; ret = -IPSET_ERR_BUSY;
goto out; goto out;
} }
} }
read_unlock_bh(&ip_set_ref_lock); read_unlock_bh(&ip_set_ref_lock);
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < inst->ip_set_max; i++) {
s = nfnl_set(i); s = nfnl_set(inst, i);
if (s != NULL) if (s != NULL)
ip_set_destroy_set(i); ip_set_destroy_set(inst, i);
} }
} else { } else {
s = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &i); s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
&i);
if (s == NULL) { if (s == NULL) {
ret = -ENOENT; ret = -ENOENT;
goto out; goto out;
@ -918,7 +1014,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
} }
read_unlock_bh(&ip_set_ref_lock); read_unlock_bh(&ip_set_ref_lock);
ip_set_destroy_set(i); ip_set_destroy_set(inst, i);
} }
return 0; return 0;
out: out:
@ -943,6 +1039,7 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *s; struct ip_set *s;
ip_set_id_t i; ip_set_id_t i;
@ -950,13 +1047,13 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
if (!attr[IPSET_ATTR_SETNAME]) { if (!attr[IPSET_ATTR_SETNAME]) {
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < inst->ip_set_max; i++) {
s = nfnl_set(i); s = nfnl_set(inst, i);
if (s != NULL) if (s != NULL)
ip_set_flush_set(s); ip_set_flush_set(s);
} }
} else { } else {
s = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); s = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (s == NULL) if (s == NULL)
return -ENOENT; return -ENOENT;
@ -982,6 +1079,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *set, *s; struct ip_set *set, *s;
const char *name2; const char *name2;
ip_set_id_t i; ip_set_id_t i;
@ -992,7 +1090,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_SETNAME2] == NULL)) attr[IPSET_ATTR_SETNAME2] == NULL))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL) if (set == NULL)
return -ENOENT; return -ENOENT;
@ -1003,8 +1101,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
} }
name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); name2 = nla_data(attr[IPSET_ATTR_SETNAME2]);
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < inst->ip_set_max; i++) {
s = nfnl_set(i); s = nfnl_set(inst, i);
if (s != NULL && STREQ(s->name, name2)) { if (s != NULL && STREQ(s->name, name2)) {
ret = -IPSET_ERR_EXIST_SETNAME2; ret = -IPSET_ERR_EXIST_SETNAME2;
goto out; goto out;
@ -1031,6 +1129,7 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *from, *to; struct ip_set *from, *to;
ip_set_id_t from_id, to_id; ip_set_id_t from_id, to_id;
char from_name[IPSET_MAXNAMELEN]; char from_name[IPSET_MAXNAMELEN];
@ -1040,11 +1139,13 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_SETNAME2] == NULL)) attr[IPSET_ATTR_SETNAME2] == NULL))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
from = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &from_id); from = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
&from_id);
if (from == NULL) if (from == NULL)
return -ENOENT; return -ENOENT;
to = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME2]), &to_id); to = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME2]),
&to_id);
if (to == NULL) if (to == NULL)
return -IPSET_ERR_EXIST_SETNAME2; return -IPSET_ERR_EXIST_SETNAME2;
@ -1061,8 +1162,8 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
write_lock_bh(&ip_set_ref_lock); write_lock_bh(&ip_set_ref_lock);
swap(from->ref, to->ref); swap(from->ref, to->ref);
nfnl_set(from_id) = to; nfnl_set(inst, from_id) = to;
nfnl_set(to_id) = from; nfnl_set(inst, to_id) = from;
write_unlock_bh(&ip_set_ref_lock); write_unlock_bh(&ip_set_ref_lock);
return 0; return 0;
@ -1081,9 +1182,10 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
static int static int
ip_set_dump_done(struct netlink_callback *cb) ip_set_dump_done(struct netlink_callback *cb)
{ {
struct ip_set_net *inst = (struct ip_set_net *)cb->data;
if (cb->args[2]) { if (cb->args[2]) {
pr_debug("release set %s\n", nfnl_set(cb->args[1])->name); pr_debug("release set %s\n", nfnl_set(inst, cb->args[1])->name);
ip_set_put_byindex((ip_set_id_t) cb->args[1]); __ip_set_put_byindex(inst, (ip_set_id_t) cb->args[1]);
} }
return 0; return 0;
} }
@ -1109,6 +1211,7 @@ dump_init(struct netlink_callback *cb)
struct nlattr *attr = (void *)nlh + min_len; struct nlattr *attr = (void *)nlh + min_len;
u32 dump_type; u32 dump_type;
ip_set_id_t index; ip_set_id_t index;
struct ip_set_net *inst = (struct ip_set_net *)cb->data;
/* Second pass, so parser can't fail */ /* Second pass, so parser can't fail */
nla_parse(cda, IPSET_ATTR_CMD_MAX, nla_parse(cda, IPSET_ATTR_CMD_MAX,
@ -1122,7 +1225,7 @@ dump_init(struct netlink_callback *cb)
if (cda[IPSET_ATTR_SETNAME]) { if (cda[IPSET_ATTR_SETNAME]) {
struct ip_set *set; struct ip_set *set;
set = find_set_and_id(nla_data(cda[IPSET_ATTR_SETNAME]), set = find_set_and_id(inst, nla_data(cda[IPSET_ATTR_SETNAME]),
&index); &index);
if (set == NULL) if (set == NULL)
return -ENOENT; return -ENOENT;
@ -1150,6 +1253,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
unsigned int flags = NETLINK_CB(cb->skb).portid ? NLM_F_MULTI : 0; unsigned int flags = NETLINK_CB(cb->skb).portid ? NLM_F_MULTI : 0;
u32 dump_type, dump_flags; u32 dump_type, dump_flags;
int ret = 0; int ret = 0;
struct ip_set_net *inst = (struct ip_set_net *)cb->data;
if (!cb->args[0]) { if (!cb->args[0]) {
ret = dump_init(cb); ret = dump_init(cb);
@ -1163,18 +1267,18 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
} }
} }
if (cb->args[1] >= ip_set_max) if (cb->args[1] >= inst->ip_set_max)
goto out; goto out;
dump_type = DUMP_TYPE(cb->args[0]); dump_type = DUMP_TYPE(cb->args[0]);
dump_flags = DUMP_FLAGS(cb->args[0]); dump_flags = DUMP_FLAGS(cb->args[0]);
max = dump_type == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; max = dump_type == DUMP_ONE ? cb->args[1] + 1 : inst->ip_set_max;
dump_last: dump_last:
pr_debug("args[0]: %u %u args[1]: %ld\n", pr_debug("args[0]: %u %u args[1]: %ld\n",
dump_type, dump_flags, cb->args[1]); dump_type, dump_flags, cb->args[1]);
for (; cb->args[1] < max; cb->args[1]++) { for (; cb->args[1] < max; cb->args[1]++) {
index = (ip_set_id_t) cb->args[1]; index = (ip_set_id_t) cb->args[1];
set = nfnl_set(index); set = nfnl_set(inst, index);
if (set == NULL) { if (set == NULL) {
if (dump_type == DUMP_ONE) { if (dump_type == DUMP_ONE) {
ret = -ENOENT; ret = -ENOENT;
@ -1252,8 +1356,8 @@ next_set:
release_refcount: release_refcount:
/* If there was an error or set is done, release set */ /* If there was an error or set is done, release set */
if (ret || !cb->args[2]) { if (ret || !cb->args[2]) {
pr_debug("release set %s\n", nfnl_set(index)->name); pr_debug("release set %s\n", nfnl_set(inst, index)->name);
ip_set_put_byindex(index); __ip_set_put_byindex(inst, index);
cb->args[2] = 0; cb->args[2] = 0;
} }
out: out:
@ -1271,6 +1375,8 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
if (unlikely(protocol_failed(attr))) if (unlikely(protocol_failed(attr)))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
@ -1278,6 +1384,7 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
struct netlink_dump_control c = { struct netlink_dump_control c = {
.dump = ip_set_dump_start, .dump = ip_set_dump_start,
.done = ip_set_dump_done, .done = ip_set_dump_done,
.data = (void *)inst
}; };
return netlink_dump_start(ctnl, skb, nlh, &c); return netlink_dump_start(ctnl, skb, nlh, &c);
} }
@ -1356,6 +1463,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *set; struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
const struct nlattr *nla; const struct nlattr *nla;
@ -1374,7 +1482,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_LINENO] == NULL)))) attr[IPSET_ATTR_LINENO] == NULL))))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL) if (set == NULL)
return -ENOENT; return -ENOENT;
@ -1410,6 +1518,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *set; struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
const struct nlattr *nla; const struct nlattr *nla;
@ -1428,7 +1537,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_LINENO] == NULL)))) attr[IPSET_ATTR_LINENO] == NULL))))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL) if (set == NULL)
return -ENOENT; return -ENOENT;
@ -1464,6 +1573,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *set; struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
int ret = 0; int ret = 0;
@ -1474,7 +1584,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
!flag_nested(attr[IPSET_ATTR_DATA]))) !flag_nested(attr[IPSET_ATTR_DATA])))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL) if (set == NULL)
return -ENOENT; return -ENOENT;
@ -1499,6 +1609,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const attr[]) const struct nlattr * const attr[])
{ {
struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
const struct ip_set *set; const struct ip_set *set;
struct sk_buff *skb2; struct sk_buff *skb2;
struct nlmsghdr *nlh2; struct nlmsghdr *nlh2;
@ -1508,7 +1619,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_SETNAME] == NULL)) attr[IPSET_ATTR_SETNAME] == NULL))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL) if (set == NULL)
return -ENOENT; return -ENOENT;
@ -1733,8 +1844,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
unsigned int *op; unsigned int *op;
void *data; void *data;
int copylen = *len, ret = 0; int copylen = *len, ret = 0;
struct net *net = sock_net(sk);
struct ip_set_net *inst = ip_set_pernet(net);
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM; return -EPERM;
if (optval != SO_IP_SET) if (optval != SO_IP_SET)
return -EBADF; return -EBADF;
@ -1783,22 +1896,39 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
} }
req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
nfnl_lock(NFNL_SUBSYS_IPSET); nfnl_lock(NFNL_SUBSYS_IPSET);
find_set_and_id(req_get->set.name, &id); find_set_and_id(inst, req_get->set.name, &id);
req_get->set.index = id; req_get->set.index = id;
nfnl_unlock(NFNL_SUBSYS_IPSET); nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy; goto copy;
} }
case IP_SET_OP_GET_FNAME: {
struct ip_set_req_get_set_family *req_get = data;
ip_set_id_t id;
if (*len != sizeof(struct ip_set_req_get_set_family)) {
ret = -EINVAL;
goto done;
}
req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
nfnl_lock(NFNL_SUBSYS_IPSET);
find_set_and_id(inst, req_get->set.name, &id);
req_get->set.index = id;
if (id != IPSET_INVALID_ID)
req_get->family = nfnl_set(inst, id)->family;
nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy;
}
case IP_SET_OP_GET_BYINDEX: { case IP_SET_OP_GET_BYINDEX: {
struct ip_set_req_get_set *req_get = data; struct ip_set_req_get_set *req_get = data;
struct ip_set *set; struct ip_set *set;
if (*len != sizeof(struct ip_set_req_get_set) || if (*len != sizeof(struct ip_set_req_get_set) ||
req_get->set.index >= ip_set_max) { req_get->set.index >= inst->ip_set_max) {
ret = -EINVAL; ret = -EINVAL;
goto done; goto done;
} }
nfnl_lock(NFNL_SUBSYS_IPSET); nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(req_get->set.index); set = nfnl_set(inst, req_get->set.index);
strncpy(req_get->set.name, set ? set->name : "", strncpy(req_get->set.name, set ? set->name : "",
IPSET_MAXNAMELEN); IPSET_MAXNAMELEN);
nfnl_unlock(NFNL_SUBSYS_IPSET); nfnl_unlock(NFNL_SUBSYS_IPSET);
@ -1827,49 +1957,82 @@ static struct nf_sockopt_ops so_set __read_mostly = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int __net_init
ip_set_net_init(struct net *net)
{
struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set **list;
inst->ip_set_max = max_sets ? max_sets : CONFIG_IP_SET_MAX;
if (inst->ip_set_max >= IPSET_INVALID_ID)
inst->ip_set_max = IPSET_INVALID_ID - 1;
list = kzalloc(sizeof(struct ip_set *) * inst->ip_set_max, GFP_KERNEL);
if (!list)
return -ENOMEM;
inst->is_deleted = 0;
rcu_assign_pointer(inst->ip_set_list, list);
pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL);
return 0;
}
static void __net_exit
ip_set_net_exit(struct net *net)
{
struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set = NULL;
ip_set_id_t i;
inst->is_deleted = 1; /* flag for ip_set_nfnl_put */
for (i = 0; i < inst->ip_set_max; i++) {
set = nfnl_set(inst, i);
if (set != NULL)
ip_set_destroy_set(inst, i);
}
kfree(rcu_dereference_protected(inst->ip_set_list, 1));
}
static struct pernet_operations ip_set_net_ops = {
.init = ip_set_net_init,
.exit = ip_set_net_exit,
.id = &ip_set_net_id,
.size = sizeof(struct ip_set_net)
};
static int __init static int __init
ip_set_init(void) ip_set_init(void)
{ {
struct ip_set **list; int ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
int ret;
if (max_sets)
ip_set_max = max_sets;
if (ip_set_max >= IPSET_INVALID_ID)
ip_set_max = IPSET_INVALID_ID - 1;
list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL);
if (!list)
return -ENOMEM;
rcu_assign_pointer(ip_set_list, list);
ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
if (ret != 0) { if (ret != 0) {
pr_err("ip_set: cannot register with nfnetlink.\n"); pr_err("ip_set: cannot register with nfnetlink.\n");
kfree(list);
return ret; return ret;
} }
ret = nf_register_sockopt(&so_set); ret = nf_register_sockopt(&so_set);
if (ret != 0) { if (ret != 0) {
pr_err("SO_SET registry failed: %d\n", ret); pr_err("SO_SET registry failed: %d\n", ret);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys); nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
kfree(list);
return ret; return ret;
} }
ret = register_pernet_subsys(&ip_set_net_ops);
pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL); if (ret) {
pr_err("ip_set: cannot register pernet_subsys.\n");
nf_unregister_sockopt(&so_set);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
return ret;
}
return 0; return 0;
} }
static void __exit static void __exit
ip_set_fini(void) ip_set_fini(void)
{ {
struct ip_set **list = rcu_dereference_protected(ip_set_list, 1); unregister_pernet_subsys(&ip_set_net_ops);
/* There can't be any existing set */
nf_unregister_sockopt(&so_set); nf_unregister_sockopt(&so_set);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys); nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
kfree(list);
pr_debug("these are the famous last words\n"); pr_debug("these are the famous last words\n");
} }

View file

@ -102,9 +102,25 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
int protocol = iph->protocol; int protocol = iph->protocol;
/* See comments at tcp_match in ip_tables.c */ /* See comments at tcp_match in ip_tables.c */
if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET)) if (protocol <= 0)
return false; return false;
if (ntohs(iph->frag_off) & IP_OFFSET)
switch (protocol) {
case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
case IPPROTO_ICMP:
/* Port info not available for fragment offset > 0 */
return false;
default:
/* Other protocols doesn't have ports,
so we can match fragments */
*proto = protocol;
return true;
}
return get_port(skb, protocol, protooff, src, port, proto); return get_port(skb, protocol, protooff, src, port, proto);
} }
EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);

View file

@ -15,8 +15,7 @@
#define rcu_dereference_bh(p) rcu_dereference(p) #define rcu_dereference_bh(p) rcu_dereference(p)
#endif #endif
#define CONCAT(a, b) a##b #define rcu_dereference_bh_nfnl(p) rcu_dereference_bh_check(p, 1)
#define TOKEN(a, b) CONCAT(a, b)
/* Hashing which uses arrays to resolve clashing. The hash table is resized /* Hashing which uses arrays to resolve clashing. The hash table is resized
* (doubled) when searching becomes too long. * (doubled) when searching becomes too long.
@ -78,10 +77,14 @@ struct htable {
#define hbucket(h, i) (&((h)->bucket[i])) #define hbucket(h, i) (&((h)->bucket[i]))
#ifndef IPSET_NET_COUNT
#define IPSET_NET_COUNT 1
#endif
/* Book-keeping of the prefixes added to the set */ /* Book-keeping of the prefixes added to the set */
struct net_prefixes { struct net_prefixes {
u8 cidr; /* the different cidr values in the set */ u32 nets[IPSET_NET_COUNT]; /* number of elements per cidr */
u32 nets; /* number of elements per cidr */ u8 cidr[IPSET_NET_COUNT]; /* the different cidr values in the set */
}; };
/* Compute the hash table size */ /* Compute the hash table size */
@ -114,23 +117,6 @@ htable_bits(u32 hashsize)
return bits; return bits;
} }
/* Destroy the hashtable part of the set */
static void
ahash_destroy(struct htable *t)
{
struct hbucket *n;
u32 i;
for (i = 0; i < jhash_size(t->htable_bits); i++) {
n = hbucket(t, i);
if (n->size)
/* FIXME: use slab cache */
kfree(n->value);
}
ip_set_free(t);
}
static int static int
hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
{ {
@ -156,30 +142,30 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
} }
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
#if IPSET_NET_COUNT > 1
#define __CIDR(cidr, i) (cidr[i])
#else
#define __CIDR(cidr, i) (cidr)
#endif
#ifdef IP_SET_HASH_WITH_NETS_PACKED #ifdef IP_SET_HASH_WITH_NETS_PACKED
/* When cidr is packed with nomatch, cidr - 1 is stored in the entry */ /* When cidr is packed with nomatch, cidr - 1 is stored in the entry */
#define CIDR(cidr) (cidr + 1) #define CIDR(cidr, i) (__CIDR(cidr, i) + 1)
#else #else
#define CIDR(cidr) (cidr) #define CIDR(cidr, i) (__CIDR(cidr, i))
#endif #endif
#define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128)
#ifdef IP_SET_HASH_WITH_MULTI #ifdef IP_SET_HASH_WITH_MULTI
#define NETS_LENGTH(family) (SET_HOST_MASK(family) + 1) #define NLEN(family) (SET_HOST_MASK(family) + 1)
#else #else
#define NETS_LENGTH(family) SET_HOST_MASK(family) #define NLEN(family) SET_HOST_MASK(family)
#endif #endif
#else #else
#define NETS_LENGTH(family) 0 #define NLEN(family) 0
#endif /* IP_SET_HASH_WITH_NETS */ #endif /* IP_SET_HASH_WITH_NETS */
#define ext_timeout(e, h) \
(unsigned long *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_TIMEOUT])
#define ext_counter(e, h) \
(struct ip_set_counter *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_COUNTER])
#endif /* _IP_SET_HASH_GEN_H */ #endif /* _IP_SET_HASH_GEN_H */
/* Family dependent templates */ /* Family dependent templates */
@ -194,6 +180,8 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
#undef mtype_data_next #undef mtype_data_next
#undef mtype_elem #undef mtype_elem
#undef mtype_ahash_destroy
#undef mtype_ext_cleanup
#undef mtype_add_cidr #undef mtype_add_cidr
#undef mtype_del_cidr #undef mtype_del_cidr
#undef mtype_ahash_memsize #undef mtype_ahash_memsize
@ -220,41 +208,44 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
#undef HKEY #undef HKEY
#define mtype_data_equal TOKEN(MTYPE, _data_equal) #define mtype_data_equal IPSET_TOKEN(MTYPE, _data_equal)
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
#define mtype_do_data_match TOKEN(MTYPE, _do_data_match) #define mtype_do_data_match IPSET_TOKEN(MTYPE, _do_data_match)
#else #else
#define mtype_do_data_match(d) 1 #define mtype_do_data_match(d) 1
#endif #endif
#define mtype_data_set_flags TOKEN(MTYPE, _data_set_flags) #define mtype_data_set_flags IPSET_TOKEN(MTYPE, _data_set_flags)
#define mtype_data_reset_flags TOKEN(MTYPE, _data_reset_flags) #define mtype_data_reset_elem IPSET_TOKEN(MTYPE, _data_reset_elem)
#define mtype_data_netmask TOKEN(MTYPE, _data_netmask) #define mtype_data_reset_flags IPSET_TOKEN(MTYPE, _data_reset_flags)
#define mtype_data_list TOKEN(MTYPE, _data_list) #define mtype_data_netmask IPSET_TOKEN(MTYPE, _data_netmask)
#define mtype_data_next TOKEN(MTYPE, _data_next) #define mtype_data_list IPSET_TOKEN(MTYPE, _data_list)
#define mtype_elem TOKEN(MTYPE, _elem) #define mtype_data_next IPSET_TOKEN(MTYPE, _data_next)
#define mtype_add_cidr TOKEN(MTYPE, _add_cidr) #define mtype_elem IPSET_TOKEN(MTYPE, _elem)
#define mtype_del_cidr TOKEN(MTYPE, _del_cidr) #define mtype_ahash_destroy IPSET_TOKEN(MTYPE, _ahash_destroy)
#define mtype_ahash_memsize TOKEN(MTYPE, _ahash_memsize) #define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup)
#define mtype_flush TOKEN(MTYPE, _flush) #define mtype_add_cidr IPSET_TOKEN(MTYPE, _add_cidr)
#define mtype_destroy TOKEN(MTYPE, _destroy) #define mtype_del_cidr IPSET_TOKEN(MTYPE, _del_cidr)
#define mtype_gc_init TOKEN(MTYPE, _gc_init) #define mtype_ahash_memsize IPSET_TOKEN(MTYPE, _ahash_memsize)
#define mtype_same_set TOKEN(MTYPE, _same_set) #define mtype_flush IPSET_TOKEN(MTYPE, _flush)
#define mtype_kadt TOKEN(MTYPE, _kadt) #define mtype_destroy IPSET_TOKEN(MTYPE, _destroy)
#define mtype_uadt TOKEN(MTYPE, _uadt) #define mtype_gc_init IPSET_TOKEN(MTYPE, _gc_init)
#define mtype_same_set IPSET_TOKEN(MTYPE, _same_set)
#define mtype_kadt IPSET_TOKEN(MTYPE, _kadt)
#define mtype_uadt IPSET_TOKEN(MTYPE, _uadt)
#define mtype MTYPE #define mtype MTYPE
#define mtype_elem TOKEN(MTYPE, _elem) #define mtype_elem IPSET_TOKEN(MTYPE, _elem)
#define mtype_add TOKEN(MTYPE, _add) #define mtype_add IPSET_TOKEN(MTYPE, _add)
#define mtype_del TOKEN(MTYPE, _del) #define mtype_del IPSET_TOKEN(MTYPE, _del)
#define mtype_test_cidrs TOKEN(MTYPE, _test_cidrs) #define mtype_test_cidrs IPSET_TOKEN(MTYPE, _test_cidrs)
#define mtype_test TOKEN(MTYPE, _test) #define mtype_test IPSET_TOKEN(MTYPE, _test)
#define mtype_expire TOKEN(MTYPE, _expire) #define mtype_expire IPSET_TOKEN(MTYPE, _expire)
#define mtype_resize TOKEN(MTYPE, _resize) #define mtype_resize IPSET_TOKEN(MTYPE, _resize)
#define mtype_head TOKEN(MTYPE, _head) #define mtype_head IPSET_TOKEN(MTYPE, _head)
#define mtype_list TOKEN(MTYPE, _list) #define mtype_list IPSET_TOKEN(MTYPE, _list)
#define mtype_gc TOKEN(MTYPE, _gc) #define mtype_gc IPSET_TOKEN(MTYPE, _gc)
#define mtype_variant TOKEN(MTYPE, _variant) #define mtype_variant IPSET_TOKEN(MTYPE, _variant)
#define mtype_data_match TOKEN(MTYPE, _data_match) #define mtype_data_match IPSET_TOKEN(MTYPE, _data_match)
#ifndef HKEY_DATALEN #ifndef HKEY_DATALEN
#define HKEY_DATALEN sizeof(struct mtype_elem) #define HKEY_DATALEN sizeof(struct mtype_elem)
@ -269,13 +260,10 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
/* The generic hash structure */ /* The generic hash structure */
struct htype { struct htype {
struct htable *table; /* the hash table */ struct htable __rcu *table; /* the hash table */
u32 maxelem; /* max elements in the hash */ u32 maxelem; /* max elements in the hash */
u32 elements; /* current element (vs timeout) */ u32 elements; /* current element (vs timeout) */
u32 initval; /* random jhash init value */ u32 initval; /* random jhash init value */
u32 timeout; /* timeout value, if enabled */
size_t dsize; /* data struct size */
size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
struct timer_list gc; /* garbage collection when timeout enabled */ struct timer_list gc; /* garbage collection when timeout enabled */
struct mtype_elem next; /* temporary storage for uadd */ struct mtype_elem next; /* temporary storage for uadd */
#ifdef IP_SET_HASH_WITH_MULTI #ifdef IP_SET_HASH_WITH_MULTI
@ -297,49 +285,49 @@ struct htype {
/* Network cidr size book keeping when the hash stores different /* Network cidr size book keeping when the hash stores different
* sized networks */ * sized networks */
static void static void
mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length) mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
{ {
int i, j; int i, j;
/* Add in increasing prefix order, so larger cidr first */ /* Add in increasing prefix order, so larger cidr first */
for (i = 0, j = -1; i < nets_length && h->nets[i].nets; i++) { for (i = 0, j = -1; i < nets_length && h->nets[i].nets[n]; i++) {
if (j != -1) if (j != -1)
continue; continue;
else if (h->nets[i].cidr < cidr) else if (h->nets[i].cidr[n] < cidr)
j = i; j = i;
else if (h->nets[i].cidr == cidr) { else if (h->nets[i].cidr[n] == cidr) {
h->nets[i].nets++; h->nets[i].nets[n]++;
return; return;
} }
} }
if (j != -1) { if (j != -1) {
for (; i > j; i--) { for (; i > j; i--) {
h->nets[i].cidr = h->nets[i - 1].cidr; h->nets[i].cidr[n] = h->nets[i - 1].cidr[n];
h->nets[i].nets = h->nets[i - 1].nets; h->nets[i].nets[n] = h->nets[i - 1].nets[n];
} }
} }
h->nets[i].cidr = cidr; h->nets[i].cidr[n] = cidr;
h->nets[i].nets = 1; h->nets[i].nets[n] = 1;
} }
static void static void
mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length) mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
{ {
u8 i, j, net_end = nets_length - 1; u8 i, j, net_end = nets_length - 1;
for (i = 0; i < nets_length; i++) { for (i = 0; i < nets_length; i++) {
if (h->nets[i].cidr != cidr) if (h->nets[i].cidr[n] != cidr)
continue; continue;
if (h->nets[i].nets > 1 || i == net_end || if (h->nets[i].nets[n] > 1 || i == net_end ||
h->nets[i + 1].nets == 0) { h->nets[i + 1].nets[n] == 0) {
h->nets[i].nets--; h->nets[i].nets[n]--;
return; return;
} }
for (j = i; j < net_end && h->nets[j].nets; j++) { for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
h->nets[j].cidr = h->nets[j + 1].cidr; h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
h->nets[j].nets = h->nets[j + 1].nets; h->nets[j].nets[n] = h->nets[j + 1].nets[n];
} }
h->nets[j].nets = 0; h->nets[j].nets[n] = 0;
return; return;
} }
} }
@ -347,10 +335,10 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length)
/* Calculate the actual memory size of the set data */ /* Calculate the actual memory size of the set data */
static size_t static size_t
mtype_ahash_memsize(const struct htype *h, u8 nets_length) mtype_ahash_memsize(const struct htype *h, const struct htable *t,
u8 nets_length, size_t dsize)
{ {
u32 i; u32 i;
struct htable *t = h->table;
size_t memsize = sizeof(*h) size_t memsize = sizeof(*h)
+ sizeof(*t) + sizeof(*t)
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
@ -359,35 +347,70 @@ mtype_ahash_memsize(const struct htype *h, u8 nets_length)
+ jhash_size(t->htable_bits) * sizeof(struct hbucket); + jhash_size(t->htable_bits) * sizeof(struct hbucket);
for (i = 0; i < jhash_size(t->htable_bits); i++) for (i = 0; i < jhash_size(t->htable_bits); i++)
memsize += t->bucket[i].size * h->dsize; memsize += t->bucket[i].size * dsize;
return memsize; return memsize;
} }
/* Get the ith element from the array block n */
#define ahash_data(n, i, dsize) \
((struct mtype_elem *)((n)->value + ((i) * (dsize))))
static void
mtype_ext_cleanup(struct ip_set *set, struct hbucket *n)
{
int i;
for (i = 0; i < n->pos; i++)
ip_set_ext_destroy(set, ahash_data(n, i, set->dsize));
}
/* Flush a hash type of set: destroy all elements */ /* Flush a hash type of set: destroy all elements */
static void static void
mtype_flush(struct ip_set *set) mtype_flush(struct ip_set *set)
{ {
struct htype *h = set->data; struct htype *h = set->data;
struct htable *t = h->table; struct htable *t;
struct hbucket *n; struct hbucket *n;
u32 i; u32 i;
t = rcu_dereference_bh_nfnl(h->table);
for (i = 0; i < jhash_size(t->htable_bits); i++) { for (i = 0; i < jhash_size(t->htable_bits); i++) {
n = hbucket(t, i); n = hbucket(t, i);
if (n->size) { if (n->size) {
if (set->extensions & IPSET_EXT_DESTROY)
mtype_ext_cleanup(set, n);
n->size = n->pos = 0; n->size = n->pos = 0;
/* FIXME: use slab cache */ /* FIXME: use slab cache */
kfree(n->value); kfree(n->value);
} }
} }
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
memset(h->nets, 0, sizeof(struct net_prefixes) memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family));
* NETS_LENGTH(set->family));
#endif #endif
h->elements = 0; h->elements = 0;
} }
/* Destroy the hashtable part of the set */
static void
mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy)
{
struct hbucket *n;
u32 i;
for (i = 0; i < jhash_size(t->htable_bits); i++) {
n = hbucket(t, i);
if (n->size) {
if (set->extensions & IPSET_EXT_DESTROY && ext_destroy)
mtype_ext_cleanup(set, n);
/* FIXME: use slab cache */
kfree(n->value);
}
}
ip_set_free(t);
}
/* Destroy a hash type of set */ /* Destroy a hash type of set */
static void static void
mtype_destroy(struct ip_set *set) mtype_destroy(struct ip_set *set)
@ -397,7 +420,7 @@ mtype_destroy(struct ip_set *set)
if (set->extensions & IPSET_EXT_TIMEOUT) if (set->extensions & IPSET_EXT_TIMEOUT)
del_timer_sync(&h->gc); del_timer_sync(&h->gc);
ahash_destroy(h->table); mtype_ahash_destroy(set, rcu_dereference_bh_nfnl(h->table), true);
#ifdef IP_SET_HASH_WITH_RBTREE #ifdef IP_SET_HASH_WITH_RBTREE
rbtree_destroy(&h->rbtree); rbtree_destroy(&h->rbtree);
#endif #endif
@ -414,10 +437,10 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
init_timer(&h->gc); init_timer(&h->gc);
h->gc.data = (unsigned long) set; h->gc.data = (unsigned long) set;
h->gc.function = gc; h->gc.function = gc;
h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
add_timer(&h->gc); add_timer(&h->gc);
pr_debug("gc initialized, run in every %u\n", pr_debug("gc initialized, run in every %u\n",
IPSET_GC_PERIOD(h->timeout)); IPSET_GC_PERIOD(set->timeout));
} }
static bool static bool
@ -428,37 +451,40 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b)
/* Resizing changes htable_bits, so we ignore it */ /* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem && return x->maxelem == y->maxelem &&
x->timeout == y->timeout && a->timeout == b->timeout &&
#ifdef IP_SET_HASH_WITH_NETMASK #ifdef IP_SET_HASH_WITH_NETMASK
x->netmask == y->netmask && x->netmask == y->netmask &&
#endif #endif
a->extensions == b->extensions; a->extensions == b->extensions;
} }
/* Get the ith element from the array block n */
#define ahash_data(n, i, dsize) \
((struct mtype_elem *)((n)->value + ((i) * (dsize))))
/* Delete expired elements from the hashtable */ /* Delete expired elements from the hashtable */
static void static void
mtype_expire(struct htype *h, u8 nets_length, size_t dsize) mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
{ {
struct htable *t = h->table; struct htable *t;
struct hbucket *n; struct hbucket *n;
struct mtype_elem *data; struct mtype_elem *data;
u32 i; u32 i;
int j; int j;
#ifdef IP_SET_HASH_WITH_NETS
u8 k;
#endif
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
for (i = 0; i < jhash_size(t->htable_bits); i++) { for (i = 0; i < jhash_size(t->htable_bits); i++) {
n = hbucket(t, i); n = hbucket(t, i);
for (j = 0; j < n->pos; j++) { for (j = 0; j < n->pos; j++) {
data = ahash_data(n, j, dsize); data = ahash_data(n, j, dsize);
if (ip_set_timeout_expired(ext_timeout(data, h))) { if (ip_set_timeout_expired(ext_timeout(data, set))) {
pr_debug("expired %u/%u\n", i, j); pr_debug("expired %u/%u\n", i, j);
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
mtype_del_cidr(h, CIDR(data->cidr), for (k = 0; k < IPSET_NET_COUNT; k++)
nets_length); mtype_del_cidr(h, CIDR(data->cidr, k),
nets_length, k);
#endif #endif
ip_set_ext_destroy(set, data);
if (j != n->pos - 1) if (j != n->pos - 1)
/* Not last one */ /* Not last one */
memcpy(data, memcpy(data,
@ -481,6 +507,7 @@ mtype_expire(struct htype *h, u8 nets_length, size_t dsize)
n->value = tmp; n->value = tmp;
} }
} }
rcu_read_unlock_bh();
} }
static void static void
@ -491,10 +518,10 @@ mtype_gc(unsigned long ul_set)
pr_debug("called\n"); pr_debug("called\n");
write_lock_bh(&set->lock); write_lock_bh(&set->lock);
mtype_expire(h, NETS_LENGTH(set->family), h->dsize); mtype_expire(set, h, NLEN(set->family), set->dsize);
write_unlock_bh(&set->lock); write_unlock_bh(&set->lock);
h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
add_timer(&h->gc); add_timer(&h->gc);
} }
@ -505,7 +532,7 @@ static int
mtype_resize(struct ip_set *set, bool retried) mtype_resize(struct ip_set *set, bool retried)
{ {
struct htype *h = set->data; struct htype *h = set->data;
struct htable *t, *orig = h->table; struct htable *t, *orig = rcu_dereference_bh_nfnl(h->table);
u8 htable_bits = orig->htable_bits; u8 htable_bits = orig->htable_bits;
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
u8 flags; u8 flags;
@ -520,8 +547,7 @@ mtype_resize(struct ip_set *set, bool retried)
if (SET_WITH_TIMEOUT(set) && !retried) { if (SET_WITH_TIMEOUT(set) && !retried) {
i = h->elements; i = h->elements;
write_lock_bh(&set->lock); write_lock_bh(&set->lock);
mtype_expire(set->data, NETS_LENGTH(set->family), mtype_expire(set, set->data, NLEN(set->family), set->dsize);
h->dsize);
write_unlock_bh(&set->lock); write_unlock_bh(&set->lock);
if (h->elements < i) if (h->elements < i)
return 0; return 0;
@ -548,25 +574,25 @@ retry:
for (i = 0; i < jhash_size(orig->htable_bits); i++) { for (i = 0; i < jhash_size(orig->htable_bits); i++) {
n = hbucket(orig, i); n = hbucket(orig, i);
for (j = 0; j < n->pos; j++) { for (j = 0; j < n->pos; j++) {
data = ahash_data(n, j, h->dsize); data = ahash_data(n, j, set->dsize);
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
flags = 0; flags = 0;
mtype_data_reset_flags(data, &flags); mtype_data_reset_flags(data, &flags);
#endif #endif
m = hbucket(t, HKEY(data, h->initval, htable_bits)); m = hbucket(t, HKEY(data, h->initval, htable_bits));
ret = hbucket_elem_add(m, AHASH_MAX(h), h->dsize); ret = hbucket_elem_add(m, AHASH_MAX(h), set->dsize);
if (ret < 0) { if (ret < 0) {
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
mtype_data_reset_flags(data, &flags); mtype_data_reset_flags(data, &flags);
#endif #endif
read_unlock_bh(&set->lock); read_unlock_bh(&set->lock);
ahash_destroy(t); mtype_ahash_destroy(set, t, false);
if (ret == -EAGAIN) if (ret == -EAGAIN)
goto retry; goto retry;
return ret; return ret;
} }
d = ahash_data(m, m->pos++, h->dsize); d = ahash_data(m, m->pos++, set->dsize);
memcpy(d, data, h->dsize); memcpy(d, data, set->dsize);
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
mtype_data_reset_flags(d, &flags); mtype_data_reset_flags(d, &flags);
#endif #endif
@ -581,7 +607,7 @@ retry:
pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name, pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name,
orig->htable_bits, orig, t->htable_bits, t); orig->htable_bits, orig, t->htable_bits, t);
ahash_destroy(orig); mtype_ahash_destroy(set, orig, false);
return 0; return 0;
} }
@ -604,7 +630,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem) if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
/* FIXME: when set is full, we slow down here */ /* FIXME: when set is full, we slow down here */
mtype_expire(h, NETS_LENGTH(set->family), h->dsize); mtype_expire(set, h, NLEN(set->family), set->dsize);
if (h->elements >= h->maxelem) { if (h->elements >= h->maxelem) {
if (net_ratelimit()) if (net_ratelimit())
@ -618,11 +644,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
key = HKEY(value, h->initval, t->htable_bits); key = HKEY(value, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i, h->dsize); data = ahash_data(n, i, set->dsize);
if (mtype_data_equal(data, d, &multi)) { if (mtype_data_equal(data, d, &multi)) {
if (flag_exist || if (flag_exist ||
(SET_WITH_TIMEOUT(set) && (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(data, h)))) { ip_set_timeout_expired(ext_timeout(data, set)))) {
/* Just the extensions could be overwritten */ /* Just the extensions could be overwritten */
j = i; j = i;
goto reuse_slot; goto reuse_slot;
@ -633,30 +659,37 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
} }
/* Reuse first timed out entry */ /* Reuse first timed out entry */
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(data, h)) && ip_set_timeout_expired(ext_timeout(data, set)) &&
j != AHASH_MAX(h) + 1) j != AHASH_MAX(h) + 1)
j = i; j = i;
} }
reuse_slot: reuse_slot:
if (j != AHASH_MAX(h) + 1) { if (j != AHASH_MAX(h) + 1) {
/* Fill out reused slot */ /* Fill out reused slot */
data = ahash_data(n, j, h->dsize); data = ahash_data(n, j, set->dsize);
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
mtype_del_cidr(h, CIDR(data->cidr), NETS_LENGTH(set->family)); for (i = 0; i < IPSET_NET_COUNT; i++) {
mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); mtype_del_cidr(h, CIDR(data->cidr, i),
NLEN(set->family), i);
mtype_add_cidr(h, CIDR(d->cidr, i),
NLEN(set->family), i);
}
#endif #endif
ip_set_ext_destroy(set, data);
} else { } else {
/* Use/create a new slot */ /* Use/create a new slot */
TUNE_AHASH_MAX(h, multi); TUNE_AHASH_MAX(h, multi);
ret = hbucket_elem_add(n, AHASH_MAX(h), h->dsize); ret = hbucket_elem_add(n, AHASH_MAX(h), set->dsize);
if (ret != 0) { if (ret != 0) {
if (ret == -EAGAIN) if (ret == -EAGAIN)
mtype_data_next(&h->next, d); mtype_data_next(&h->next, d);
goto out; goto out;
} }
data = ahash_data(n, n->pos++, h->dsize); data = ahash_data(n, n->pos++, set->dsize);
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); for (i = 0; i < IPSET_NET_COUNT; i++)
mtype_add_cidr(h, CIDR(d->cidr, i), NLEN(set->family),
i);
#endif #endif
h->elements++; h->elements++;
} }
@ -665,9 +698,11 @@ reuse_slot:
mtype_data_set_flags(data, flags); mtype_data_set_flags(data, flags);
#endif #endif
if (SET_WITH_TIMEOUT(set)) if (SET_WITH_TIMEOUT(set))
ip_set_timeout_set(ext_timeout(data, h), ext->timeout); ip_set_timeout_set(ext_timeout(data, set), ext->timeout);
if (SET_WITH_COUNTER(set)) if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(data, h), ext); ip_set_init_counter(ext_counter(data, set), ext);
if (SET_WITH_COMMENT(set))
ip_set_init_comment(ext_comment(data, set), ext);
out: out:
rcu_read_unlock_bh(); rcu_read_unlock_bh();
@ -682,47 +717,60 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags) struct ip_set_ext *mext, u32 flags)
{ {
struct htype *h = set->data; struct htype *h = set->data;
struct htable *t = h->table; struct htable *t;
const struct mtype_elem *d = value; const struct mtype_elem *d = value;
struct mtype_elem *data; struct mtype_elem *data;
struct hbucket *n; struct hbucket *n;
int i; int i, ret = -IPSET_ERR_EXIST;
#ifdef IP_SET_HASH_WITH_NETS
u8 j;
#endif
u32 key, multi = 0; u32 key, multi = 0;
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
key = HKEY(value, h->initval, t->htable_bits); key = HKEY(value, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i, h->dsize); data = ahash_data(n, i, set->dsize);
if (!mtype_data_equal(data, d, &multi)) if (!mtype_data_equal(data, d, &multi))
continue; continue;
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(data, h))) ip_set_timeout_expired(ext_timeout(data, set)))
return -IPSET_ERR_EXIST; goto out;
if (i != n->pos - 1) if (i != n->pos - 1)
/* Not last one */ /* Not last one */
memcpy(data, ahash_data(n, n->pos - 1, h->dsize), memcpy(data, ahash_data(n, n->pos - 1, set->dsize),
h->dsize); set->dsize);
n->pos--; n->pos--;
h->elements--; h->elements--;
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
mtype_del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); for (j = 0; j < IPSET_NET_COUNT; j++)
mtype_del_cidr(h, CIDR(d->cidr, j), NLEN(set->family),
j);
#endif #endif
ip_set_ext_destroy(set, data);
if (n->pos + AHASH_INIT_SIZE < n->size) { if (n->pos + AHASH_INIT_SIZE < n->size) {
void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
* h->dsize, * set->dsize,
GFP_ATOMIC); GFP_ATOMIC);
if (!tmp) if (!tmp) {
return 0; ret = 0;
goto out;
}
n->size -= AHASH_INIT_SIZE; n->size -= AHASH_INIT_SIZE;
memcpy(tmp, n->value, n->size * h->dsize); memcpy(tmp, n->value, n->size * set->dsize);
kfree(n->value); kfree(n->value);
n->value = tmp; n->value = tmp;
} }
return 0; ret = 0;
goto out;
} }
return -IPSET_ERR_EXIST; out:
rcu_read_unlock_bh();
return ret;
} }
static inline int static inline int
@ -730,8 +778,7 @@ mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext,
struct ip_set_ext *mext, struct ip_set *set, u32 flags) struct ip_set_ext *mext, struct ip_set *set, u32 flags)
{ {
if (SET_WITH_COUNTER(set)) if (SET_WITH_COUNTER(set))
ip_set_update_counter(ext_counter(data, ip_set_update_counter(ext_counter(data, set),
(struct htype *)(set->data)),
ext, mext, flags); ext, mext, flags);
return mtype_do_data_match(data); return mtype_do_data_match(data);
} }
@ -745,25 +792,38 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
struct ip_set_ext *mext, u32 flags) struct ip_set_ext *mext, u32 flags)
{ {
struct htype *h = set->data; struct htype *h = set->data;
struct htable *t = h->table; struct htable *t = rcu_dereference_bh(h->table);
struct hbucket *n; struct hbucket *n;
struct mtype_elem *data; struct mtype_elem *data;
#if IPSET_NET_COUNT == 2
struct mtype_elem orig = *d;
int i, j = 0, k;
#else
int i, j = 0; int i, j = 0;
#endif
u32 key, multi = 0; u32 key, multi = 0;
u8 nets_length = NETS_LENGTH(set->family); u8 nets_length = NLEN(set->family);
pr_debug("test by nets\n"); pr_debug("test by nets\n");
for (; j < nets_length && h->nets[j].nets && !multi; j++) { for (; j < nets_length && h->nets[j].nets[0] && !multi; j++) {
mtype_data_netmask(d, h->nets[j].cidr); #if IPSET_NET_COUNT == 2
mtype_data_reset_elem(d, &orig);
mtype_data_netmask(d, h->nets[j].cidr[0], false);
for (k = 0; k < nets_length && h->nets[k].nets[1] && !multi;
k++) {
mtype_data_netmask(d, h->nets[k].cidr[1], true);
#else
mtype_data_netmask(d, h->nets[j].cidr[0]);
#endif
key = HKEY(d, h->initval, t->htable_bits); key = HKEY(d, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i, h->dsize); data = ahash_data(n, i, set->dsize);
if (!mtype_data_equal(data, d, &multi)) if (!mtype_data_equal(data, d, &multi))
continue; continue;
if (SET_WITH_TIMEOUT(set)) { if (SET_WITH_TIMEOUT(set)) {
if (!ip_set_timeout_expired( if (!ip_set_timeout_expired(
ext_timeout(data, h))) ext_timeout(data, set)))
return mtype_data_match(data, ext, return mtype_data_match(data, ext,
mext, set, mext, set,
flags); flags);
@ -774,6 +834,9 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
return mtype_data_match(data, ext, return mtype_data_match(data, ext,
mext, set, flags); mext, set, flags);
} }
#if IPSET_NET_COUNT == 2
}
#endif
} }
return 0; return 0;
} }
@ -785,30 +848,41 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags) struct ip_set_ext *mext, u32 flags)
{ {
struct htype *h = set->data; struct htype *h = set->data;
struct htable *t = h->table; struct htable *t;
struct mtype_elem *d = value; struct mtype_elem *d = value;
struct hbucket *n; struct hbucket *n;
struct mtype_elem *data; struct mtype_elem *data;
int i; int i, ret = 0;
u32 key, multi = 0; u32 key, multi = 0;
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
/* If we test an IP address and not a network address, /* If we test an IP address and not a network address,
* try all possible network sizes */ * try all possible network sizes */
if (CIDR(d->cidr) == SET_HOST_MASK(set->family)) for (i = 0; i < IPSET_NET_COUNT; i++)
return mtype_test_cidrs(set, d, ext, mext, flags); if (CIDR(d->cidr, i) != SET_HOST_MASK(set->family))
break;
if (i == IPSET_NET_COUNT) {
ret = mtype_test_cidrs(set, d, ext, mext, flags);
goto out;
}
#endif #endif
key = HKEY(d, h->initval, t->htable_bits); key = HKEY(d, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i, h->dsize); data = ahash_data(n, i, set->dsize);
if (mtype_data_equal(data, d, &multi) && if (mtype_data_equal(data, d, &multi) &&
!(SET_WITH_TIMEOUT(set) && !(SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(data, h)))) ip_set_timeout_expired(ext_timeout(data, set)))) {
return mtype_data_match(data, ext, mext, set, flags); ret = mtype_data_match(data, ext, mext, set, flags);
goto out;
}
} }
return 0; out:
rcu_read_unlock_bh();
return ret;
} }
/* Reply a HEADER request: fill out the header part of the set */ /* Reply a HEADER request: fill out the header part of the set */
@ -816,18 +890,18 @@ static int
mtype_head(struct ip_set *set, struct sk_buff *skb) mtype_head(struct ip_set *set, struct sk_buff *skb)
{ {
const struct htype *h = set->data; const struct htype *h = set->data;
const struct htable *t;
struct nlattr *nested; struct nlattr *nested;
size_t memsize; size_t memsize;
read_lock_bh(&set->lock); t = rcu_dereference_bh_nfnl(h->table);
memsize = mtype_ahash_memsize(h, NETS_LENGTH(set->family)); memsize = mtype_ahash_memsize(h, t, NLEN(set->family), set->dsize);
read_unlock_bh(&set->lock);
nested = ipset_nest_start(skb, IPSET_ATTR_DATA); nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) if (!nested)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE,
htonl(jhash_size(h->table->htable_bits))) || htonl(jhash_size(t->htable_bits))) ||
nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
goto nla_put_failure; goto nla_put_failure;
#ifdef IP_SET_HASH_WITH_NETMASK #ifdef IP_SET_HASH_WITH_NETMASK
@ -836,12 +910,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
goto nla_put_failure; goto nla_put_failure;
#endif #endif
if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)))
((set->extensions & IPSET_EXT_TIMEOUT) && goto nla_put_failure;
nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))) || if (unlikely(ip_set_put_flags(skb, set)))
((set->extensions & IPSET_EXT_COUNTER) &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
htonl(IPSET_FLAG_WITH_COUNTERS))))
goto nla_put_failure; goto nla_put_failure;
ipset_nest_end(skb, nested); ipset_nest_end(skb, nested);
@ -856,7 +927,7 @@ mtype_list(const struct ip_set *set,
struct sk_buff *skb, struct netlink_callback *cb) struct sk_buff *skb, struct netlink_callback *cb)
{ {
const struct htype *h = set->data; const struct htype *h = set->data;
const struct htable *t = h->table; const struct htable *t = rcu_dereference_bh_nfnl(h->table);
struct nlattr *atd, *nested; struct nlattr *atd, *nested;
const struct hbucket *n; const struct hbucket *n;
const struct mtype_elem *e; const struct mtype_elem *e;
@ -874,9 +945,9 @@ mtype_list(const struct ip_set *set,
n = hbucket(t, cb->args[2]); n = hbucket(t, cb->args[2]);
pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n); pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
e = ahash_data(n, i, h->dsize); e = ahash_data(n, i, set->dsize);
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, h))) ip_set_timeout_expired(ext_timeout(e, set)))
continue; continue;
pr_debug("list hash %lu hbucket %p i %u, data %p\n", pr_debug("list hash %lu hbucket %p i %u, data %p\n",
cb->args[2], n, i, e); cb->args[2], n, i, e);
@ -890,13 +961,7 @@ mtype_list(const struct ip_set *set,
} }
if (mtype_data_list(skb, e)) if (mtype_data_list(skb, e))
goto nla_put_failure; goto nla_put_failure;
if (SET_WITH_TIMEOUT(set) && if (ip_set_put_extensions(skb, set, e, true))
nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(
ext_timeout(e, h)))))
goto nla_put_failure;
if (SET_WITH_COUNTER(set) &&
ip_set_put_counter(skb, ext_counter(e, h)))
goto nla_put_failure; goto nla_put_failure;
ipset_nest_end(skb, nested); ipset_nest_end(skb, nested);
} }
@ -909,24 +974,24 @@ mtype_list(const struct ip_set *set,
nla_put_failure: nla_put_failure:
nlmsg_trim(skb, incomplete); nlmsg_trim(skb, incomplete);
ipset_nest_end(skb, atd);
if (unlikely(first == cb->args[2])) { if (unlikely(first == cb->args[2])) {
pr_warning("Can't list set %s: one bucket does not fit into " pr_warning("Can't list set %s: one bucket does not fit into "
"a message. Please report it!\n", set->name); "a message. Please report it!\n", set->name);
cb->args[2] = 0; cb->args[2] = 0;
return -EMSGSIZE; return -EMSGSIZE;
} }
ipset_nest_end(skb, atd);
return 0; return 0;
} }
static int static int
TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb, IPSET_TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par, const struct xt_action_param *par,
enum ipset_adt adt, struct ip_set_adt_opt *opt); enum ipset_adt adt, struct ip_set_adt_opt *opt);
static int static int
TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[], IPSET_TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried); enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
static const struct ip_set_type_variant mtype_variant = { static const struct ip_set_type_variant mtype_variant = {
.kadt = mtype_kadt, .kadt = mtype_kadt,
@ -946,16 +1011,17 @@ static const struct ip_set_type_variant mtype_variant = {
#ifdef IP_SET_EMIT_CREATE #ifdef IP_SET_EMIT_CREATE
static int static int
TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags) IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
struct nlattr *tb[], u32 flags)
{ {
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u32 cadt_flags = 0;
u8 hbits; u8 hbits;
#ifdef IP_SET_HASH_WITH_NETMASK #ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask; u8 netmask;
#endif #endif
size_t hsize; size_t hsize;
struct HTYPE *h; struct HTYPE *h;
struct htable *t;
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY; return -IPSET_ERR_INVALID_FAMILY;
@ -1005,7 +1071,7 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
h->netmask = netmask; h->netmask = netmask;
#endif #endif
get_random_bytes(&h->initval, sizeof(h->initval)); get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT; set->timeout = IPSET_NO_TIMEOUT;
hbits = htable_bits(hashsize); hbits = htable_bits(hashsize);
hsize = htable_size(hbits); hsize = htable_size(hbits);
@ -1013,91 +1079,37 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
kfree(h); kfree(h);
return -ENOMEM; return -ENOMEM;
} }
h->table = ip_set_alloc(hsize); t = ip_set_alloc(hsize);
if (!h->table) { if (!t) {
kfree(h); kfree(h);
return -ENOMEM; return -ENOMEM;
} }
h->table->htable_bits = hbits; t->htable_bits = hbits;
rcu_assign_pointer(h->table, t);
set->data = h; set->data = h;
if (set->family == NFPROTO_IPV4) if (set->family == NFPROTO_IPV4) {
set->variant = &TOKEN(HTYPE, 4_variant); set->variant = &IPSET_TOKEN(HTYPE, 4_variant);
else set->dsize = ip_set_elem_len(set, tb,
set->variant = &TOKEN(HTYPE, 6_variant); sizeof(struct IPSET_TOKEN(HTYPE, 4_elem)));
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]) {
h->timeout =
ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
if (set->family == NFPROTO_IPV4) {
h->dsize =
sizeof(struct TOKEN(HTYPE, 4ct_elem));
h->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct TOKEN(HTYPE, 4ct_elem),
timeout);
h->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct TOKEN(HTYPE, 4ct_elem),
counter);
TOKEN(HTYPE, 4_gc_init)(set,
TOKEN(HTYPE, 4_gc));
} else {
h->dsize =
sizeof(struct TOKEN(HTYPE, 6ct_elem));
h->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct TOKEN(HTYPE, 6ct_elem),
timeout);
h->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct TOKEN(HTYPE, 6ct_elem),
counter);
TOKEN(HTYPE, 6_gc_init)(set,
TOKEN(HTYPE, 6_gc));
}
} else {
if (set->family == NFPROTO_IPV4) {
h->dsize =
sizeof(struct TOKEN(HTYPE, 4c_elem));
h->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct TOKEN(HTYPE, 4c_elem),
counter);
} else {
h->dsize =
sizeof(struct TOKEN(HTYPE, 6c_elem));
h->offset[IPSET_OFFSET_COUNTER] =
offsetof(struct TOKEN(HTYPE, 6c_elem),
counter);
}
}
} else if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
if (set->family == NFPROTO_IPV4) {
h->dsize = sizeof(struct TOKEN(HTYPE, 4t_elem));
h->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct TOKEN(HTYPE, 4t_elem),
timeout);
TOKEN(HTYPE, 4_gc_init)(set, TOKEN(HTYPE, 4_gc));
} else {
h->dsize = sizeof(struct TOKEN(HTYPE, 6t_elem));
h->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct TOKEN(HTYPE, 6t_elem),
timeout);
TOKEN(HTYPE, 6_gc_init)(set, TOKEN(HTYPE, 6_gc));
}
} else { } else {
set->variant = &IPSET_TOKEN(HTYPE, 6_variant);
set->dsize = ip_set_elem_len(set, tb,
sizeof(struct IPSET_TOKEN(HTYPE, 6_elem)));
}
if (tb[IPSET_ATTR_TIMEOUT]) {
set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
if (set->family == NFPROTO_IPV4) if (set->family == NFPROTO_IPV4)
h->dsize = sizeof(struct TOKEN(HTYPE, 4_elem)); IPSET_TOKEN(HTYPE, 4_gc_init)(set,
IPSET_TOKEN(HTYPE, 4_gc));
else else
h->dsize = sizeof(struct TOKEN(HTYPE, 6_elem)); IPSET_TOKEN(HTYPE, 6_gc_init)(set,
IPSET_TOKEN(HTYPE, 6_gc));
} }
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(h->table->htable_bits), set->name, jhash_size(t->htable_bits),
h->table->htable_bits, h->maxelem, set->data, h->table); t->htable_bits, h->maxelem, set->data, t);
return 0; return 0;
} }

View file

@ -23,19 +23,20 @@
#include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_hash.h> #include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
#define REVISION_MAX 1 /* Counters support */ /* 1 Counters support */
#define IPSET_TYPE_REV_MAX 2 /* Comments support */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("hash:ip", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("hash:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:ip"); MODULE_ALIAS("ip_set_hash:ip");
/* Type specific function prefix */ /* Type specific function prefix */
#define HTYPE hash_ip #define HTYPE hash_ip
#define IP_SET_HASH_WITH_NETMASK #define IP_SET_HASH_WITH_NETMASK
/* IPv4 variants */ /* IPv4 variant */
/* Member elements */ /* Member elements */
struct hash_ip4_elem { struct hash_ip4_elem {
@ -43,22 +44,6 @@ struct hash_ip4_elem {
__be32 ip; __be32 ip;
}; };
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 */ /* Common functions */
static inline bool static inline bool
@ -99,7 +84,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_ip *h = set->data; const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ip4_elem e = {}; struct hash_ip4_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
__be32 ip; __be32 ip;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
@ -118,8 +103,8 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_ip *h = set->data; const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ip4_elem e = {}; struct hash_ip4_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip, ip_to, hosts; u32 ip = 0, ip_to = 0, hosts;
int ret = 0; int ret = 0;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
@ -178,29 +163,13 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret; return ret;
} }
/* IPv6 variants */ /* IPv6 variant */
/* Member elements */ /* Member elements */
struct hash_ip6_elem { struct hash_ip6_elem {
union nf_inet_addr ip; union nf_inet_addr ip;
}; };
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 */ /* Common functions */
static inline bool static inline bool
@ -253,7 +222,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_ip *h = set->data; const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ip6_elem e = {}; struct hash_ip6_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
hash_ip6_netmask(&e.ip, h->netmask); hash_ip6_netmask(&e.ip, h->netmask);
@ -270,7 +239,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_ip *h = set->data; const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ip6_elem e = {}; struct hash_ip6_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
int ret; int ret;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
@ -304,8 +273,8 @@ static struct ip_set_type hash_ip_type __read_mostly = {
.features = IPSET_TYPE_IP, .features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = hash_ip_create, .create = hash_ip_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@ -324,6 +293,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
[IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -24,19 +24,20 @@
#include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h> #include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
/* 1 SCTP and UDPLITE support added */ /* 1 SCTP and UDPLITE support added */
#define REVISION_MAX 2 /* Counters support added */ /* 2 Counters support added */
#define IPSET_TYPE_REV_MAX 3 /* Comments support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("hash:ip,port", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("hash:ip,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:ip,port"); MODULE_ALIAS("ip_set_hash:ip,port");
/* Type specific function prefix */ /* Type specific function prefix */
#define HTYPE hash_ipport #define HTYPE hash_ipport
/* IPv4 variants */ /* IPv4 variant */
/* Member elements */ /* Member elements */
struct hash_ipport4_elem { struct hash_ipport4_elem {
@ -46,31 +47,6 @@ struct hash_ipport4_elem {
u8 padding; u8 padding;
}; };
struct hash_ipport4t_elem {
__be32 ip;
__be16 port;
u8 proto;
u8 padding;
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 */ /* Common functions */
static inline bool static inline bool
@ -116,10 +92,9 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par, const struct xt_action_param *par,
enum ipset_adt adt, struct ip_set_adt_opt *opt) enum ipset_adt adt, struct ip_set_adt_opt *opt)
{ {
const struct hash_ipport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem e = { }; struct hash_ipport4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto)) &e.port, &e.proto))
@ -136,8 +111,8 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_ipport *h = set->data; const struct hash_ipport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem e = { }; struct hash_ipport4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip, ip_to, p = 0, port, port_to; u32 ip, ip_to = 0, p = 0, port, port_to;
bool with_ports = false; bool with_ports = false;
int ret; int ret;
@ -222,7 +197,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret; return ret;
} }
/* IPv6 variants */ /* IPv6 variant */
struct hash_ipport6_elem { struct hash_ipport6_elem {
union nf_inet_addr ip; union nf_inet_addr ip;
@ -231,31 +206,6 @@ struct hash_ipport6_elem {
u8 padding; u8 padding;
}; };
struct hash_ipport6t_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
u8 padding;
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 */ /* Common functions */
static inline bool static inline bool
@ -306,10 +256,9 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par, const struct xt_action_param *par,
enum ipset_adt adt, struct ip_set_adt_opt *opt) enum ipset_adt adt, struct ip_set_adt_opt *opt)
{ {
const struct hash_ipport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem e = { }; struct hash_ipport6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto)) &e.port, &e.proto))
@ -326,7 +275,7 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_ipport *h = set->data; const struct hash_ipport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem e = { }; struct hash_ipport6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 port, port_to; u32 port, port_to;
bool with_ports = false; bool with_ports = false;
int ret; int ret;
@ -396,8 +345,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = hash_ipport_create, .create = hash_ipport_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@ -419,6 +368,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
[IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -24,19 +24,20 @@
#include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h> #include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
/* 1 SCTP and UDPLITE support added */ /* 1 SCTP and UDPLITE support added */
#define REVISION_MAX 2 /* Counters support added */ /* 2 Counters support added */
#define IPSET_TYPE_REV_MAX 3 /* Comments support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("hash:ip,port,ip", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("hash:ip,port,ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:ip,port,ip"); MODULE_ALIAS("ip_set_hash:ip,port,ip");
/* Type specific function prefix */ /* Type specific function prefix */
#define HTYPE hash_ipportip #define HTYPE hash_ipportip
/* IPv4 variants */ /* IPv4 variant */
/* Member elements */ /* Member elements */
struct hash_ipportip4_elem { struct hash_ipportip4_elem {
@ -47,34 +48,6 @@ struct hash_ipportip4_elem {
u8 padding; u8 padding;
}; };
struct hash_ipportip4t_elem {
__be32 ip;
__be32 ip2;
__be16 port;
u8 proto;
u8 padding;
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 static inline bool
hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
const struct hash_ipportip4_elem *ip2, const struct hash_ipportip4_elem *ip2,
@ -120,10 +93,9 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par, const struct xt_action_param *par,
enum ipset_adt adt, struct ip_set_adt_opt *opt) enum ipset_adt adt, struct ip_set_adt_opt *opt)
{ {
const struct hash_ipportip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem e = { }; struct hash_ipportip4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto)) &e.port, &e.proto))
@ -141,8 +113,8 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_ipportip *h = set->data; const struct hash_ipportip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem e = { }; struct hash_ipportip4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip, ip_to, p = 0, port, port_to; u32 ip, ip_to = 0, p = 0, port, port_to;
bool with_ports = false; bool with_ports = false;
int ret; int ret;
@ -231,7 +203,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret; return ret;
} }
/* IPv6 variants */ /* IPv6 variant */
struct hash_ipportip6_elem { struct hash_ipportip6_elem {
union nf_inet_addr ip; union nf_inet_addr ip;
@ -241,34 +213,6 @@ struct hash_ipportip6_elem {
u8 padding; u8 padding;
}; };
struct hash_ipportip6t_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
u8 proto;
u8 padding;
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 */ /* Common functions */
static inline bool static inline bool
@ -319,10 +263,9 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par, const struct xt_action_param *par,
enum ipset_adt adt, struct ip_set_adt_opt *opt) enum ipset_adt adt, struct ip_set_adt_opt *opt)
{ {
const struct hash_ipportip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem e = { }; struct hash_ipportip6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto)) &e.port, &e.proto))
@ -340,7 +283,7 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_ipportip *h = set->data; const struct hash_ipportip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip6_elem e = { }; struct hash_ipportip6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 port, port_to; u32 port, port_to;
bool with_ports = false; bool with_ports = false;
int ret; int ret;
@ -414,8 +357,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE, .dimension = IPSET_DIM_THREE,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = hash_ipportip_create, .create = hash_ipportip_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@ -437,6 +380,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
[IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -24,15 +24,16 @@
#include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h> #include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
/* 1 SCTP and UDPLITE support added */ /* 1 SCTP and UDPLITE support added */
/* 2 Range as input support for IPv4 added */ /* 2 Range as input support for IPv4 added */
/* 3 nomatch flag support added */ /* 3 nomatch flag support added */
#define REVISION_MAX 4 /* Counters support added */ /* 4 Counters support added */
#define IPSET_TYPE_REV_MAX 5 /* Comments support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("hash:ip,port,net", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:ip,port,net"); MODULE_ALIAS("ip_set_hash:ip,port,net");
/* Type specific function prefix */ /* Type specific function prefix */
@ -46,7 +47,7 @@ MODULE_ALIAS("ip_set_hash:ip,port,net");
#define IP_SET_HASH_WITH_PROTO #define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS #define IP_SET_HASH_WITH_NETS
/* IPv4 variants */ /* IPv4 variant */
/* Member elements */ /* Member elements */
struct hash_ipportnet4_elem { struct hash_ipportnet4_elem {
@ -58,37 +59,6 @@ struct hash_ipportnet4_elem {
u8 proto; u8 proto;
}; };
struct hash_ipportnet4t_elem {
__be32 ip;
__be32 ip2;
__be16 port;
u8 cidr:7;
u8 nomatch:1;
u8 proto;
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 */ /* Common functions */
static inline bool static inline bool
@ -170,9 +140,9 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_ipportnet *h = set->data; const struct hash_ipportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem e = { struct hash_ipportnet4_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
}; };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
e.cidr = HOST_MASK - 1; e.cidr = HOST_MASK - 1;
@ -195,9 +165,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_ipportnet *h = set->data; const struct hash_ipportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 }; struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip, ip_to, p = 0, port, port_to; u32 ip = 0, ip_to = 0, p = 0, port, port_to;
u32 ip2_from, ip2_to, ip2_last, ip2; u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
bool with_ports = false; bool with_ports = false;
u8 cidr; u8 cidr;
int ret; int ret;
@ -272,7 +242,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ip > ip_to) if (ip > ip_to)
swap(ip, ip_to); swap(ip, ip_to);
} else if (tb[IPSET_ATTR_CIDR]) { } else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!cidr || cidr > 32) if (!cidr || cidr > 32)
return -IPSET_ERR_INVALID_CIDR; return -IPSET_ERR_INVALID_CIDR;
@ -306,9 +276,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
: port; : port;
for (; p <= port_to; p++) { for (; p <= port_to; p++) {
e.port = htons(p); e.port = htons(p);
ip2 = retried ip2 = retried &&
&& ip == ntohl(h->next.ip) ip == ntohl(h->next.ip) &&
&& p == ntohs(h->next.port) p == ntohs(h->next.port)
? ntohl(h->next.ip2) : ip2_from; ? ntohl(h->next.ip2) : ip2_from;
while (!after(ip2, ip2_to)) { while (!after(ip2, ip2_to)) {
e.ip2 = htonl(ip2); e.ip2 = htonl(ip2);
@ -328,7 +298,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret; return ret;
} }
/* IPv6 variants */ /* IPv6 variant */
struct hash_ipportnet6_elem { struct hash_ipportnet6_elem {
union nf_inet_addr ip; union nf_inet_addr ip;
@ -339,37 +309,6 @@ struct hash_ipportnet6_elem {
u8 proto; u8 proto;
}; };
struct hash_ipportnet6t_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
u8 cidr:7;
u8 nomatch:1;
u8 proto;
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 */ /* Common functions */
static inline bool static inline bool
@ -454,9 +393,9 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_ipportnet *h = set->data; const struct hash_ipportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem e = { struct hash_ipportnet6_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
}; };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
e.cidr = HOST_MASK - 1; e.cidr = HOST_MASK - 1;
@ -479,7 +418,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_ipportnet *h = set->data; const struct hash_ipportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 }; struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 port, port_to; u32 port, port_to;
bool with_ports = false; bool with_ports = false;
u8 cidr; u8 cidr;
@ -574,8 +513,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
IPSET_TYPE_NOMATCH, IPSET_TYPE_NOMATCH,
.dimension = IPSET_DIM_THREE, .dimension = IPSET_DIM_THREE,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = hash_ipportnet_create, .create = hash_ipportnet_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@ -600,6 +539,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
[IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -22,21 +22,22 @@
#include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_hash.h> #include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
/* 1 Range as input support for IPv4 added */ /* 1 Range as input support for IPv4 added */
/* 2 nomatch flag support added */ /* 2 nomatch flag support added */
#define REVISION_MAX 3 /* Counters support added */ /* 3 Counters support added */
#define IPSET_TYPE_REV_MAX 4 /* Comments support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("hash:net", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("hash:net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:net"); MODULE_ALIAS("ip_set_hash:net");
/* Type specific function prefix */ /* Type specific function prefix */
#define HTYPE hash_net #define HTYPE hash_net
#define IP_SET_HASH_WITH_NETS #define IP_SET_HASH_WITH_NETS
/* IPv4 variants */ /* IPv4 variant */
/* Member elements */ /* Member elements */
struct hash_net4_elem { struct hash_net4_elem {
@ -46,31 +47,6 @@ struct hash_net4_elem {
u8 cidr; u8 cidr;
}; };
struct hash_net4t_elem {
__be32 ip;
u16 padding0;
u8 nomatch;
u8 cidr;
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 */ /* Common functions */
static inline bool static inline bool
@ -143,9 +119,9 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_net *h = set->data; const struct hash_net *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem e = { struct hash_net4_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
}; };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (e.cidr == 0) if (e.cidr == 0)
return -EINVAL; return -EINVAL;
@ -165,8 +141,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_net *h = set->data; const struct hash_net *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem e = { .cidr = HOST_MASK }; struct hash_net4_elem e = { .cidr = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to, last; u32 ip = 0, ip_to = 0, last;
int ret; int ret;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
@ -228,7 +204,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret; return ret;
} }
/* IPv6 variants */ /* IPv6 variant */
struct hash_net6_elem { struct hash_net6_elem {
union nf_inet_addr ip; union nf_inet_addr ip;
@ -237,31 +213,6 @@ struct hash_net6_elem {
u8 cidr; u8 cidr;
}; };
struct hash_net6t_elem {
union nf_inet_addr ip;
u16 padding0;
u8 nomatch;
u8 cidr;
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 */ /* Common functions */
static inline bool static inline bool
@ -338,9 +289,9 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_net *h = set->data; const struct hash_net *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem e = { struct hash_net6_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
}; };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (e.cidr == 0) if (e.cidr == 0)
return -EINVAL; return -EINVAL;
@ -357,10 +308,9 @@ static int
hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{ {
const struct hash_net *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem e = { .cidr = HOST_MASK }; struct hash_net6_elem e = { .cidr = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
int ret; int ret;
if (unlikely(!tb[IPSET_ATTR_IP] || if (unlikely(!tb[IPSET_ATTR_IP] ||
@ -406,8 +356,8 @@ static struct ip_set_type hash_net_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_NOMATCH, .features = IPSET_TYPE_IP | IPSET_TYPE_NOMATCH,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = hash_net_create, .create = hash_net_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@ -425,6 +375,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -23,14 +23,15 @@
#include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_hash.h> #include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
/* 1 nomatch flag support added */ /* 1 nomatch flag support added */
/* 2 /0 support added */ /* 2 /0 support added */
#define REVISION_MAX 3 /* Counters support added */ /* 3 Counters support added */
#define IPSET_TYPE_REV_MAX 4 /* Comments support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("hash:net,iface", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("hash:net,iface", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:net,iface"); MODULE_ALIAS("ip_set_hash:net,iface");
/* Interface name rbtree */ /* Interface name rbtree */
@ -134,7 +135,7 @@ iface_add(struct rb_root *root, const char **iface)
#define STREQ(a, b) (strcmp(a, b) == 0) #define STREQ(a, b) (strcmp(a, b) == 0)
/* IPv4 variants */ /* IPv4 variant */
struct hash_netiface4_elem_hashed { struct hash_netiface4_elem_hashed {
__be32 ip; __be32 ip;
@ -144,7 +145,7 @@ struct hash_netiface4_elem_hashed {
u8 elem; u8 elem;
}; };
/* Member elements without timeout */ /* Member elements */
struct hash_netiface4_elem { struct hash_netiface4_elem {
__be32 ip; __be32 ip;
u8 physdev; u8 physdev;
@ -154,37 +155,6 @@ struct hash_netiface4_elem {
const char *iface; const char *iface;
}; };
struct hash_netiface4t_elem {
__be32 ip;
u8 physdev;
u8 cidr;
u8 nomatch;
u8 elem;
const char *iface;
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 */ /* Common functions */
static inline bool static inline bool
@ -265,10 +235,10 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
struct hash_netiface *h = set->data; struct hash_netiface *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem e = { struct hash_netiface4_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.elem = 1, .elem = 1,
}; };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
int ret; int ret;
if (e.cidr == 0) if (e.cidr == 0)
@ -319,8 +289,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
struct hash_netiface *h = set->data; struct hash_netiface *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem e = { .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); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to, last; u32 ip = 0, ip_to = 0, last;
char iface[IFNAMSIZ]; char iface[IFNAMSIZ];
int ret; int ret;
@ -399,7 +369,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret; return ret;
} }
/* IPv6 variants */ /* IPv6 variant */
struct hash_netiface6_elem_hashed { struct hash_netiface6_elem_hashed {
union nf_inet_addr ip; union nf_inet_addr ip;
@ -418,37 +388,6 @@ struct hash_netiface6_elem {
const char *iface; const char *iface;
}; };
struct hash_netiface6t_elem {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
u8 nomatch;
u8 elem;
const char *iface;
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 */ /* Common functions */
static inline bool static inline bool
@ -534,10 +473,10 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
struct hash_netiface *h = set->data; struct hash_netiface *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface6_elem e = { struct hash_netiface6_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.elem = 1, .elem = 1,
}; };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
int ret; int ret;
if (e.cidr == 0) if (e.cidr == 0)
@ -584,7 +523,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
struct hash_netiface *h = set->data; struct hash_netiface *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 }; struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
char iface[IFNAMSIZ]; char iface[IFNAMSIZ];
int ret; int ret;
@ -645,8 +584,8 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
IPSET_TYPE_NOMATCH, IPSET_TYPE_NOMATCH,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = hash_netiface_create, .create = hash_netiface_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@ -668,6 +607,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
[IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -0,0 +1,483 @@
/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
* Copyright (C) 2013 Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the hash:net type */
#include <linux/jhash.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define IPSET_TYPE_REV_MIN 0
#define IPSET_TYPE_REV_MAX 0
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
IP_SET_MODULE_DESC("hash:net,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:net,net");
/* Type specific function prefix */
#define HTYPE hash_netnet
#define IP_SET_HASH_WITH_NETS
#define IPSET_NET_COUNT 2
/* IPv4 variants */
/* Member elements */
struct hash_netnet4_elem {
union {
__be32 ip[2];
__be64 ipcmp;
};
u8 nomatch;
union {
u8 cidr[2];
u16 ccmp;
};
};
/* Common functions */
static inline bool
hash_netnet4_data_equal(const struct hash_netnet4_elem *ip1,
const struct hash_netnet4_elem *ip2,
u32 *multi)
{
return ip1->ipcmp == ip2->ipcmp &&
ip2->ccmp == ip2->ccmp;
}
static inline int
hash_netnet4_do_data_match(const struct hash_netnet4_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_netnet4_data_set_flags(struct hash_netnet4_elem *elem, u32 flags)
{
elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
}
static inline void
hash_netnet4_data_reset_flags(struct hash_netnet4_elem *elem, u8 *flags)
{
swap(*flags, elem->nomatch);
}
static inline void
hash_netnet4_data_reset_elem(struct hash_netnet4_elem *elem,
struct hash_netnet4_elem *orig)
{
elem->ip[1] = orig->ip[1];
}
static inline void
hash_netnet4_data_netmask(struct hash_netnet4_elem *elem, u8 cidr, bool inner)
{
if (inner) {
elem->ip[1] &= ip_set_netmask(cidr);
elem->cidr[1] = cidr;
} else {
elem->ip[0] &= ip_set_netmask(cidr);
elem->cidr[0] = cidr;
}
}
static bool
hash_netnet4_data_list(struct sk_buff *skb,
const struct hash_netnet4_elem *data)
{
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) ||
nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
static inline void
hash_netnet4_data_next(struct hash_netnet4_elem *next,
const struct hash_netnet4_elem *d)
{
next->ipcmp = d->ipcmp;
}
#define MTYPE hash_netnet4
#define PF 4
#define HOST_MASK 32
#include "ip_set_hash_gen.h"
static int
hash_netnet4_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)
{
const struct hash_netnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet4_elem e = {
.cidr[0] = h->nets[0].cidr[0] ? h->nets[0].cidr[0] : HOST_MASK,
.cidr[1] = h->nets[0].cidr[1] ? h->nets[0].cidr[1] : HOST_MASK,
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (adt == IPSET_TEST)
e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]);
e.ip[0] &= ip_set_netmask(e.cidr[0]);
e.ip[1] &= ip_set_netmask(e.cidr[1]);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct hash_netnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet4_elem e = { .cidr[0] = HOST_MASK,
.cidr[1] = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0, last;
u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2;
u8 cidr, cidr2;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!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)))
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) ||
ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR]) {
cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
e.cidr[0] = cidr;
}
if (tb[IPSET_ATTR_CIDR2]) {
cidr2 = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!cidr2 || cidr2 > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
e.cidr[1] = cidr2;
}
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 |= (IPSET_FLAG_NOMATCH << 16);
}
if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] &&
tb[IPSET_ATTR_IP2_TO])) {
e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0]));
e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1]));
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt, set) ? -ret :
ip_set_eexist(ret, flags) ? 0 : ret;
}
ip_to = ip;
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
if (ip_to < ip)
swap(ip, ip_to);
if (ip + UINT_MAX == ip_to)
return -IPSET_ERR_HASH_RANGE;
}
ip2_to = ip2_from;
if (tb[IPSET_ATTR_IP2_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
if (ret)
return ret;
if (ip2_to < ip2_from)
swap(ip2_from, ip2_to);
if (ip2_from + UINT_MAX == ip2_to)
return -IPSET_ERR_HASH_RANGE;
}
if (retried)
ip = ntohl(h->next.ip[0]);
while (!after(ip, ip_to)) {
e.ip[0] = htonl(ip);
last = ip_set_range_to_cidr(ip, ip_to, &cidr);
e.cidr[0] = cidr;
ip2 = (retried &&
ip == ntohl(h->next.ip[0])) ? ntohl(h->next.ip[1])
: ip2_from;
while (!after(ip2, ip2_to)) {
e.ip[1] = htonl(ip2);
last2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr2);
e.cidr[1] = cidr2;
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
ip2 = last2 + 1;
}
ip = last + 1;
}
return ret;
}
/* IPv6 variants */
struct hash_netnet6_elem {
union nf_inet_addr ip[2];
u8 nomatch;
union {
u8 cidr[2];
u16 ccmp;
};
};
/* Common functions */
static inline bool
hash_netnet6_data_equal(const struct hash_netnet6_elem *ip1,
const struct hash_netnet6_elem *ip2,
u32 *multi)
{
return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) &&
ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) &&
ip1->ccmp == ip2->ccmp;
}
static inline int
hash_netnet6_do_data_match(const struct hash_netnet6_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_netnet6_data_set_flags(struct hash_netnet6_elem *elem, u32 flags)
{
elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
}
static inline void
hash_netnet6_data_reset_flags(struct hash_netnet6_elem *elem, u8 *flags)
{
swap(*flags, elem->nomatch);
}
static inline void
hash_netnet6_data_reset_elem(struct hash_netnet6_elem *elem,
struct hash_netnet6_elem *orig)
{
elem->ip[1] = orig->ip[1];
}
static inline void
hash_netnet6_data_netmask(struct hash_netnet6_elem *elem, u8 cidr, bool inner)
{
if (inner) {
ip6_netmask(&elem->ip[1], cidr);
elem->cidr[1] = cidr;
} else {
ip6_netmask(&elem->ip[0], cidr);
elem->cidr[0] = cidr;
}
}
static bool
hash_netnet6_data_list(struct sk_buff *skb,
const struct hash_netnet6_elem *data)
{
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) ||
nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
static inline void
hash_netnet6_data_next(struct hash_netnet4_elem *next,
const struct hash_netnet6_elem *d)
{
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#define MTYPE hash_netnet6
#define PF 6
#define HOST_MASK 128
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_netnet6_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)
{
const struct hash_netnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet6_elem e = {
.cidr[0] = h->nets[0].cidr[0] ? h->nets[0].cidr[0] : HOST_MASK,
.cidr[1] = h->nets[0].cidr[1] ? h->nets[0].cidr[1] : HOST_MASK
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (adt == IPSET_TEST)
e.ccmp = (HOST_MASK << (sizeof(u8)*8)) | HOST_MASK;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1].in6);
ip6_netmask(&e.ip[0], e.cidr[0]);
ip6_netmask(&e.ip[1], e.cidr[1]);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet6_elem e = { .cidr[0] = HOST_MASK,
.cidr[1] = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!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)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) ||
ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR])
e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (tb[IPSET_ATTR_CIDR2])
e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] ||
e.cidr[1] > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
ip6_netmask(&e.ip[0], e.cidr[0]);
ip6_netmask(&e.ip[1], e.cidr[1]);
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 |= (IPSET_FLAG_NOMATCH << 16);
}
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt, set) ? -ret :
ip_set_eexist(ret, flags) ? 0 : ret;
}
static struct ip_set_type hash_netnet_type __read_mostly = {
.name = "hash:net,net",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_IP2 | IPSET_TYPE_NOMATCH,
.dimension = IPSET_DIM_TWO,
.family = NFPROTO_UNSPEC,
.revision_min = IPSET_TYPE_REV_MIN,
.revision_max = IPSET_TYPE_REV_MAX,
.create = hash_netnet_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR2] = { .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 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
},
.me = THIS_MODULE,
};
static int __init
hash_netnet_init(void)
{
return ip_set_type_register(&hash_netnet_type);
}
static void __exit
hash_netnet_fini(void)
{
ip_set_type_unregister(&hash_netnet_type);
}
module_init(hash_netnet_init);
module_exit(hash_netnet_fini);

View file

@ -23,15 +23,16 @@
#include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h> #include <linux/netfilter/ipset/ip_set_hash.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
/* 1 SCTP and UDPLITE support added */ /* 1 SCTP and UDPLITE support added */
/* 2 Range as input support for IPv4 added */ /* 2 Range as input support for IPv4 added */
/* 3 nomatch flag support added */ /* 3 nomatch flag support added */
#define REVISION_MAX 4 /* Counters support added */ /* 4 Counters support added */
#define IPSET_TYPE_REV_MAX 5 /* Comments support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("hash:net,port", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("hash:net,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:net,port"); MODULE_ALIAS("ip_set_hash:net,port");
/* Type specific function prefix */ /* Type specific function prefix */
@ -45,7 +46,7 @@ MODULE_ALIAS("ip_set_hash:net,port");
*/ */
#define IP_SET_HASH_WITH_NETS_PACKED #define IP_SET_HASH_WITH_NETS_PACKED
/* IPv4 variants */ /* IPv4 variant */
/* Member elements */ /* Member elements */
struct hash_netport4_elem { struct hash_netport4_elem {
@ -56,34 +57,6 @@ struct hash_netport4_elem {
u8 nomatch:1; u8 nomatch:1;
}; };
struct hash_netport4t_elem {
__be32 ip;
__be16 port;
u8 proto;
u8 cidr:7;
u8 nomatch:1;
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 */ /* Common functions */
static inline bool static inline bool
@ -162,9 +135,9 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_netport *h = set->data; const struct hash_netport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem e = { struct hash_netport4_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
}; };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
e.cidr = HOST_MASK - 1; e.cidr = HOST_MASK - 1;
@ -186,8 +159,8 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_netport *h = set->data; const struct hash_netport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 }; struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 port, port_to, p = 0, ip = 0, ip_to, last; u32 port, port_to, p = 0, ip = 0, ip_to = 0, last;
bool with_ports = false; bool with_ports = false;
u8 cidr; u8 cidr;
int ret; int ret;
@ -287,7 +260,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret; return ret;
} }
/* IPv6 variants */ /* IPv6 variant */
struct hash_netport6_elem { struct hash_netport6_elem {
union nf_inet_addr ip; union nf_inet_addr ip;
@ -297,34 +270,6 @@ struct hash_netport6_elem {
u8 nomatch:1; u8 nomatch:1;
}; };
struct hash_netport6t_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
u8 cidr:7;
u8 nomatch:1;
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 */ /* Common functions */
static inline bool static inline bool
@ -407,9 +352,9 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_netport *h = set->data; const struct hash_netport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem e = { struct hash_netport6_elem e = {
.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1, .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
}; };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (adt == IPSET_TEST) if (adt == IPSET_TEST)
e.cidr = HOST_MASK - 1; e.cidr = HOST_MASK - 1;
@ -431,7 +376,7 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
const struct hash_netport *h = set->data; const struct hash_netport *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem e = { .cidr = HOST_MASK - 1 }; struct hash_netport6_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(h); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 port, port_to; u32 port, port_to;
bool with_ports = false; bool with_ports = false;
u8 cidr; u8 cidr;
@ -518,8 +463,8 @@ static struct ip_set_type hash_netport_type __read_mostly = {
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_NOMATCH, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_NOMATCH,
.dimension = IPSET_DIM_TWO, .dimension = IPSET_DIM_TWO,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = hash_netport_create, .create = hash_netport_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@ -542,6 +487,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -0,0 +1,588 @@
/* 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
* published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the hash:ip,port,net type */
#include <linux/jhash.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlink.h>
#include <net/tcp.h>
#include <linux/netfilter.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_getport.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define IPSET_TYPE_REV_MIN 0
#define IPSET_TYPE_REV_MAX 0 /* Comments support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
IP_SET_MODULE_DESC("hash:net,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_hash:net,port,net");
/* Type specific function prefix */
#define HTYPE hash_netportnet
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
#define IPSET_NET_COUNT 2
/* IPv4 variant */
/* Member elements */
struct hash_netportnet4_elem {
union {
__be32 ip[2];
__be64 ipcmp;
};
__be16 port;
union {
u8 cidr[2];
u16 ccmp;
};
u8 nomatch:1;
u8 proto;
};
/* Common functions */
static inline bool
hash_netportnet4_data_equal(const struct hash_netportnet4_elem *ip1,
const struct hash_netportnet4_elem *ip2,
u32 *multi)
{
return ip1->ipcmp == ip2->ipcmp &&
ip1->ccmp == ip2->ccmp &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto;
}
static inline int
hash_netportnet4_do_data_match(const struct hash_netportnet4_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_netportnet4_data_set_flags(struct hash_netportnet4_elem *elem, u32 flags)
{
elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
}
static inline void
hash_netportnet4_data_reset_flags(struct hash_netportnet4_elem *elem, u8 *flags)
{
swap(*flags, elem->nomatch);
}
static inline void
hash_netportnet4_data_reset_elem(struct hash_netportnet4_elem *elem,
struct hash_netportnet4_elem *orig)
{
elem->ip[1] = orig->ip[1];
}
static inline void
hash_netportnet4_data_netmask(struct hash_netportnet4_elem *elem,
u8 cidr, bool inner)
{
if (inner) {
elem->ip[1] &= ip_set_netmask(cidr);
elem->cidr[1] = cidr;
} else {
elem->ip[0] &= ip_set_netmask(cidr);
elem->cidr[0] = cidr;
}
}
static bool
hash_netportnet4_data_list(struct sk_buff *skb,
const struct hash_netportnet4_elem *data)
{
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) ||
nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) ||
nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
static inline void
hash_netportnet4_data_next(struct hash_netportnet4_elem *next,
const struct hash_netportnet4_elem *d)
{
next->ipcmp = d->ipcmp;
next->port = d->port;
}
#define MTYPE hash_netportnet4
#define PF 4
#define HOST_MASK 32
#include "ip_set_hash_gen.h"
static int
hash_netportnet4_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)
{
const struct hash_netportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netportnet4_elem e = {
.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK),
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (adt == IPSET_TEST)
e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto))
return -EINVAL;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1]);
e.ip[0] &= ip_set_netmask(e.cidr[0]);
e.ip[1] &= ip_set_netmask(e.cidr[1]);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct hash_netportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netportnet4_elem e = { .cidr[0] = HOST_MASK,
.cidr[1] = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to;
u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
bool with_ports = false;
u8 cidr, cidr2;
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_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) ||
ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR]) {
cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
e.cidr[0] = cidr;
}
if (tb[IPSET_ATTR_CIDR2]) {
cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
e.cidr[1] = cidr;
}
if (tb[IPSET_ATTR_PORT])
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || e.proto == IPPROTO_ICMP))
e.port = 0;
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 |= (IPSET_FLAG_NOMATCH << 16);
}
with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
if (adt == IPSET_TEST ||
!(tb[IPSET_ATTR_IP_TO] || with_ports || tb[IPSET_ATTR_IP2_TO])) {
e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0]));
e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1]));
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt, set) ? -ret :
ip_set_eexist(ret, flags) ? 0 : ret;
}
ip_to = ip;
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
if (ip > ip_to)
swap(ip, ip_to);
if (unlikely(ip + UINT_MAX == ip_to))
return -IPSET_ERR_HASH_RANGE;
}
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)
swap(port, port_to);
}
ip2_to = ip2_from;
if (tb[IPSET_ATTR_IP2_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
if (ret)
return ret;
if (ip2_from > ip2_to)
swap(ip2_from, ip2_to);
if (unlikely(ip2_from + UINT_MAX == ip2_to))
return -IPSET_ERR_HASH_RANGE;
}
if (retried)
ip = ntohl(h->next.ip[0]);
while (!after(ip, ip_to)) {
e.ip[0] = htonl(ip);
ip_last = ip_set_range_to_cidr(ip, ip_to, &cidr);
e.cidr[0] = cidr;
p = retried && ip == ntohl(h->next.ip[0]) ? ntohs(h->next.port)
: port;
for (; p <= port_to; p++) {
e.port = htons(p);
ip2 = (retried && ip == ntohl(h->next.ip[0]) &&
p == ntohs(h->next.port)) ? ntohl(h->next.ip[1])
: ip2_from;
while (!after(ip2, ip2_to)) {
e.ip[1] = htonl(ip2);
ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
&cidr2);
e.cidr[1] = cidr2;
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
ip2 = ip2_last + 1;
}
}
ip = ip_last + 1;
}
return ret;
}
/* IPv6 variant */
struct hash_netportnet6_elem {
union nf_inet_addr ip[2];
__be16 port;
union {
u8 cidr[2];
u16 ccmp;
};
u8 nomatch:1;
u8 proto;
};
/* Common functions */
static inline bool
hash_netportnet6_data_equal(const struct hash_netportnet6_elem *ip1,
const struct hash_netportnet6_elem *ip2,
u32 *multi)
{
return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) &&
ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) &&
ip1->ccmp == ip2->ccmp &&
ip1->port == ip2->port &&
ip1->proto == ip2->proto;
}
static inline int
hash_netportnet6_do_data_match(const struct hash_netportnet6_elem *elem)
{
return elem->nomatch ? -ENOTEMPTY : 1;
}
static inline void
hash_netportnet6_data_set_flags(struct hash_netportnet6_elem *elem, u32 flags)
{
elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
}
static inline void
hash_netportnet6_data_reset_flags(struct hash_netportnet6_elem *elem, u8 *flags)
{
swap(*flags, elem->nomatch);
}
static inline void
hash_netportnet6_data_reset_elem(struct hash_netportnet6_elem *elem,
struct hash_netportnet6_elem *orig)
{
elem->ip[1] = orig->ip[1];
}
static inline void
hash_netportnet6_data_netmask(struct hash_netportnet6_elem *elem,
u8 cidr, bool inner)
{
if (inner) {
ip6_netmask(&elem->ip[1], cidr);
elem->cidr[1] = cidr;
} else {
ip6_netmask(&elem->ip[0], cidr);
elem->cidr[0] = cidr;
}
}
static bool
hash_netportnet6_data_list(struct sk_buff *skb,
const struct hash_netportnet6_elem *data)
{
u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) ||
nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) ||
nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
return 0;
nla_put_failure:
return 1;
}
static inline void
hash_netportnet6_data_next(struct hash_netportnet4_elem *next,
const struct hash_netportnet6_elem *d)
{
next->port = d->port;
}
#undef MTYPE
#undef PF
#undef HOST_MASK
#define MTYPE hash_netportnet6
#define PF 6
#define HOST_MASK 128
#define IP_SET_EMIT_CREATE
#include "ip_set_hash_gen.h"
static int
hash_netportnet6_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)
{
const struct hash_netportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netportnet6_elem e = {
.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK),
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
if (adt == IPSET_TEST)
e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK;
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto))
return -EINVAL;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1].in6);
ip6_netmask(&e.ip[0], e.cidr[0]);
ip6_netmask(&e.ip[1], e.cidr[1]);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
static int
hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
const struct hash_netportnet *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netportnet6_elem e = { .cidr[0] = HOST_MASK,
.cidr[1] = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 port, port_to;
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_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] || tb[IPSET_ATTR_IP2_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) ||
ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) ||
ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
if (tb[IPSET_ATTR_CIDR])
e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (tb[IPSET_ATTR_CIDR2])
e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
if (unlikely(!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] ||
e.cidr[1] > HOST_MASK))
return -IPSET_ERR_INVALID_CIDR;
ip6_netmask(&e.ip[0], e.cidr[0]);
ip6_netmask(&e.ip[1], e.cidr[1]);
if (tb[IPSET_ATTR_PORT])
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
else
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_PROTO]) {
e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
with_ports = ip_set_proto_with_ports(e.proto);
if (e.proto == 0)
return -IPSET_ERR_INVALID_PROTO;
} else
return -IPSET_ERR_MISSING_PROTO;
if (!(with_ports || e.proto == IPPROTO_ICMPV6))
e.port = 0;
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 |= (IPSET_FLAG_NOMATCH << 16);
}
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt, set) ? -ret :
ip_set_eexist(ret, flags) ? 0 : ret;
}
port = ntohs(e.port);
port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (port > port_to)
swap(port, port_to);
if (retried)
port = ntohs(h->next.port);
for (; port <= port_to; port++) {
e.port = htons(port);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
else
ret = 0;
}
return ret;
}
static struct ip_set_type hash_netportnet_type __read_mostly = {
.name = "hash:net,port,net",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 |
IPSET_TYPE_NOMATCH,
.dimension = IPSET_DIM_THREE,
.family = NFPROTO_UNSPEC,
.revision_min = IPSET_TYPE_REV_MIN,
.revision_max = IPSET_TYPE_REV_MAX,
.create = hash_netportnet_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2] = { .type = NLA_NESTED },
[IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[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 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
},
.me = THIS_MODULE,
};
static int __init
hash_netportnet_init(void)
{
return ip_set_type_register(&hash_netportnet_type);
}
static void __exit
hash_netportnet_fini(void)
{
ip_set_type_unregister(&hash_netportnet_type);
}
module_init(hash_netportnet_init);
module_exit(hash_netportnet_fini);

View file

@ -15,12 +15,13 @@
#include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_list.h> #include <linux/netfilter/ipset/ip_set_list.h>
#define REVISION_MIN 0 #define IPSET_TYPE_REV_MIN 0
#define REVISION_MAX 1 /* Counters support added */ /* 1 Counters support added */
#define IPSET_TYPE_REV_MAX 2 /* Comments support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX); IP_SET_MODULE_DESC("list:set", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
MODULE_ALIAS("ip_set_list:set"); MODULE_ALIAS("ip_set_list:set");
/* Member elements */ /* Member elements */
@ -28,28 +29,6 @@ struct set_elem {
ip_set_id_t id; 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 { struct set_adt_elem {
ip_set_id_t id; ip_set_id_t id;
ip_set_id_t refid; ip_set_id_t refid;
@ -58,24 +37,14 @@ struct set_adt_elem {
/* Type structure */ /* Type structure */
struct list_set { 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 size; /* size of set list array */
u32 timeout; /* timeout value */
struct timer_list gc; /* garbage collection */ struct timer_list gc; /* garbage collection */
struct net *net; /* namespace */
struct set_elem members[0]; /* the set members */ struct set_elem members[0]; /* the set members */
}; };
static inline struct set_elem * #define list_set_elem(set, map, id) \
list_set_elem(const struct list_set *map, u32 id) (struct set_elem *)((void *)(map)->members + (id) * (set)->dsize)
{
return (struct set_elem *)((void *)map->members + id * map->dsize);
}
#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 static int
list_set_ktest(struct ip_set *set, const struct sk_buff *skb, list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
@ -92,16 +61,16 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE) if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE; opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE;
for (i = 0; i < map->size; i++) { for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
return 0; return 0;
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map))) ip_set_timeout_expired(ext_timeout(e, set)))
continue; continue;
ret = ip_set_test(e->id, skb, par, opt); ret = ip_set_test(e->id, skb, par, opt);
if (ret > 0) { if (ret > 0) {
if (SET_WITH_COUNTER(set)) if (SET_WITH_COUNTER(set))
ip_set_update_counter(ext_counter(e, map), ip_set_update_counter(ext_counter(e, set),
ext, &opt->ext, ext, &opt->ext,
cmdflags); cmdflags);
return ret; return ret;
@ -121,11 +90,11 @@ list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
int ret; int ret;
for (i = 0; i < map->size; i++) { for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
return 0; return 0;
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map))) ip_set_timeout_expired(ext_timeout(e, set)))
continue; continue;
ret = ip_set_add(e->id, skb, par, opt); ret = ip_set_add(e->id, skb, par, opt);
if (ret == 0) if (ret == 0)
@ -145,11 +114,11 @@ list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
int ret; int ret;
for (i = 0; i < map->size; i++) { for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
return 0; return 0;
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map))) ip_set_timeout_expired(ext_timeout(e, set)))
continue; continue;
ret = ip_set_del(e->id, skb, par, opt); ret = ip_set_del(e->id, skb, par, opt);
if (ret == 0) if (ret == 0)
@ -163,8 +132,7 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par, const struct xt_action_param *par,
enum ipset_adt adt, struct ip_set_adt_opt *opt) 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, set);
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
switch (adt) { switch (adt) {
case IPSET_TEST: case IPSET_TEST:
@ -188,10 +156,10 @@ id_eq(const struct ip_set *set, u32 i, ip_set_id_t id)
if (i >= map->size) if (i >= map->size)
return 0; return 0;
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
return !!(e->id == id && return !!(e->id == id &&
!(SET_WITH_TIMEOUT(set) && !(SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))); ip_set_timeout_expired(ext_timeout(e, set))));
} }
static int static int
@ -199,28 +167,36 @@ list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d,
const struct ip_set_ext *ext) const struct ip_set_ext *ext)
{ {
struct list_set *map = set->data; struct list_set *map = set->data;
struct set_elem *e = list_set_elem(map, i); struct set_elem *e = list_set_elem(set, map, i);
if (e->id != IPSET_INVALID_ID) { if (e->id != IPSET_INVALID_ID) {
if (i == map->size - 1) if (i == map->size - 1) {
/* Last element replaced: e.g. add new,before,last */ /* Last element replaced: e.g. add new,before,last */
ip_set_put_byindex(e->id); ip_set_put_byindex(map->net, e->id);
else { ip_set_ext_destroy(set, e);
struct set_elem *x = list_set_elem(map, map->size - 1); } else {
struct set_elem *x = list_set_elem(set, map,
map->size - 1);
/* Last element pushed off */ /* Last element pushed off */
if (x->id != IPSET_INVALID_ID) if (x->id != IPSET_INVALID_ID) {
ip_set_put_byindex(x->id); ip_set_put_byindex(map->net, x->id);
memmove(list_set_elem(map, i + 1), e, ip_set_ext_destroy(set, x);
map->dsize * (map->size - (i + 1))); }
memmove(list_set_elem(set, map, i + 1), e,
set->dsize * (map->size - (i + 1)));
/* Extensions must be initialized to zero */
memset(e, 0, set->dsize);
} }
} }
e->id = d->id; e->id = d->id;
if (SET_WITH_TIMEOUT(set)) if (SET_WITH_TIMEOUT(set))
ip_set_timeout_set(ext_timeout(e, map), ext->timeout); ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
if (SET_WITH_COUNTER(set)) if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(e, map), ext); ip_set_init_counter(ext_counter(e, set), ext);
if (SET_WITH_COMMENT(set))
ip_set_init_comment(ext_comment(e, set), ext);
return 0; return 0;
} }
@ -228,16 +204,17 @@ static int
list_set_del(struct ip_set *set, u32 i) list_set_del(struct ip_set *set, u32 i)
{ {
struct list_set *map = set->data; struct list_set *map = set->data;
struct set_elem *e = list_set_elem(map, i); struct set_elem *e = list_set_elem(set, map, i);
ip_set_put_byindex(e->id); ip_set_put_byindex(map->net, e->id);
ip_set_ext_destroy(set, e);
if (i < map->size - 1) if (i < map->size - 1)
memmove(e, list_set_elem(map, i + 1), memmove(e, list_set_elem(set, map, i + 1),
map->dsize * (map->size - (i + 1))); set->dsize * (map->size - (i + 1)));
/* Last element */ /* Last element */
e = list_set_elem(map, map->size - 1); e = list_set_elem(set, map, map->size - 1);
e->id = IPSET_INVALID_ID; e->id = IPSET_INVALID_ID;
return 0; return 0;
} }
@ -247,13 +224,16 @@ set_cleanup_entries(struct ip_set *set)
{ {
struct list_set *map = set->data; struct list_set *map = set->data;
struct set_elem *e; struct set_elem *e;
u32 i; u32 i = 0;
for (i = 0; i < map->size; i++) { while (i < map->size) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id != IPSET_INVALID_ID && if (e->id != IPSET_INVALID_ID &&
ip_set_timeout_expired(ext_timeout(e, map))) ip_set_timeout_expired(ext_timeout(e, set)))
list_set_del(set, i); list_set_del(set, i);
/* Check element moved to position i in next loop */
else
i++;
} }
} }
@ -268,11 +248,11 @@ list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
int ret; int ret;
for (i = 0; i < map->size; i++) { for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
return 0; return 0;
else if (SET_WITH_TIMEOUT(set) && else if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map))) ip_set_timeout_expired(ext_timeout(e, set)))
continue; continue;
else if (e->id != d->id) else if (e->id != d->id)
continue; continue;
@ -299,14 +279,14 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
bool flag_exist = flags & IPSET_FLAG_EXIST; bool flag_exist = flags & IPSET_FLAG_EXIST;
u32 i, ret = 0; u32 i, ret = 0;
if (SET_WITH_TIMEOUT(set))
set_cleanup_entries(set);
/* Check already added element */ /* Check already added element */
for (i = 0; i < map->size; i++) { for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
goto insert; goto insert;
else if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map)))
continue;
else if (e->id != d->id) else if (e->id != d->id)
continue; continue;
@ -319,18 +299,22 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
/* Can't re-add */ /* Can't re-add */
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
/* Update extensions */ /* Update extensions */
ip_set_ext_destroy(set, e);
if (SET_WITH_TIMEOUT(set)) if (SET_WITH_TIMEOUT(set))
ip_set_timeout_set(ext_timeout(e, map), ext->timeout); ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
if (SET_WITH_COUNTER(set)) if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(e, map), ext); ip_set_init_counter(ext_counter(e, set), ext);
if (SET_WITH_COMMENT(set))
ip_set_init_comment(ext_comment(e, set), ext);
/* Set is already added to the list */ /* Set is already added to the list */
ip_set_put_byindex(d->id); ip_set_put_byindex(map->net, d->id);
return 0; return 0;
} }
insert: insert:
ret = -IPSET_ERR_LIST_FULL; ret = -IPSET_ERR_LIST_FULL;
for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
ret = d->before != 0 ? -IPSET_ERR_REF_EXIST ret = d->before != 0 ? -IPSET_ERR_REF_EXIST
: list_set_add(set, i, d, ext); : list_set_add(set, i, d, ext);
@ -355,12 +339,12 @@ list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
u32 i; u32 i;
for (i = 0; i < map->size; i++) { for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
return d->before != 0 ? -IPSET_ERR_REF_EXIST return d->before != 0 ? -IPSET_ERR_REF_EXIST
: -IPSET_ERR_EXIST; : -IPSET_ERR_EXIST;
else if (SET_WITH_TIMEOUT(set) && else if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map))) ip_set_timeout_expired(ext_timeout(e, set)))
continue; continue;
else if (e->id != d->id) else if (e->id != d->id)
continue; continue;
@ -386,7 +370,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
struct list_set *map = set->data; struct list_set *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct set_adt_elem e = { .refid = IPSET_INVALID_ID }; struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
struct ip_set_ext ext = IP_SET_INIT_UEXT(map); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
struct ip_set *s; struct ip_set *s;
int ret = 0; int ret = 0;
@ -403,7 +387,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
ret = ip_set_get_extensions(set, tb, &ext); ret = ip_set_get_extensions(set, tb, &ext);
if (ret) if (ret)
return ret; return ret;
e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
if (e.id == IPSET_INVALID_ID) if (e.id == IPSET_INVALID_ID)
return -IPSET_ERR_NAME; return -IPSET_ERR_NAME;
/* "Loop detection" */ /* "Loop detection" */
@ -423,7 +407,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
} }
if (tb[IPSET_ATTR_NAMEREF]) { if (tb[IPSET_ATTR_NAMEREF]) {
e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), e.refid = ip_set_get_byname(map->net,
nla_data(tb[IPSET_ATTR_NAMEREF]),
&s); &s);
if (e.refid == IPSET_INVALID_ID) { if (e.refid == IPSET_INVALID_ID) {
ret = -IPSET_ERR_NAMEREF; ret = -IPSET_ERR_NAMEREF;
@ -439,9 +424,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
finish: finish:
if (e.refid != IPSET_INVALID_ID) if (e.refid != IPSET_INVALID_ID)
ip_set_put_byindex(e.refid); ip_set_put_byindex(map->net, e.refid);
if (adt != IPSET_ADD || ret) if (adt != IPSET_ADD || ret)
ip_set_put_byindex(e.id); ip_set_put_byindex(map->net, e.id);
return ip_set_eexist(ret, flags) ? 0 : ret; return ip_set_eexist(ret, flags) ? 0 : ret;
} }
@ -454,9 +439,10 @@ list_set_flush(struct ip_set *set)
u32 i; u32 i;
for (i = 0; i < map->size; i++) { for (i = 0; i < map->size; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id != IPSET_INVALID_ID) { if (e->id != IPSET_INVALID_ID) {
ip_set_put_byindex(e->id); ip_set_put_byindex(map->net, e->id);
ip_set_ext_destroy(set, e);
e->id = IPSET_INVALID_ID; e->id = IPSET_INVALID_ID;
} }
} }
@ -485,14 +471,11 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
if (!nested) if (!nested)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) || if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
(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_REFERENCES, htonl(set->ref - 1)) ||
nla_put_net32(skb, IPSET_ATTR_MEMSIZE, nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
htonl(sizeof(*map) + map->size * map->dsize))) htonl(sizeof(*map) + map->size * set->dsize)))
goto nla_put_failure;
if (unlikely(ip_set_put_flags(skb, set)))
goto nla_put_failure; goto nla_put_failure;
ipset_nest_end(skb, nested); ipset_nest_end(skb, nested);
@ -515,11 +498,11 @@ list_set_list(const struct ip_set *set,
return -EMSGSIZE; return -EMSGSIZE;
for (; cb->args[2] < map->size; cb->args[2]++) { for (; cb->args[2] < map->size; cb->args[2]++) {
i = cb->args[2]; i = cb->args[2];
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
if (e->id == IPSET_INVALID_ID) if (e->id == IPSET_INVALID_ID)
goto finish; goto finish;
if (SET_WITH_TIMEOUT(set) && if (SET_WITH_TIMEOUT(set) &&
ip_set_timeout_expired(ext_timeout(e, map))) ip_set_timeout_expired(ext_timeout(e, set)))
continue; continue;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA); nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested) { if (!nested) {
@ -530,15 +513,9 @@ list_set_list(const struct ip_set *set,
goto nla_put_failure; goto nla_put_failure;
} }
if (nla_put_string(skb, IPSET_ATTR_NAME, if (nla_put_string(skb, IPSET_ATTR_NAME,
ip_set_name_byindex(e->id))) ip_set_name_byindex(map->net, e->id)))
goto nla_put_failure; goto nla_put_failure;
if (SET_WITH_TIMEOUT(set) && if (ip_set_put_extensions(skb, set, e, true))
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; goto nla_put_failure;
ipset_nest_end(skb, nested); ipset_nest_end(skb, nested);
} }
@ -550,11 +527,11 @@ finish:
nla_put_failure: nla_put_failure:
nla_nest_cancel(skb, nested); nla_nest_cancel(skb, nested);
ipset_nest_end(skb, atd);
if (unlikely(i == first)) { if (unlikely(i == first)) {
cb->args[2] = 0; cb->args[2] = 0;
return -EMSGSIZE; return -EMSGSIZE;
} }
ipset_nest_end(skb, atd);
return 0; return 0;
} }
@ -565,7 +542,7 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b)
const struct list_set *y = b->data; const struct list_set *y = b->data;
return x->size == y->size && return x->size == y->size &&
x->timeout == y->timeout && a->timeout == b->timeout &&
a->extensions == b->extensions; a->extensions == b->extensions;
} }
@ -594,7 +571,7 @@ list_set_gc(unsigned long ul_set)
set_cleanup_entries(set); set_cleanup_entries(set);
write_unlock_bh(&set->lock); write_unlock_bh(&set->lock);
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
add_timer(&map->gc); add_timer(&map->gc);
} }
@ -606,43 +583,40 @@ list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
init_timer(&map->gc); init_timer(&map->gc);
map->gc.data = (unsigned long) set; map->gc.data = (unsigned long) set;
map->gc.function = gc; map->gc.function = gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
add_timer(&map->gc); add_timer(&map->gc);
} }
/* Create list:set type of sets */ /* Create list:set type of sets */
static struct list_set * static bool
init_list_set(struct ip_set *set, u32 size, size_t dsize, init_list_set(struct net *net, struct ip_set *set, u32 size)
unsigned long timeout)
{ {
struct list_set *map; struct list_set *map;
struct set_elem *e; struct set_elem *e;
u32 i; u32 i;
map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); map = kzalloc(sizeof(*map) + size * set->dsize, GFP_KERNEL);
if (!map) if (!map)
return NULL; return false;
map->size = size; map->size = size;
map->dsize = dsize; map->net = net;
map->timeout = timeout;
set->data = map; set->data = map;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
e = list_set_elem(map, i); e = list_set_elem(set, map, i);
e->id = IPSET_INVALID_ID; e->id = IPSET_INVALID_ID;
} }
return map; return true;
} }
static int static int
list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
u32 flags)
{ {
struct list_set *map; u32 size = IP_SET_LIST_DEFAULT_SIZE;
u32 size = IP_SET_LIST_DEFAULT_SIZE, cadt_flags = 0;
unsigned long timeout = 0;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || 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) ||
@ -654,45 +628,13 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (size < IP_SET_LIST_MIN_SIZE) if (size < IP_SET_LIST_MIN_SIZE)
size = IP_SET_LIST_MIN_SIZE; size = IP_SET_LIST_MIN_SIZE;
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; set->variant = &set_variant;
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem));
set->extensions |= IPSET_EXT_COUNTER; if (!init_list_set(net, set, size))
if (tb[IPSET_ATTR_TIMEOUT]) { return -ENOMEM;
map = init_list_set(set, size, if (tb[IPSET_ATTR_TIMEOUT]) {
sizeof(struct setct_elem), timeout); set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_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;
set->extensions |= IPSET_EXT_TIMEOUT;
map->offset[IPSET_OFFSET_TIMEOUT] =
offsetof(struct sett_elem, timeout);
list_set_gc_init(set, list_set_gc); list_set_gc_init(set, list_set_gc);
} else {
map = init_list_set(set, size, sizeof(struct set_elem), 0);
if (!map)
return -ENOMEM;
} }
return 0; return 0;
} }
@ -703,8 +645,8 @@ static struct ip_set_type list_set_type __read_mostly = {
.features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
.dimension = IPSET_DIM_ONE, .dimension = IPSET_DIM_ONE,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.revision_min = REVISION_MIN, .revision_min = IPSET_TYPE_REV_MIN,
.revision_max = REVISION_MAX, .revision_max = IPSET_TYPE_REV_MAX,
.create = list_set_create, .create = list_set_create,
.create_policy = { .create_policy = {
[IPSET_ATTR_SIZE] = { .type = NLA_U32 }, [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
@ -721,6 +663,7 @@ static struct ip_set_type list_set_type __read_mostly = {
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
[IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
[IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
}, },
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

View file

@ -52,66 +52,8 @@ module_param(sip_direct_media, int, 0600);
MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling " MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling "
"endpoints only (default 1)"); "endpoints only (default 1)");
unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff, const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
unsigned int dataoff, const char **dptr, EXPORT_SYMBOL_GPL(nf_nat_sip_hooks);
unsigned int *datalen) __read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff,
s16 off) __read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook);
unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
unsigned int protoff,
unsigned int dataoff,
const char **dptr,
unsigned int *datalen,
struct nf_conntrack_expect *exp,
unsigned int matchoff,
unsigned int matchlen) __read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook);
unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff,
unsigned int dataoff,
const char **dptr,
unsigned int *datalen,
unsigned int sdpoff,
enum sdp_header_types type,
enum sdp_header_types term,
const union nf_inet_addr *addr)
__read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook);
unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff,
unsigned int dataoff,
const char **dptr,
unsigned int *datalen,
unsigned int matchoff,
unsigned int matchlen,
u_int16_t port) __read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook);
unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
unsigned int protoff,
unsigned int dataoff,
const char **dptr,
unsigned int *datalen,
unsigned int sdpoff,
const union nf_inet_addr *addr)
__read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook);
unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff,
unsigned int dataoff,
const char **dptr,
unsigned int *datalen,
struct nf_conntrack_expect *rtp_exp,
struct nf_conntrack_expect *rtcp_exp,
unsigned int mediaoff,
unsigned int medialen,
union nf_inet_addr *rtp_addr)
__read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook);
static int string_len(const struct nf_conn *ct, const char *dptr, static int string_len(const struct nf_conn *ct, const char *dptr,
const char *limit, int *shift) const char *limit, int *shift)
@ -914,8 +856,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
int direct_rtp = 0, skip_expect = 0, ret = NF_DROP; int direct_rtp = 0, skip_expect = 0, ret = NF_DROP;
u_int16_t base_port; u_int16_t base_port;
__be16 rtp_port, rtcp_port; __be16 rtp_port, rtcp_port;
typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port; const struct nf_nat_sip_hooks *hooks;
typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media;
saddr = NULL; saddr = NULL;
if (sip_direct_media) { if (sip_direct_media) {
@ -966,22 +907,23 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
#endif #endif
skip_expect = 1; skip_expect = 1;
} while (!skip_expect); } while (!skip_expect);
rcu_read_unlock();
base_port = ntohs(tuple.dst.u.udp.port) & ~1; base_port = ntohs(tuple.dst.u.udp.port) & ~1;
rtp_port = htons(base_port); rtp_port = htons(base_port);
rtcp_port = htons(base_port + 1); rtcp_port = htons(base_port + 1);
if (direct_rtp) { if (direct_rtp) {
nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook); hooks = rcu_dereference(nf_nat_sip_hooks);
if (nf_nat_sdp_port && if (hooks &&
!nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, !hooks->sdp_port(skb, protoff, dataoff, dptr, datalen,
mediaoff, medialen, ntohs(rtp_port))) mediaoff, medialen, ntohs(rtp_port)))
goto err1; goto err1;
} }
if (skip_expect) if (skip_expect) {
rcu_read_unlock();
return NF_ACCEPT; return NF_ACCEPT;
}
rtp_exp = nf_ct_expect_alloc(ct); rtp_exp = nf_ct_expect_alloc(ct);
if (rtp_exp == NULL) if (rtp_exp == NULL)
@ -995,10 +937,10 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr, nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr,
IPPROTO_UDP, NULL, &rtcp_port); IPPROTO_UDP, NULL, &rtcp_port);
nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); hooks = rcu_dereference(nf_nat_sip_hooks);
if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp) if (hooks && ct->status & IPS_NAT_MASK && !direct_rtp)
ret = nf_nat_sdp_media(skb, protoff, dataoff, dptr, datalen, ret = hooks->sdp_media(skb, protoff, dataoff, dptr,
rtp_exp, rtcp_exp, datalen, rtp_exp, rtcp_exp,
mediaoff, medialen, daddr); mediaoff, medialen, daddr);
else { else {
if (nf_ct_expect_related(rtp_exp) == 0) { if (nf_ct_expect_related(rtp_exp) == 0) {
@ -1012,6 +954,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
err2: err2:
nf_ct_expect_put(rtp_exp); nf_ct_expect_put(rtp_exp);
err1: err1:
rcu_read_unlock();
return ret; return ret;
} }
@ -1051,13 +994,12 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
unsigned int caddr_len, maddr_len; unsigned int caddr_len, maddr_len;
unsigned int i; unsigned int i;
union nf_inet_addr caddr, maddr, rtp_addr; union nf_inet_addr caddr, maddr, rtp_addr;
const struct nf_nat_sip_hooks *hooks;
unsigned int port; unsigned int port;
const struct sdp_media_type *t; const struct sdp_media_type *t;
int ret = NF_ACCEPT; int ret = NF_ACCEPT;
typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr;
typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session;
nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); hooks = rcu_dereference(nf_nat_sip_hooks);
/* Find beginning of session description */ /* Find beginning of session description */
if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
@ -1125,10 +1067,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
} }
/* Update media connection address if present */ /* Update media connection address if present */
if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { if (maddr_len && hooks && ct->status & IPS_NAT_MASK) {
ret = nf_nat_sdp_addr(skb, protoff, dataoff, ret = hooks->sdp_addr(skb, protoff, dataoff,
dptr, datalen, mediaoff, dptr, datalen, mediaoff,
SDP_HDR_CONNECTION, SDP_HDR_MEDIA, SDP_HDR_CONNECTION,
SDP_HDR_MEDIA,
&rtp_addr); &rtp_addr);
if (ret != NF_ACCEPT) { if (ret != NF_ACCEPT) {
nf_ct_helper_log(skb, ct, "cannot mangle SDP"); nf_ct_helper_log(skb, ct, "cannot mangle SDP");
@ -1139,10 +1082,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
} }
/* Update session connection and owner addresses */ /* Update session connection and owner addresses */
nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook); hooks = rcu_dereference(nf_nat_sip_hooks);
if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK) if (hooks && ct->status & IPS_NAT_MASK)
ret = nf_nat_sdp_session(skb, protoff, dataoff, ret = hooks->sdp_session(skb, protoff, dataoff,
dptr, datalen, sdpoff, &rtp_addr); dptr, datalen, sdpoff,
&rtp_addr);
return ret; return ret;
} }
@ -1242,11 +1186,11 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
unsigned int matchoff, matchlen; unsigned int matchoff, matchlen;
struct nf_conntrack_expect *exp; struct nf_conntrack_expect *exp;
union nf_inet_addr *saddr, daddr; union nf_inet_addr *saddr, daddr;
const struct nf_nat_sip_hooks *hooks;
__be16 port; __be16 port;
u8 proto; u8 proto;
unsigned int expires = 0; unsigned int expires = 0;
int ret; int ret;
typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect;
/* Expected connections can not register again. */ /* Expected connections can not register again. */
if (ct->status & IPS_EXPECTED) if (ct->status & IPS_EXPECTED)
@ -1309,10 +1253,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
exp->helper = nfct_help(ct)->helper; exp->helper = nfct_help(ct)->helper;
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook); hooks = rcu_dereference(nf_nat_sip_hooks);
if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK) if (hooks && ct->status & IPS_NAT_MASK)
ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen, ret = hooks->expect(skb, protoff, dataoff, dptr, datalen,
exp, matchoff, matchlen); exp, matchoff, matchlen);
else { else {
if (nf_ct_expect_related(exp) != 0) { if (nf_ct_expect_related(exp) != 0) {
nf_ct_helper_log(skb, ct, "cannot add expectation"); nf_ct_helper_log(skb, ct, "cannot add expectation");
@ -1515,7 +1459,7 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct,
unsigned int protoff, unsigned int dataoff, unsigned int protoff, unsigned int dataoff,
const char **dptr, unsigned int *datalen) const char **dptr, unsigned int *datalen)
{ {
typeof(nf_nat_sip_hook) nf_nat_sip; const struct nf_nat_sip_hooks *hooks;
int ret; int ret;
if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0) if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
@ -1524,9 +1468,9 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct,
ret = process_sip_response(skb, protoff, dataoff, dptr, datalen); ret = process_sip_response(skb, protoff, dataoff, dptr, datalen);
if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
nf_nat_sip = rcu_dereference(nf_nat_sip_hook); hooks = rcu_dereference(nf_nat_sip_hooks);
if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff, if (hooks && !hooks->msg(skb, protoff, dataoff,
dptr, datalen)) { dptr, datalen)) {
nf_ct_helper_log(skb, ct, "cannot NAT SIP message"); nf_ct_helper_log(skb, ct, "cannot NAT SIP message");
ret = NF_DROP; ret = NF_DROP;
} }
@ -1546,7 +1490,6 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
s16 diff, tdiff = 0; s16 diff, tdiff = 0;
int ret = NF_ACCEPT; int ret = NF_ACCEPT;
bool term; bool term;
typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust;
if (ctinfo != IP_CT_ESTABLISHED && if (ctinfo != IP_CT_ESTABLISHED &&
ctinfo != IP_CT_ESTABLISHED_REPLY) ctinfo != IP_CT_ESTABLISHED_REPLY)
@ -1610,9 +1553,11 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
} }
if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
nf_nat_sip_seq_adjust = rcu_dereference(nf_nat_sip_seq_adjust_hook); const struct nf_nat_sip_hooks *hooks;
if (nf_nat_sip_seq_adjust)
nf_nat_sip_seq_adjust(skb, protoff, tdiff); hooks = rcu_dereference(nf_nat_sip_hooks);
if (hooks)
hooks->seq_adjust(skb, protoff, tdiff);
} }
return ret; return ret;

View file

@ -625,33 +625,26 @@ static struct nf_ct_helper_expectfn sip_nat = {
static void __exit nf_nat_sip_fini(void) static void __exit nf_nat_sip_fini(void)
{ {
RCU_INIT_POINTER(nf_nat_sip_hook, NULL); RCU_INIT_POINTER(nf_nat_sip_hooks, NULL);
RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL);
RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL);
RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL);
RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL);
RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL);
RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL);
nf_ct_helper_expectfn_unregister(&sip_nat); nf_ct_helper_expectfn_unregister(&sip_nat);
synchronize_rcu(); synchronize_rcu();
} }
static const struct nf_nat_sip_hooks sip_hooks = {
.msg = nf_nat_sip,
.seq_adjust = nf_nat_sip_seq_adjust,
.expect = nf_nat_sip_expect,
.sdp_addr = nf_nat_sdp_addr,
.sdp_port = nf_nat_sdp_port,
.sdp_session = nf_nat_sdp_session,
.sdp_media = nf_nat_sdp_media,
};
static int __init nf_nat_sip_init(void) static int __init nf_nat_sip_init(void)
{ {
BUG_ON(nf_nat_sip_hook != NULL); BUG_ON(nf_nat_sip_hooks != NULL);
BUG_ON(nf_nat_sip_seq_adjust_hook != NULL); RCU_INIT_POINTER(nf_nat_sip_hooks, &sip_hooks);
BUG_ON(nf_nat_sip_expect_hook != NULL);
BUG_ON(nf_nat_sdp_addr_hook != NULL);
BUG_ON(nf_nat_sdp_port_hook != NULL);
BUG_ON(nf_nat_sdp_session_hook != NULL);
BUG_ON(nf_nat_sdp_media_hook != NULL);
RCU_INIT_POINTER(nf_nat_sip_hook, nf_nat_sip);
RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, nf_nat_sip_seq_adjust);
RCU_INIT_POINTER(nf_nat_sip_expect_hook, nf_nat_sip_expect);
RCU_INIT_POINTER(nf_nat_sdp_addr_hook, nf_nat_sdp_addr);
RCU_INIT_POINTER(nf_nat_sdp_port_hook, nf_nat_sdp_port);
RCU_INIT_POINTER(nf_nat_sdp_session_hook, nf_nat_sdp_session);
RCU_INIT_POINTER(nf_nat_sdp_media_hook, nf_nat_sdp_media);
nf_ct_helper_expectfn_register(&sip_nat); nf_ct_helper_expectfn_register(&sip_nat);
return 0; return 0;
} }

View file

@ -49,10 +49,8 @@ static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
}; };
static int static int
ctnl_timeout_parse_policy(struct ctnl_timeout *timeout, ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto,
struct nf_conntrack_l4proto *l4proto, struct net *net, const struct nlattr *attr)
struct net *net,
const struct nlattr *attr)
{ {
int ret = 0; int ret = 0;
@ -64,8 +62,7 @@ ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeouts);
&timeout->data);
} }
return ret; return ret;
} }
@ -123,7 +120,8 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
goto err_proto_put; goto err_proto_put;
} }
ret = ctnl_timeout_parse_policy(matching, l4proto, net, ret = ctnl_timeout_parse_policy(&matching->data,
l4proto, net,
cda[CTA_TIMEOUT_DATA]); cda[CTA_TIMEOUT_DATA]);
return ret; return ret;
} }
@ -138,7 +136,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
goto err_proto_put; goto err_proto_put;
} }
ret = ctnl_timeout_parse_policy(timeout, l4proto, net, ret = ctnl_timeout_parse_policy(&timeout->data, l4proto, net,
cda[CTA_TIMEOUT_DATA]); cda[CTA_TIMEOUT_DATA]);
if (ret < 0) if (ret < 0)
goto err; goto err;
@ -342,6 +340,147 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
return ret; return ret;
} }
static int
cttimeout_default_set(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
struct net *net = sock_net(skb->sk);
unsigned int *timeouts;
int ret;
if (!cda[CTA_TIMEOUT_L3PROTO] ||
!cda[CTA_TIMEOUT_L4PROTO] ||
!cda[CTA_TIMEOUT_DATA])
return -EINVAL;
l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
l4proto = nf_ct_l4proto_find_get(l3num, l4num);
/* This protocol is not supported, skip. */
if (l4proto->l4proto != l4num) {
ret = -EOPNOTSUPP;
goto err;
}
timeouts = l4proto->get_timeouts(net);
ret = ctnl_timeout_parse_policy(timeouts, l4proto, net,
cda[CTA_TIMEOUT_DATA]);
if (ret < 0)
goto err;
nf_ct_l4proto_put(l4proto);
return 0;
err:
nf_ct_l4proto_put(l4proto);
return ret;
}
static int
cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid,
u32 seq, u32 type, int event,
struct nf_conntrack_l4proto *l4proto)
{
struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg;
unsigned int flags = portid ? NLM_F_MULTI : 0;
event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
if (nlh == NULL)
goto nlmsg_failure;
nfmsg = nlmsg_data(nlh);
nfmsg->nfgen_family = AF_UNSPEC;
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = 0;
if (nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(l4proto->l3proto)) ||
nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto))
goto nla_put_failure;
if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) {
struct nlattr *nest_parms;
unsigned int *timeouts = l4proto->get_timeouts(net);
int ret;
nest_parms = nla_nest_start(skb,
CTA_TIMEOUT_DATA | NLA_F_NESTED);
if (!nest_parms)
goto nla_put_failure;
ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, timeouts);
if (ret < 0)
goto nla_put_failure;
nla_nest_end(skb, nest_parms);
}
nlmsg_end(skb, nlh);
return skb->len;
nlmsg_failure:
nla_put_failure:
nlmsg_cancel(skb, nlh);
return -1;
}
static int cttimeout_default_get(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
struct net *net = sock_net(skb->sk);
struct sk_buff *skb2;
int ret, err;
if (!cda[CTA_TIMEOUT_L3PROTO] || !cda[CTA_TIMEOUT_L4PROTO])
return -EINVAL;
l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
l4proto = nf_ct_l4proto_find_get(l3num, l4num);
/* This protocol is not supported, skip. */
if (l4proto->l4proto != l4num) {
err = -EOPNOTSUPP;
goto err;
}
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (skb2 == NULL) {
err = -ENOMEM;
goto err;
}
ret = cttimeout_default_fill_info(net, skb2, NETLINK_CB(skb).portid,
nlh->nlmsg_seq,
NFNL_MSG_TYPE(nlh->nlmsg_type),
IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
l4proto);
if (ret <= 0) {
kfree_skb(skb2);
err = -ENOMEM;
goto err;
}
ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
if (ret > 0)
ret = 0;
/* this avoids a loop in nfnetlink. */
return ret == -EAGAIN ? -ENOBUFS : ret;
err:
nf_ct_l4proto_put(l4proto);
return err;
}
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
static struct ctnl_timeout *ctnl_timeout_find_get(const char *name) static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
{ {
@ -384,6 +523,12 @@ static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
[IPCTNL_MSG_TIMEOUT_DELETE] = { .call = cttimeout_del_timeout, [IPCTNL_MSG_TIMEOUT_DELETE] = { .call = cttimeout_del_timeout,
.attr_count = CTA_TIMEOUT_MAX, .attr_count = CTA_TIMEOUT_MAX,
.policy = cttimeout_nla_policy }, .policy = cttimeout_nla_policy },
[IPCTNL_MSG_TIMEOUT_DEFAULT_SET]= { .call = cttimeout_default_set,
.attr_count = CTA_TIMEOUT_MAX,
.policy = cttimeout_nla_policy },
[IPCTNL_MSG_TIMEOUT_DEFAULT_GET]= { .call = cttimeout_default_get,
.attr_count = CTA_TIMEOUT_MAX,
.policy = cttimeout_nla_policy },
}; };
static const struct nfnetlink_subsystem cttimeout_subsys = { static const struct nfnetlink_subsystem cttimeout_subsys = {

View file

@ -319,7 +319,8 @@ nfulnl_set_flags(struct nfulnl_instance *inst, u_int16_t flags)
} }
static struct sk_buff * static struct sk_buff *
nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size) nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size,
unsigned int pkt_size)
{ {
struct sk_buff *skb; struct sk_buff *skb;
unsigned int n; unsigned int n;
@ -328,13 +329,13 @@ nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size)
* message. WARNING: has to be <= 128k due to slab restrictions */ * message. WARNING: has to be <= 128k due to slab restrictions */
n = max(inst_size, pkt_size); n = max(inst_size, pkt_size);
skb = nfnetlink_alloc_skb(&init_net, n, peer_portid, GFP_ATOMIC); skb = nfnetlink_alloc_skb(net, n, peer_portid, GFP_ATOMIC);
if (!skb) { if (!skb) {
if (n > pkt_size) { if (n > pkt_size) {
/* try to allocate only as much as we need for current /* try to allocate only as much as we need for current
* packet */ * packet */
skb = nfnetlink_alloc_skb(&init_net, pkt_size, skb = nfnetlink_alloc_skb(net, pkt_size,
peer_portid, GFP_ATOMIC); peer_portid, GFP_ATOMIC);
if (!skb) if (!skb)
pr_err("nfnetlink_log: can't even alloc %u bytes\n", pr_err("nfnetlink_log: can't even alloc %u bytes\n",
@ -702,8 +703,8 @@ nfulnl_log_packet(struct net *net,
} }
if (!inst->skb) { if (!inst->skb) {
inst->skb = nfulnl_alloc_skb(inst->peer_portid, inst->nlbufsiz, inst->skb = nfulnl_alloc_skb(net, inst->peer_portid,
size); inst->nlbufsiz, size);
if (!inst->skb) if (!inst->skb)
goto alloc_failure; goto alloc_failure;
} }

View file

@ -298,7 +298,7 @@ nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
} }
static struct sk_buff * static struct sk_buff *
nfqnl_build_packet_message(struct nfqnl_instance *queue, nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
struct nf_queue_entry *entry, struct nf_queue_entry *entry,
__be32 **packet_id_ptr) __be32 **packet_id_ptr)
{ {
@ -372,7 +372,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
if (queue->flags & NFQA_CFG_F_CONNTRACK) if (queue->flags & NFQA_CFG_F_CONNTRACK)
ct = nfqnl_ct_get(entskb, &size, &ctinfo); ct = nfqnl_ct_get(entskb, &size, &ctinfo);
skb = nfnetlink_alloc_skb(&init_net, size, queue->peer_portid, skb = nfnetlink_alloc_skb(net, size, queue->peer_portid,
GFP_ATOMIC); GFP_ATOMIC);
if (!skb) if (!skb)
return NULL; return NULL;
@ -525,7 +525,7 @@ __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue,
__be32 *packet_id_ptr; __be32 *packet_id_ptr;
int failopen = 0; int failopen = 0;
nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr); nskb = nfqnl_build_packet_message(net, queue, entry, &packet_id_ptr);
if (nskb == NULL) { if (nskb == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err_out; goto err_out;

View file

@ -43,10 +43,42 @@ optlen(const u_int8_t *opt, unsigned int offset)
return opt[offset+1]; return opt[offset+1];
} }
static u_int32_t tcpmss_reverse_mtu(struct net *net,
const struct sk_buff *skb,
unsigned int family)
{
struct flowi fl;
const struct nf_afinfo *ai;
struct rtable *rt = NULL;
u_int32_t mtu = ~0U;
if (family == PF_INET) {
struct flowi4 *fl4 = &fl.u.ip4;
memset(fl4, 0, sizeof(*fl4));
fl4->daddr = ip_hdr(skb)->saddr;
} else {
struct flowi6 *fl6 = &fl.u.ip6;
memset(fl6, 0, sizeof(*fl6));
fl6->daddr = ipv6_hdr(skb)->saddr;
}
rcu_read_lock();
ai = nf_get_afinfo(family);
if (ai != NULL)
ai->route(net, (struct dst_entry **)&rt, &fl, false);
rcu_read_unlock();
if (rt != NULL) {
mtu = dst_mtu(&rt->dst);
dst_release(&rt->dst);
}
return mtu;
}
static int static int
tcpmss_mangle_packet(struct sk_buff *skb, tcpmss_mangle_packet(struct sk_buff *skb,
const struct xt_action_param *par, const struct xt_action_param *par,
unsigned int in_mtu, unsigned int family,
unsigned int tcphoff, unsigned int tcphoff,
unsigned int minlen) unsigned int minlen)
{ {
@ -76,6 +108,9 @@ tcpmss_mangle_packet(struct sk_buff *skb,
return -1; return -1;
if (info->mss == XT_TCPMSS_CLAMP_PMTU) { if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
struct net *net = dev_net(par->in ? par->in : par->out);
unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family);
if (dst_mtu(skb_dst(skb)) <= minlen) { if (dst_mtu(skb_dst(skb)) <= minlen) {
net_err_ratelimited("unknown or invalid path-MTU (%u)\n", net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
dst_mtu(skb_dst(skb))); dst_mtu(skb_dst(skb)));
@ -165,37 +200,6 @@ tcpmss_mangle_packet(struct sk_buff *skb,
return TCPOLEN_MSS; return TCPOLEN_MSS;
} }
static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
unsigned int family)
{
struct flowi fl;
const struct nf_afinfo *ai;
struct rtable *rt = NULL;
u_int32_t mtu = ~0U;
if (family == PF_INET) {
struct flowi4 *fl4 = &fl.u.ip4;
memset(fl4, 0, sizeof(*fl4));
fl4->daddr = ip_hdr(skb)->saddr;
} else {
struct flowi6 *fl6 = &fl.u.ip6;
memset(fl6, 0, sizeof(*fl6));
fl6->daddr = ipv6_hdr(skb)->saddr;
}
rcu_read_lock();
ai = nf_get_afinfo(family);
if (ai != NULL)
ai->route(&init_net, (struct dst_entry **)&rt, &fl, false);
rcu_read_unlock();
if (rt != NULL) {
mtu = dst_mtu(&rt->dst);
dst_release(&rt->dst);
}
return mtu;
}
static unsigned int static unsigned int
tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
{ {
@ -204,7 +208,7 @@ tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
int ret; int ret;
ret = tcpmss_mangle_packet(skb, par, ret = tcpmss_mangle_packet(skb, par,
tcpmss_reverse_mtu(skb, PF_INET), PF_INET,
iph->ihl * 4, iph->ihl * 4,
sizeof(*iph) + sizeof(struct tcphdr)); sizeof(*iph) + sizeof(struct tcphdr));
if (ret < 0) if (ret < 0)
@ -233,7 +237,7 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
if (tcphoff < 0) if (tcphoff < 0)
return NF_DROP; return NF_DROP;
ret = tcpmss_mangle_packet(skb, par, ret = tcpmss_mangle_packet(skb, par,
tcpmss_reverse_mtu(skb, PF_INET6), PF_INET6,
tcphoff, tcphoff,
sizeof(*ipv6h) + sizeof(struct tcphdr)); sizeof(*ipv6h) + sizeof(struct tcphdr));
if (ret < 0) if (ret < 0)

View file

@ -81,7 +81,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
struct xt_set_info_match_v0 *info = par->matchinfo; struct xt_set_info_match_v0 *info = par->matchinfo;
ip_set_id_t index; ip_set_id_t index;
index = ip_set_nfnl_get_byindex(info->match_set.index); index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match\n", pr_warning("Cannot find set indentified by id %u to match\n",
@ -91,7 +91,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: set match dimension " pr_warning("Protocol error: set match dimension "
"is over the limit!\n"); "is over the limit!\n");
ip_set_nfnl_put(info->match_set.index); ip_set_nfnl_put(par->net, info->match_set.index);
return -ERANGE; return -ERANGE;
} }
@ -106,81 +106,10 @@ set_match_v0_destroy(const struct xt_mtdtor_param *par)
{ {
struct xt_set_info_match_v0 *info = par->matchinfo; struct xt_set_info_match_v0 *info = par->matchinfo;
ip_set_nfnl_put(info->match_set.index); ip_set_nfnl_put(par->net, info->match_set.index);
} }
static unsigned int /* Revision 1 match */
set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_set_info_target_v0 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
info->add_set.u.compat.flags, 0, UINT_MAX);
ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
info->del_set.u.compat.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
static int
set_target_v0_checkentry(const struct xt_tgchk_param *par)
{
struct xt_set_info_target_v0 *info = par->targinfo;
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
return -ENOENT;
}
}
if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
return -ENOENT;
}
}
if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
return -ERANGE;
}
/* Fill out compatibility data */
compat_flags(&info->add_set);
compat_flags(&info->del_set);
return 0;
}
static void
set_target_v0_destroy(const struct xt_tgdtor_param *par)
{
const struct xt_set_info_target_v0 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
}
/* Revision 1 match and target */
static bool static bool
set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
@ -202,7 +131,7 @@ set_match_v1_checkentry(const struct xt_mtchk_param *par)
struct xt_set_info_match_v1 *info = par->matchinfo; struct xt_set_info_match_v1 *info = par->matchinfo;
ip_set_id_t index; ip_set_id_t index;
index = ip_set_nfnl_get_byindex(info->match_set.index); index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match\n", pr_warning("Cannot find set indentified by id %u to match\n",
@ -212,7 +141,7 @@ set_match_v1_checkentry(const struct xt_mtchk_param *par)
if (info->match_set.dim > IPSET_DIM_MAX) { if (info->match_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: set match dimension " pr_warning("Protocol error: set match dimension "
"is over the limit!\n"); "is over the limit!\n");
ip_set_nfnl_put(info->match_set.index); ip_set_nfnl_put(par->net, info->match_set.index);
return -ERANGE; return -ERANGE;
} }
@ -224,102 +153,9 @@ set_match_v1_destroy(const struct xt_mtdtor_param *par)
{ {
struct xt_set_info_match_v1 *info = par->matchinfo; struct xt_set_info_match_v1 *info = par->matchinfo;
ip_set_nfnl_put(info->match_set.index); ip_set_nfnl_put(par->net, info->match_set.index);
} }
static unsigned int
set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_set_info_target_v1 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.dim,
info->add_set.flags, 0, UINT_MAX);
ADT_OPT(del_opt, par->family, info->del_set.dim,
info->del_set.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
static int
set_target_v1_checkentry(const struct xt_tgchk_param *par)
{
const struct xt_set_info_target_v1 *info = par->targinfo;
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
return -ENOENT;
}
}
if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
return -ENOENT;
}
}
if (info->add_set.dim > IPSET_DIM_MAX ||
info->del_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
return -ERANGE;
}
return 0;
}
static void
set_target_v1_destroy(const struct xt_tgdtor_param *par)
{
const struct xt_set_info_target_v1 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
}
/* Revision 2 target */
static unsigned int
set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_set_info_target_v2 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.dim,
info->add_set.flags, info->flags, info->timeout);
ADT_OPT(del_opt, par->family, info->del_set.dim,
info->del_set.flags, 0, UINT_MAX);
/* Normalize to fit into jiffies */
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)
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
#define set_target_v2_checkentry set_target_v1_checkentry
#define set_target_v2_destroy set_target_v1_destroy
/* Revision 3 match */ /* Revision 3 match */
static bool static bool
@ -366,6 +202,174 @@ set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
#define set_match_v3_checkentry set_match_v1_checkentry #define set_match_v3_checkentry set_match_v1_checkentry
#define set_match_v3_destroy set_match_v1_destroy #define set_match_v3_destroy set_match_v1_destroy
/* Revision 0 interface: backward compatible with netfilter/iptables */
static unsigned int
set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_set_info_target_v0 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
info->add_set.u.compat.flags, 0, UINT_MAX);
ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
info->del_set.u.compat.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
static int
set_target_v0_checkentry(const struct xt_tgchk_param *par)
{
struct xt_set_info_target_v0 *info = par->targinfo;
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
return -ENOENT;
}
}
if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->add_set.index);
return -ENOENT;
}
}
if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->del_set.index);
return -ERANGE;
}
/* Fill out compatibility data */
compat_flags(&info->add_set);
compat_flags(&info->del_set);
return 0;
}
static void
set_target_v0_destroy(const struct xt_tgdtor_param *par)
{
const struct xt_set_info_target_v0 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->del_set.index);
}
/* Revision 1 target */
static unsigned int
set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_set_info_target_v1 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.dim,
info->add_set.flags, 0, UINT_MAX);
ADT_OPT(del_opt, par->family, info->del_set.dim,
info->del_set.flags, 0, UINT_MAX);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_add(info->add_set.index, skb, par, &add_opt);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
static int
set_target_v1_checkentry(const struct xt_tgchk_param *par)
{
const struct xt_set_info_target_v1 *info = par->targinfo;
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
return -ENOENT;
}
}
if (info->del_set.index != IPSET_INVALID_ID) {
index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->add_set.index);
return -ENOENT;
}
}
if (info->add_set.dim > IPSET_DIM_MAX ||
info->del_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->del_set.index);
return -ERANGE;
}
return 0;
}
static void
set_target_v1_destroy(const struct xt_tgdtor_param *par)
{
const struct xt_set_info_target_v1 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(par->net, info->del_set.index);
}
/* Revision 2 target */
static unsigned int
set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_set_info_target_v2 *info = par->targinfo;
ADT_OPT(add_opt, par->family, info->add_set.dim,
info->add_set.flags, info->flags, info->timeout);
ADT_OPT(del_opt, par->family, info->del_set.dim,
info->del_set.flags, 0, UINT_MAX);
/* Normalize to fit into jiffies */
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)
ip_set_del(info->del_set.index, skb, par, &del_opt);
return XT_CONTINUE;
}
#define set_target_v2_checkentry set_target_v1_checkentry
#define set_target_v2_destroy set_target_v1_destroy
static struct xt_match set_matches[] __read_mostly = { static struct xt_match set_matches[] __read_mostly = {
{ {
.name = "set", .name = "set",

View file

@ -24,11 +24,12 @@ static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len,
{ {
struct xt_set_info *set = data; struct xt_set_info *set = data;
ip_set_id_t index; ip_set_id_t index;
struct net *net = qdisc_dev(tp->q)->nd_net;
if (data_len != sizeof(*set)) if (data_len != sizeof(*set))
return -EINVAL; return -EINVAL;
index = ip_set_nfnl_get_byindex(set->index); index = ip_set_nfnl_get_byindex(net, set->index);
if (index == IPSET_INVALID_ID) if (index == IPSET_INVALID_ID)
return -ENOENT; return -ENOENT;
@ -37,7 +38,7 @@ static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len,
if (em->data) if (em->data)
return 0; return 0;
ip_set_nfnl_put(index); ip_set_nfnl_put(net, index);
return -ENOMEM; return -ENOMEM;
} }
@ -45,7 +46,7 @@ static void em_ipset_destroy(struct tcf_proto *p, struct tcf_ematch *em)
{ {
const struct xt_set_info *set = (const void *) em->data; const struct xt_set_info *set = (const void *) em->data;
if (set) { if (set) {
ip_set_nfnl_put(set->index); ip_set_nfnl_put(qdisc_dev(p->q)->nd_net, set->index);
kfree((void *) em->data); kfree((void *) em->data);
} }
} }