summaryrefslogtreecommitdiff
path: root/include/net/netfilter/nf_conntrack_ecache.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/net/netfilter/nf_conntrack_ecache.h')
-rw-r--r--include/net/netfilter/nf_conntrack_ecache.h206
1 files changed, 157 insertions, 49 deletions
diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h
index 0ff0dc69ca4a..4f20d58e2ab7 100644
--- a/include/net/netfilter/nf_conntrack_ecache.h
+++ b/include/net/netfilter/nf_conntrack_ecache.h
@@ -6,17 +6,54 @@
#define _NF_CONNTRACK_ECACHE_H
#include <net/netfilter/nf_conntrack.h>
-#include <linux/notifier.h>
-#include <linux/interrupt.h>
#include <net/net_namespace.h>
#include <net/netfilter/nf_conntrack_expect.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+#include <net/netfilter/nf_conntrack_extend.h>
+
+/* Connection tracking event types */
+enum ip_conntrack_events
+{
+ IPCT_NEW = 0, /* new conntrack */
+ IPCT_RELATED = 1, /* related conntrack */
+ IPCT_DESTROY = 2, /* destroyed conntrack */
+ IPCT_STATUS = 3, /* status has changed */
+ IPCT_PROTOINFO = 4, /* protocol information has changed */
+ IPCT_HELPER = 5, /* new helper has been set */
+ IPCT_MARK = 6, /* new mark has been set */
+ IPCT_NATSEQADJ = 7, /* NAT is doing sequence adjustment */
+ IPCT_SECMARK = 8, /* new security mark has been set */
+};
+
+enum ip_conntrack_expect_events {
+ IPEXP_NEW = 0, /* new expectation */
+};
-#ifdef CONFIG_NF_CONNTRACK_EVENTS
struct nf_conntrack_ecache {
- struct nf_conn *ct;
- unsigned int events;
+ unsigned long cache; /* bitops want long */
+ unsigned long missed; /* missed events */
+ u32 pid; /* netlink pid of destroyer */
+};
+
+static inline struct nf_conntrack_ecache *
+nf_ct_ecache_find(const struct nf_conn *ct)
+{
+ return nf_ct_ext_find(ct, NF_CT_EXT_ECACHE);
+}
+
+static inline struct nf_conntrack_ecache *
+nf_ct_ecache_ext_add(struct nf_conn *ct, gfp_t gfp)
+{
+ struct net *net = nf_ct_net(ct);
+
+ if (!net->ct.sysctl_events)
+ return NULL;
+
+ return nf_ct_ext_add(ct, NF_CT_EXT_ECACHE, gfp);
};
+#ifdef CONFIG_NF_CONNTRACK_EVENTS
/* This structure is passed to event handler */
struct nf_ct_event {
struct nf_conn *ct;
@@ -24,47 +61,96 @@ struct nf_ct_event {
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);
+struct nf_ct_event_notifier {
+ int (*fcn)(unsigned int events, struct nf_ct_event *item);
+};
+
+extern struct nf_ct_event_notifier *nf_conntrack_event_cb;
+extern int nf_conntrack_register_notifier(struct nf_ct_event_notifier *nb);
+extern void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *nb);
-extern void nf_ct_deliver_cached_events(const struct nf_conn *ct);
-extern void __nf_ct_event_cache_init(struct nf_conn *ct);
-extern void nf_ct_event_cache_flush(struct net *net);
+extern void nf_ct_deliver_cached_events(struct nf_conn *ct);
static inline void
nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
{
+ struct nf_conntrack_ecache *e;
+
+ if (nf_conntrack_event_cb == NULL)
+ return;
+
+ e = nf_ct_ecache_find(ct);
+ if (e == NULL)
+ return;
+
+ set_bit(event, &e->cache);
+}
+
+static inline int
+nf_conntrack_eventmask_report(unsigned int eventmask,
+ struct nf_conn *ct,
+ u32 pid,
+ int report)
+{
+ int ret = 0;
struct net *net = nf_ct_net(ct);
- struct nf_conntrack_ecache *ecache;
-
- local_bh_disable();
- ecache = per_cpu_ptr(net->ct.ecache, raw_smp_processor_id());
- if (ct != ecache->ct)
- __nf_ct_event_cache_init(ct);
- ecache->events |= event;
- local_bh_enable();
+ struct nf_ct_event_notifier *notify;
+ struct nf_conntrack_ecache *e;
+
+ rcu_read_lock();
+ notify = rcu_dereference(nf_conntrack_event_cb);
+ if (notify == NULL)
+ goto out_unlock;
+
+ if (!net->ct.sysctl_events)
+ goto out_unlock;
+
+ e = nf_ct_ecache_find(ct);
+ if (e == NULL)
+ goto out_unlock;
+
+ if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) {
+ struct nf_ct_event item = {
+ .ct = ct,
+ .pid = e->pid ? e->pid : pid,
+ .report = report
+ };
+ /* This is a resent of a destroy event? If so, skip missed */
+ unsigned long missed = e->pid ? 0 : e->missed;
+
+ ret = notify->fcn(eventmask | missed, &item);
+ if (unlikely(ret < 0 || missed)) {
+ spin_lock_bh(&ct->lock);
+ if (ret < 0) {
+ /* This is a destroy event that has been
+ * triggered by a process, we store the PID
+ * to include it in the retransmission. */
+ if (eventmask & (1 << IPCT_DESTROY) &&
+ e->pid == 0 && pid != 0)
+ e->pid = pid;
+ else
+ e->missed |= eventmask;
+ } else
+ e->missed &= ~missed;
+ spin_unlock_bh(&ct->lock);
+ }
+ }
+out_unlock:
+ rcu_read_unlock();
+ return ret;
}
-static inline void
-nf_conntrack_event_report(enum ip_conntrack_events event,
- struct nf_conn *ct,
- u32 pid,
- int report)
+static inline int
+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, &item);
+ return nf_conntrack_eventmask_report(1 << event, ct, pid, report);
}
-static inline void
+static inline int
nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct)
{
- nf_conntrack_event_report(event, ct, 0, 0);
+ return nf_conntrack_eventmask_report(1 << event, ct, 0, 0);
}
struct nf_exp_event {
@@ -73,9 +159,13 @@ struct nf_exp_event {
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);
+struct nf_exp_event_notifier {
+ int (*fcn)(unsigned int events, struct nf_exp_event *item);
+};
+
+extern struct nf_exp_event_notifier *nf_expect_event_cb;
+extern int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *nb);
+extern void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *nb);
static inline void
nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
@@ -83,12 +173,27 @@ nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
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);
+ struct net *net = nf_ct_exp_net(exp);
+ struct nf_exp_event_notifier *notify;
+
+ rcu_read_lock();
+ notify = rcu_dereference(nf_expect_event_cb);
+ if (notify == NULL)
+ goto out_unlock;
+
+ if (!net->ct.sysctl_events)
+ goto out_unlock;
+
+ {
+ struct nf_exp_event item = {
+ .exp = exp,
+ .pid = pid,
+ .report = report
+ };
+ notify->fcn(1 << event, &item);
+ }
+out_unlock:
+ rcu_read_unlock();
}
static inline void
@@ -105,12 +210,16 @@ extern void nf_conntrack_ecache_fini(struct net *net);
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 int nf_conntrack_eventmask_report(unsigned int eventmask,
+ struct nf_conn *ct,
+ u32 pid,
+ int report) { return 0; }
+static inline int nf_conntrack_event(enum ip_conntrack_events event,
+ struct nf_conn *ct) { return 0; }
+static inline int nf_conntrack_event_report(enum ip_conntrack_events event,
+ struct nf_conn *ct,
+ u32 pid,
+ int report) { return 0; }
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) {}
@@ -118,7 +227,6 @@ 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)
{