Commit 866476f3 authored by Kristian Evensen's avatar Kristian Evensen Committed by Pablo Neira Ayuso

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: default avatarKristian Evensen <kristian.evensen@gmail.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent b44b565c
...@@ -749,13 +749,47 @@ static int ctnetlink_done(struct netlink_callback *cb) ...@@ -749,13 +749,47 @@ static int ctnetlink_done(struct netlink_callback *cb)
return 0; return 0;
} }
struct ctnetlink_dump_filter { struct ctnetlink_filter {
struct { struct {
u_int32_t val; u_int32_t val;
u_int32_t mask; u_int32_t mask;
} mark; } 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 static int
ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) 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) ...@@ -768,10 +802,6 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
int res; int res;
spinlock_t *lockp; spinlock_t *lockp;
#ifdef CONFIG_NF_CONNTRACK_MARK
const struct ctnetlink_dump_filter *filter = cb->data;
#endif
last = (struct nf_conn *)cb->args[1]; last = (struct nf_conn *)cb->args[1];
local_bh_disable(); local_bh_disable();
...@@ -798,12 +828,9 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -798,12 +828,9 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
continue; continue;
cb->args[1] = 0; cb->args[1] = 0;
} }
#ifdef CONFIG_NF_CONNTRACK_MARK if (!ctnetlink_filter_match(ct, cb->data))
if (filter && !((ct->mark & filter->mark.mask) ==
filter->mark.val)) {
continue; continue;
}
#endif
rcu_read_lock(); rcu_read_lock();
res = res =
ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid, ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid,
...@@ -1001,6 +1028,25 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { ...@@ -1001,6 +1028,25 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
.len = NF_CT_LABELS_MAX_SIZE }, .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 static int
ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
...@@ -1024,11 +1070,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, ...@@ -1024,11 +1070,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
else if (cda[CTA_TUPLE_REPLY]) else if (cda[CTA_TUPLE_REPLY])
err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3); err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3);
else { else {
/* Flush the whole table */ return ctnetlink_flush_conntrack(net, cda,
nf_conntrack_flush_report(net,
NETLINK_CB(skb).portid, NETLINK_CB(skb).portid,
nlmsg_report(nlh)); nlmsg_report(nlh));
return 0;
} }
if (err < 0) if (err < 0)
...@@ -1076,21 +1120,16 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, ...@@ -1076,21 +1120,16 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
.dump = ctnetlink_dump_table, .dump = ctnetlink_dump_table,
.done = ctnetlink_done, .done = ctnetlink_done,
}; };
#ifdef CONFIG_NF_CONNTRACK_MARK
if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) { if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) {
struct ctnetlink_dump_filter *filter; struct ctnetlink_filter *filter;
filter = kzalloc(sizeof(struct ctnetlink_dump_filter), filter = ctnetlink_alloc_filter(cda);
GFP_ATOMIC); if (IS_ERR(filter))
if (filter == NULL) return PTR_ERR(filter);
return -ENOMEM;
filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK]));
filter->mark.mask =
ntohl(nla_get_be32(cda[CTA_MARK_MASK]));
c.data = filter; c.data = filter;
} }
#endif
return netlink_dump_start(ctnl, skb, nlh, &c); return netlink_dump_start(ctnl, skb, nlh, &c);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment