netfilter: xt_connlimit: honor conntrack zone if available

Currently all the conntrack lookups are done using default zone.
In case the skb has a ct attached (e.g. template) we should use this zone
for lookups instead.  This makes connlimit work with connections assigned
to other zones.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Florian Westphal 2014-11-14 13:21:48 +01:00 committed by Pablo Neira Ayuso
parent 8225161545
commit e59ea3df3f

View File

@ -134,6 +134,7 @@ static bool add_hlist(struct hlist_head *head,
static unsigned int check_hlist(struct net *net, static unsigned int check_hlist(struct net *net,
struct hlist_head *head, struct hlist_head *head,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
u16 zone,
bool *addit) bool *addit)
{ {
const struct nf_conntrack_tuple_hash *found; const struct nf_conntrack_tuple_hash *found;
@ -147,8 +148,7 @@ static unsigned int check_hlist(struct net *net,
/* check the saved connections */ /* check the saved connections */
hlist_for_each_entry_safe(conn, n, head, node) { hlist_for_each_entry_safe(conn, n, head, node) {
found = nf_conntrack_find_get(net, NF_CT_DEFAULT_ZONE, found = nf_conntrack_find_get(net, zone, &conn->tuple);
&conn->tuple);
if (found == NULL) { if (found == NULL) {
hlist_del(&conn->node); hlist_del(&conn->node);
kmem_cache_free(connlimit_conn_cachep, conn); kmem_cache_free(connlimit_conn_cachep, conn);
@ -201,7 +201,7 @@ static unsigned int
count_tree(struct net *net, struct rb_root *root, count_tree(struct net *net, struct rb_root *root,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
const union nf_inet_addr *addr, const union nf_inet_addr *mask, const union nf_inet_addr *addr, const union nf_inet_addr *mask,
u8 family) u8 family, u16 zone)
{ {
struct xt_connlimit_rb *gc_nodes[CONNLIMIT_GC_MAX_NODES]; struct xt_connlimit_rb *gc_nodes[CONNLIMIT_GC_MAX_NODES];
struct rb_node **rbnode, *parent; struct rb_node **rbnode, *parent;
@ -229,7 +229,7 @@ count_tree(struct net *net, struct rb_root *root,
} else { } else {
/* same source network -> be counted! */ /* same source network -> be counted! */
unsigned int count; unsigned int count;
count = check_hlist(net, &rbconn->hhead, tuple, &addit); count = check_hlist(net, &rbconn->hhead, tuple, zone, &addit);
tree_nodes_free(root, gc_nodes, gc_count); tree_nodes_free(root, gc_nodes, gc_count);
if (!addit) if (!addit)
@ -245,7 +245,7 @@ count_tree(struct net *net, struct rb_root *root,
continue; continue;
/* only used for GC on hhead, retval and 'addit' ignored */ /* only used for GC on hhead, retval and 'addit' ignored */
check_hlist(net, &rbconn->hhead, tuple, &addit); check_hlist(net, &rbconn->hhead, tuple, zone, &addit);
if (hlist_empty(&rbconn->hhead)) if (hlist_empty(&rbconn->hhead))
gc_nodes[gc_count++] = rbconn; gc_nodes[gc_count++] = rbconn;
} }
@ -290,7 +290,7 @@ static int count_them(struct net *net,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
const union nf_inet_addr *addr, const union nf_inet_addr *addr,
const union nf_inet_addr *mask, const union nf_inet_addr *mask,
u_int8_t family) u_int8_t family, u16 zone)
{ {
struct rb_root *root; struct rb_root *root;
int count; int count;
@ -306,7 +306,7 @@ static int count_them(struct net *net,
spin_lock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]); spin_lock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]);
count = count_tree(net, root, tuple, addr, mask, family); count = count_tree(net, root, tuple, addr, mask, family, zone);
spin_unlock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]); spin_unlock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]);
@ -324,13 +324,16 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
const struct nf_conn *ct; const struct nf_conn *ct;
unsigned int connections; unsigned int connections;
u16 zone = NF_CT_DEFAULT_ZONE;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
if (ct != NULL) if (ct != NULL) {
tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), zone = nf_ct_zone(ct);
par->family, &tuple)) } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
par->family, &tuple)) {
goto hotdrop; goto hotdrop;
}
if (par->family == NFPROTO_IPV6) { if (par->family == NFPROTO_IPV6) {
const struct ipv6hdr *iph = ipv6_hdr(skb); const struct ipv6hdr *iph = ipv6_hdr(skb);
@ -343,7 +346,7 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
} }
connections = count_them(net, info->data, tuple_ptr, &addr, connections = count_them(net, info->data, tuple_ptr, &addr,
&info->mask, par->family); &info->mask, par->family, zone);
if (connections == 0) if (connections == 0)
/* kmalloc failed, drop it entirely */ /* kmalloc failed, drop it entirely */
goto hotdrop; goto hotdrop;