netfilter: conntrack: Flush connections with a given mark

This patch adds support for selective flushing of conntrack mappings.
By adding CTA_MARK and CTA_MARK_MASK to a delete-message, the mark (and
mask) is checked before a connection is deleted while flushing.

Configuring the flush is moved out of ctnetlink_del_conntrack(), and
instead of calling nf_conntrack_flush_report(), we always call
nf_ct_iterate_cleanup().  This enables us to only make one call from the
new ctnetlink_flush_conntrack() and makes it easy to add more filter
parameters.

Filtering is done in the ctnetlink_filter_match()-function, which is
also called from ctnetlink_dump_table(). ctnetlink_dump_filter has been
renamed ctnetlink_filter, to indicated that it is no longer only used
when dumping conntrack entries.

Moreover, reject mark filters with -EOPNOTSUPP if no ct mark support is
available.

Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Kristian Evensen 2014-12-24 09:57:10 +01:00 committed by Pablo Neira Ayuso
parent b44b565cf5
commit 866476f323
1 changed files with 64 additions and 25 deletions

View File

@ -749,13 +749,47 @@ static int ctnetlink_done(struct netlink_callback *cb)
return 0;
}
struct ctnetlink_dump_filter {
struct ctnetlink_filter {
struct {
u_int32_t val;
u_int32_t mask;
} mark;
};
static struct ctnetlink_filter *
ctnetlink_alloc_filter(const struct nlattr * const cda[])
{
#ifdef CONFIG_NF_CONNTRACK_MARK
struct ctnetlink_filter *filter;
filter = kzalloc(sizeof(*filter), GFP_KERNEL);
if (filter == NULL)
return ERR_PTR(-ENOMEM);
filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK]));
filter->mark.mask = ntohl(nla_get_be32(cda[CTA_MARK_MASK]));
return filter;
#else
return ERR_PTR(-EOPNOTSUPP);
#endif
}
static int ctnetlink_filter_match(struct nf_conn *ct, void *data)
{
struct ctnetlink_filter *filter = data;
if (filter == NULL)
return 1;
#ifdef CONFIG_NF_CONNTRACK_MARK
if ((ct->mark & filter->mark.mask) == filter->mark.val)
return 1;
#endif
return 0;
}
static int
ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
@ -768,10 +802,6 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
int res;
spinlock_t *lockp;
#ifdef CONFIG_NF_CONNTRACK_MARK
const struct ctnetlink_dump_filter *filter = cb->data;
#endif
last = (struct nf_conn *)cb->args[1];
local_bh_disable();
@ -798,12 +828,9 @@ restart:
continue;
cb->args[1] = 0;
}
#ifdef CONFIG_NF_CONNTRACK_MARK
if (filter && !((ct->mark & filter->mark.mask) ==
filter->mark.val)) {
if (!ctnetlink_filter_match(ct, cb->data))
continue;
}
#endif
rcu_read_lock();
res =
ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid,
@ -1001,6 +1028,25 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
.len = NF_CT_LABELS_MAX_SIZE },
};
static int ctnetlink_flush_conntrack(struct net *net,
const struct nlattr * const cda[],
u32 portid, int report)
{
struct ctnetlink_filter *filter = NULL;
if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) {
filter = ctnetlink_alloc_filter(cda);
if (IS_ERR(filter))
return PTR_ERR(filter);
}
nf_ct_iterate_cleanup(net, ctnetlink_filter_match, filter,
portid, report);
kfree(filter);
return 0;
}
static int
ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
@ -1024,11 +1070,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
else if (cda[CTA_TUPLE_REPLY])
err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3);
else {
/* Flush the whole table */
nf_conntrack_flush_report(net,
NETLINK_CB(skb).portid,
nlmsg_report(nlh));
return 0;
return ctnetlink_flush_conntrack(net, cda,
NETLINK_CB(skb).portid,
nlmsg_report(nlh));
}
if (err < 0)
@ -1076,21 +1120,16 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
.dump = ctnetlink_dump_table,
.done = ctnetlink_done,
};
#ifdef CONFIG_NF_CONNTRACK_MARK
if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) {
struct ctnetlink_dump_filter *filter;
struct ctnetlink_filter *filter;
filter = kzalloc(sizeof(struct ctnetlink_dump_filter),
GFP_ATOMIC);
if (filter == NULL)
return -ENOMEM;
filter = ctnetlink_alloc_filter(cda);
if (IS_ERR(filter))
return PTR_ERR(filter);
filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK]));
filter->mark.mask =
ntohl(nla_get_be32(cda[CTA_MARK_MASK]));
c.data = filter;
}
#endif
return netlink_dump_start(ctnl, skb, nlh, &c);
}