Skip to content

Commit

Permalink
netfilter: ctnetlink: deliver events for conntracks changed from user…
Browse files Browse the repository at this point in the history
…space

As for now, the creation and update of conntracks via ctnetlink do not
propagate an event to userspace. This can result in inconsistent situations
if several userspace processes modify the connection tracking table by means
of ctnetlink at the same time. Specifically, using the conntrack command
line tool and conntrackd at the same time can trigger unconsistencies.

This patch also modifies the event cache infrastructure to pass the
process PID and the ECHO flag to nfnetlink_send() to report back
to userspace if the process that triggered the change needs so.
Based on a suggestion from Patrick McHardy.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
  • Loading branch information
Pablo Neira Ayuso authored and Patrick McHardy committed Nov 18, 2008
1 parent 226c0c0 commit 19abb7b
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 34 deletions.
2 changes: 1 addition & 1 deletion include/net/netfilter/nf_conntrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple);

extern void nf_conntrack_hash_insert(struct nf_conn *ct);

extern void nf_conntrack_flush(struct net *net);
extern void nf_conntrack_flush(struct net *net, u32 pid, int report);

extern bool nf_ct_get_tuplepr(const struct sk_buff *skb,
unsigned int nhoff, u_int16_t l3num,
Expand Down
57 changes: 53 additions & 4 deletions include/net/netfilter/nf_conntrack_ecache.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ struct nf_conntrack_ecache {
unsigned int events;
};

/* This structure is passed to event handler */
struct nf_ct_event {
struct nf_conn *ct;
u32 pid;
int report;
};

extern struct atomic_notifier_head nf_conntrack_chain;
extern int nf_conntrack_register_notifier(struct notifier_block *nb);
extern int nf_conntrack_unregister_notifier(struct notifier_block *nb);
Expand All @@ -39,22 +46,56 @@ nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
local_bh_enable();
}

static inline void nf_conntrack_event(enum ip_conntrack_events event,
struct nf_conn *ct)
static inline void
nf_conntrack_event_report(enum ip_conntrack_events event,
struct nf_conn *ct,
u32 pid,
int report)
{
struct nf_ct_event item = {
.ct = ct,
.pid = pid,
.report = report
};
if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct))
atomic_notifier_call_chain(&nf_conntrack_chain, event, ct);
atomic_notifier_call_chain(&nf_conntrack_chain, event, &item);
}

static inline void
nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct)
{
nf_conntrack_event_report(event, ct, 0, 0);
}

struct nf_exp_event {
struct nf_conntrack_expect *exp;
u32 pid;
int report;
};

extern struct atomic_notifier_head nf_ct_expect_chain;
extern int nf_ct_expect_register_notifier(struct notifier_block *nb);
extern int nf_ct_expect_unregister_notifier(struct notifier_block *nb);

static inline void
nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
struct nf_conntrack_expect *exp,
u32 pid,
int report)
{
struct nf_exp_event item = {
.exp = exp,
.pid = pid,
.report = report
};
atomic_notifier_call_chain(&nf_ct_expect_chain, event, &item);
}

static inline void
nf_ct_expect_event(enum ip_conntrack_expect_events event,
struct nf_conntrack_expect *exp)
{
atomic_notifier_call_chain(&nf_ct_expect_chain, event, exp);
nf_ct_expect_event_report(event, exp, 0, 0);
}

extern int nf_conntrack_ecache_init(struct net *net);
Expand All @@ -66,9 +107,17 @@ static inline void nf_conntrack_event_cache(enum ip_conntrack_events event,
struct nf_conn *ct) {}
static inline void nf_conntrack_event(enum ip_conntrack_events event,
struct nf_conn *ct) {}
static inline void nf_conntrack_event_report(enum ip_conntrack_events event,
struct nf_conn *ct,
u32 pid,
int report) {}
static inline void nf_ct_deliver_cached_events(const struct nf_conn *ct) {}
static inline void nf_ct_expect_event(enum ip_conntrack_expect_events event,
struct nf_conntrack_expect *exp) {}
static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e,
struct nf_conntrack_expect *exp,
u32 pid,
int report) {}
static inline void nf_ct_event_cache_flush(struct net *net) {}

static inline int nf_conntrack_ecache_init(struct net *net)
Expand Down
2 changes: 2 additions & 0 deletions include/net/netfilter/nf_conntrack_expect.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ void nf_ct_expect_init(struct nf_conntrack_expect *, unsigned int, u_int8_t,
u_int8_t, const __be16 *, const __be16 *);
void nf_ct_expect_put(struct nf_conntrack_expect *exp);
int nf_ct_expect_related(struct nf_conntrack_expect *expect);
int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
u32 pid, int report);

#endif /*_NF_CONNTRACK_EXPECT_H*/

25 changes: 21 additions & 4 deletions net/netfilter/nf_conntrack_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ destroy_conntrack(struct nf_conntrack *nfct)
NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
NF_CT_ASSERT(!timer_pending(&ct->timeout));

nf_conntrack_event(IPCT_DESTROY, ct);
if (!test_bit(IPS_DYING_BIT, &ct->status))
nf_conntrack_event(IPCT_DESTROY, ct);
set_bit(IPS_DYING_BIT, &ct->status);

/* To make sure we don't get any weird locking issues here:
Expand Down Expand Up @@ -972,8 +973,20 @@ void nf_ct_iterate_cleanup(struct net *net,
}
EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup);

struct __nf_ct_flush_report {
u32 pid;
int report;
};

static int kill_all(struct nf_conn *i, void *data)
{
struct __nf_ct_flush_report *fr = (struct __nf_ct_flush_report *)data;

/* get_next_corpse sets the dying bit for us */
nf_conntrack_event_report(IPCT_DESTROY,
i,
fr->pid,
fr->report);
return 1;
}

Expand All @@ -987,9 +1000,13 @@ void nf_ct_free_hashtable(struct hlist_head *hash, int vmalloced, unsigned int s
}
EXPORT_SYMBOL_GPL(nf_ct_free_hashtable);

void nf_conntrack_flush(struct net *net)
void nf_conntrack_flush(struct net *net, u32 pid, int report)
{
nf_ct_iterate_cleanup(net, kill_all, NULL);
struct __nf_ct_flush_report fr = {
.pid = pid,
.report = report,
};
nf_ct_iterate_cleanup(net, kill_all, &fr);
}
EXPORT_SYMBOL_GPL(nf_conntrack_flush);

Expand All @@ -1005,7 +1022,7 @@ static void nf_conntrack_cleanup_net(struct net *net)
nf_ct_event_cache_flush(net);
nf_conntrack_ecache_fini(net);
i_see_dead_people:
nf_conntrack_flush(net);
nf_conntrack_flush(net, 0, 0);
if (atomic_read(&net->ct.count) != 0) {
schedule();
goto i_see_dead_people;
Expand Down
14 changes: 11 additions & 3 deletions net/netfilter/nf_conntrack_ecache.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,17 @@ static inline void
__nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache)
{
if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct)
&& ecache->events)
atomic_notifier_call_chain(&nf_conntrack_chain, ecache->events,
ecache->ct);
&& ecache->events) {
struct nf_ct_event item = {
.ct = ecache->ct,
.pid = 0,
.report = 0
};

atomic_notifier_call_chain(&nf_conntrack_chain,
ecache->events,
&item);
}

ecache->events = 0;
nf_ct_put(ecache->ct);
Expand Down
43 changes: 36 additions & 7 deletions net/netfilter/nf_conntrack_expect.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ static inline int refresh_timer(struct nf_conntrack_expect *i)
return 1;
}

int nf_ct_expect_related(struct nf_conntrack_expect *expect)
static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
{
const struct nf_conntrack_expect_policy *p;
struct nf_conntrack_expect *i;
Expand All @@ -371,11 +371,8 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect)
struct net *net = nf_ct_exp_net(expect);
struct hlist_node *n;
unsigned int h;
int ret;

NF_CT_ASSERT(master_help);
int ret = 0;

spin_lock_bh(&nf_conntrack_lock);
if (!master_help->helper) {
ret = -ESHUTDOWN;
goto out;
Expand Down Expand Up @@ -409,18 +406,50 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect)
printk(KERN_WARNING
"nf_conntrack: expectation table full\n");
ret = -EMFILE;
goto out;
}
out:
return ret;
}

int nf_ct_expect_related(struct nf_conntrack_expect *expect)
{
int ret;

spin_lock_bh(&nf_conntrack_lock);
ret = __nf_ct_expect_check(expect);
if (ret < 0)
goto out;

nf_ct_expect_insert(expect);
atomic_inc(&expect->use);
spin_unlock_bh(&nf_conntrack_lock);
nf_ct_expect_event(IPEXP_NEW, expect);
ret = 0;
nf_ct_expect_put(expect);
return ret;
out:
spin_unlock_bh(&nf_conntrack_lock);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related);

int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
u32 pid, int report)
{
int ret;

spin_lock_bh(&nf_conntrack_lock);
ret = __nf_ct_expect_check(expect);
if (ret < 0)
goto out;
nf_ct_expect_insert(expect);
out:
spin_unlock_bh(&nf_conntrack_lock);
if (ret == 0)
nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);

#ifdef CONFIG_PROC_FS
struct ct_expect_iter_state {
struct seq_net_private p;
Expand Down
Loading

0 comments on commit 19abb7b

Please sign in to comment.