Commit 03c8b234 authored by Jozsef Kadlecsik's avatar Jozsef Kadlecsik
Browse files

netfilter: ipset: Generalize extensions support



Get rid of the structure based extensions and introduce a blob for
the extensions. Thus we can support more extension types easily.
Signed-off-by: default avatarJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
parent ca134ce8
......@@ -66,6 +66,17 @@ enum ip_set_ext_id {
IPSET_EXT_ID_MAX,
};
/* Extension type */
struct ip_set_ext_type {
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 {
u64 packets;
u64 bytes;
......@@ -283,6 +294,8 @@ extern void *ip_set_alloc(size_t size);
extern void ip_set_free(void *members);
extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr);
extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr);
extern 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[],
struct ip_set_ext *ext);
......
......@@ -208,25 +208,6 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
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"
/* Create bitmap:ip type of sets */
......@@ -263,7 +244,7 @@ static int
bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct bitmap_ip *map;
u32 first_ip = 0, last_ip = 0, hosts, cadt_flags = 0;
u32 first_ip = 0, last_ip = 0, hosts;
u64 elements;
u8 netmask = 32;
int ret;
......@@ -335,61 +316,15 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
map->memsize = bitmap_bytes(0, elements - 1);
set->variant = &bitmap_ip;
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
set->extensions |= IPSET_EXT_COUNTER;
if (tb[IPSET_ATTR_TIMEOUT]) {
set->dsize = sizeof(struct bitmap_ipct_elem);
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct bitmap_ipct_elem, timeout);
set->offset[IPSET_EXT_ID_COUNTER] =
offsetof(struct bitmap_ipct_elem, counter);
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
set->timeout = ip_set_timeout_uget(
tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ip_gc_init(set, bitmap_ip_gc);
} else {
set->dsize = sizeof(struct bitmap_ipc_elem);
set->offset[IPSET_EXT_ID_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]) {
set->dsize = sizeof(struct bitmap_ipt_elem);
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct bitmap_ipt_elem, timeout);
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
set->dsize = ip_set_elem_len(set, tb, 0);
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ip_gc_init(set, bitmap_ip_gc);
} else {
set->dsize = 0;
if (!init_map_ip(set, map, first_ip, last_ip,
elements, hosts, netmask)) {
kfree(map);
return -ENOMEM;
}
}
return 0;
}
......
......@@ -289,37 +289,6 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
/* 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"
/* Create bitmap:ip,mac type of sets */
......@@ -328,7 +297,7 @@ static bool
init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
u32 first_ip, u32 last_ip, u32 elements)
{
map->members = ip_set_alloc((last_ip - first_ip + 1) * set->dsize);
map->members = ip_set_alloc(map->memsize);
if (!map->members)
return false;
if (set->dsize) {
......@@ -353,7 +322,7 @@ static int
bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
u32 flags)
{
u32 first_ip = 0, last_ip = 0, cadt_flags = 0;
u32 first_ip = 0, last_ip = 0;
u64 elements;
struct bitmap_ipmac *map;
int ret;
......@@ -397,57 +366,15 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
map->memsize = bitmap_bytes(0, elements - 1);
set->variant = &bitmap_ipmac;
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
set->extensions |= IPSET_EXT_COUNTER;
if (tb[IPSET_ATTR_TIMEOUT]) {
set->dsize = sizeof(struct bitmap_ipmacct_elem);
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct bitmap_ipmacct_elem, timeout);
set->offset[IPSET_EXT_ID_COUNTER] =
offsetof(struct bitmap_ipmacct_elem, counter);
if (!init_map_ipmac(set, map, first_ip, last_ip,
elements)) {
kfree(map);
return -ENOMEM;
}
set->timeout = ip_set_timeout_uget(
tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
} else {
set->dsize = sizeof(struct bitmap_ipmacc_elem);
set->offset[IPSET_EXT_ID_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]) {
set->dsize = sizeof(struct bitmap_ipmact_elem);
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct bitmap_ipmact_elem, timeout);
if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
kfree(map);
return -ENOMEM;
}
set->dsize = ip_set_elem_len(set, tb,
sizeof(struct bitmap_ipmac_elem));
if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
kfree(map);
return -ENOMEM;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
} else {
set->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;
}
......
......@@ -198,25 +198,6 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
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"
/* Create bitmap:ip type of sets */
......@@ -250,7 +231,6 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
struct bitmap_port *map;
u16 first_port, last_port;
u32 cadt_flags = 0;
if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) ||
......@@ -274,53 +254,14 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
map->elements = last_port - first_port + 1;
map->memsize = map->elements * sizeof(unsigned long);
set->variant = &bitmap_port;
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
set->extensions |= IPSET_EXT_COUNTER;
if (tb[IPSET_ATTR_TIMEOUT]) {
set->dsize = sizeof(struct bitmap_portct_elem);
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct bitmap_portct_elem, timeout);
set->offset[IPSET_EXT_ID_COUNTER] =
offsetof(struct bitmap_portct_elem, counter);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
set->timeout =
ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_port_gc_init(set, bitmap_port_gc);
} else {
set->dsize = sizeof(struct bitmap_portc_elem);
set->offset[IPSET_EXT_ID_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]) {
set->dsize = sizeof(struct bitmap_portt_elem);
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct bitmap_portt_elem, timeout);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
set->dsize = ip_set_elem_len(set, tb, 0);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
if (tb[IPSET_ATTR_TIMEOUT]) {
set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
bitmap_port_gc_init(set, bitmap_port_gc);
} else {
set->dsize = 0;
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
}
}
return 0;
}
......
......@@ -315,6 +315,52 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr)
}
EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
/* 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),
},
};
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
ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
struct ip_set_ext *ext)
......
......@@ -960,7 +960,6 @@ static int
IPSET_TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
{
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u32 cadt_flags = 0;
u8 hbits;
#ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask;
......@@ -1034,88 +1033,23 @@ IPSET_TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
rcu_assign_pointer(h->table, t);
set->data = h;
if (set->family == NFPROTO_IPV4)
if (set->family == NFPROTO_IPV4) {
set->variant = &IPSET_TOKEN(HTYPE, 4_variant);
else
set->dsize = ip_set_elem_len(set, tb,
sizeof(struct IPSET_TOKEN(HTYPE, 4_elem)));
} else {
set->variant = &IPSET_TOKEN(HTYPE, 6_variant);
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]) {
set->timeout =
ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->extensions |= IPSET_EXT_TIMEOUT;
if (set->family == NFPROTO_IPV4) {
set->dsize = sizeof(struct
IPSET_TOKEN(HTYPE, 4ct_elem));
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct
IPSET_TOKEN(HTYPE, 4ct_elem),
timeout);
set->offset[IPSET_EXT_ID_COUNTER] =
offsetof(struct
IPSET_TOKEN(HTYPE, 4ct_elem),
counter);
IPSET_TOKEN(HTYPE, 4_gc_init)(set,
IPSET_TOKEN(HTYPE, 4_gc));
} else {
set->dsize = sizeof(struct
IPSET_TOKEN(HTYPE, 6ct_elem));
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct
IPSET_TOKEN(HTYPE, 6ct_elem),
timeout);
set->offset[IPSET_EXT_ID_COUNTER] =
offsetof(struct
IPSET_TOKEN(HTYPE, 6ct_elem),
counter);
IPSET_TOKEN(HTYPE, 6_gc_init)(set,
IPSET_TOKEN(HTYPE, 6_gc));
}
} else {
if (set->family == NFPROTO_IPV4) {
set->dsize =
sizeof(struct
IPSET_TOKEN(HTYPE, 4c_elem));
set->offset[IPSET_EXT_ID_COUNTER] =
offsetof(struct
IPSET_TOKEN(HTYPE, 4c_elem),
counter);
} else {
set->dsize =
sizeof(struct
IPSET_TOKEN(HTYPE, 6c_elem));
set->offset[IPSET_EXT_ID_COUNTER] =
offsetof(struct
IPSET_TOKEN(HTYPE, 6c_elem),
counter);
}
}
} else if (tb[IPSET_ATTR_TIMEOUT]) {
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]);
set->extensions |= IPSET_EXT_TIMEOUT;
if (set->family == NFPROTO_IPV4) {
set->dsize = sizeof(struct IPSET_TOKEN(HTYPE, 4t_elem));
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct IPSET_TOKEN(HTYPE, 4t_elem),
timeout);
if (set->family == NFPROTO_IPV4)
IPSET_TOKEN(HTYPE, 4_gc_init)(set,
IPSET_TOKEN(HTYPE, 4_gc));
} else {
set->dsize = sizeof(struct IPSET_TOKEN(HTYPE, 6t_elem));
set->offset[IPSET_EXT_ID_TIMEOUT] =
offsetof(struct IPSET_TOKEN(HTYPE, 6t_elem),
timeout);
else
IPSET_TOKEN(HTYPE, 6_gc_init)(set,
IPSET_TOKEN(HTYPE, 6_gc));
}
} else {
if (set->family == NFPROTO_IPV4)
set->dsize = sizeof(struct IPSET_TOKEN(HTYPE, 4_elem));
else
set->dsize = sizeof(struct IPSET_TOKEN(HTYPE, 6_elem));
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
......
......@@ -35,7 +35,7 @@ MODULE_ALIAS("ip_set_hash:ip");
#define HTYPE hash_ip
#define IP_SET_HASH_WITH_NETMASK
/* IPv4 variants */
/* IPv4 variant */
/* Member elements */
struct hash_ip4_elem {
......@@ -43,22 +43,6 @@ struct hash_ip4_elem {
__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 */
static inline bool
......@@ -178,29 +162,13 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
}
/* IPv6 variants */
/* IPv6 variant */
/* Member elements */
struct hash_ip6_elem {
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 */
static inline bool
......
......@@ -36,7 +36,7 @@ MODULE_ALIAS("ip_set_hash:ip,port");
/* Type specific function prefix */
#define HTYPE hash_ipport
/* IPv4 variants */
/* IPv4 variant */
/* Member elements */
struct hash_ipport4_elem {
......@@ -46,31 +46,6 @@ struct hash_ipport4_elem {
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 */
static inline bool
......@@ -221,7 +196,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],