#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/socket.h>
+#include <linux/mm.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_l3proto.h>
#endif
DEFINE_RWLOCK(nf_conntrack_lock);
+EXPORT_SYMBOL_GPL(nf_conntrack_lock);
/* nf_conntrack_standalone needs this */
atomic_t nf_conntrack_count = ATOMIC_INIT(0);
+EXPORT_SYMBOL_GPL(nf_conntrack_count);
+
+void (*nf_conntrack_destroyed)(struct nf_conn *conntrack);
+EXPORT_SYMBOL_GPL(nf_conntrack_destroyed);
-void (*nf_conntrack_destroyed)(struct nf_conn *conntrack) = NULL;
unsigned int nf_conntrack_htable_size __read_mostly;
+EXPORT_SYMBOL_GPL(nf_conntrack_htable_size);
+
int nf_conntrack_max __read_mostly;
+EXPORT_SYMBOL_GPL(nf_conntrack_max);
+
struct list_head *nf_conntrack_hash __read_mostly;
+EXPORT_SYMBOL_GPL(nf_conntrack_hash);
+
struct nf_conn nf_conntrack_untracked __read_mostly;
+EXPORT_SYMBOL_GPL(nf_conntrack_untracked);
+
unsigned int nf_ct_log_invalid __read_mostly;
LIST_HEAD(unconfirmed);
static int nf_conntrack_vmalloc __read_mostly;
size_t size;
/* slab cache pointer */
- kmem_cache_t *cachep;
+ struct kmem_cache *cachep;
/* allocated slab cache + modules which uses this slab cache */
int use;
{
int ret = 0;
char *cache_name;
- kmem_cache_t *cachep;
+ struct kmem_cache *cachep;
DEBUGP("nf_conntrack_register_cache: features=0x%x, name=%s, size=%d\n",
features, name, size);
mutex_unlock(&nf_ct_cache_mutex);
return ret;
}
+EXPORT_SYMBOL_GPL(nf_conntrack_register_cache);
/* FIXME: In the current, only nf_conntrack_cleanup() can call this function. */
void nf_conntrack_unregister_cache(u_int32_t features)
{
- kmem_cache_t *cachep;
+ struct kmem_cache *cachep;
char *name;
/*
mutex_unlock(&nf_ct_cache_mutex);
}
+EXPORT_SYMBOL_GPL(nf_conntrack_unregister_cache);
int
nf_ct_get_tuple(const struct sk_buff *skb,
return l4proto->pkt_to_tuple(skb, dataoff, tuple);
}
+EXPORT_SYMBOL_GPL(nf_ct_get_tuple);
int
nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse,
inverse->dst.protonum = orig->dst.protonum;
return l4proto->invert_tuple(inverse, orig);
}
+EXPORT_SYMBOL_GPL(nf_ct_invert_tuple);
static void
clean_from_lists(struct nf_conn *ct)
destroy_conntrack(struct nf_conntrack *nfct)
{
struct nf_conn *ct = (struct nf_conn *)nfct;
+ struct nf_conn_help *help = nfct_help(ct);
struct nf_conntrack_l3proto *l3proto;
struct nf_conntrack_l4proto *l4proto;
+ typeof(nf_conntrack_destroyed) destroyed;
DEBUGP("destroy_conntrack(%p)\n", ct);
NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
nf_conntrack_event(IPCT_DESTROY, ct);
set_bit(IPS_DYING_BIT, &ct->status);
+ if (help && help->helper && help->helper->destroy)
+ help->helper->destroy(ct);
+
/* To make sure we don't get any weird locking issues here:
* destroy_conntrack() MUST NOT be called with a write lock
* to nf_conntrack_lock!!! -HW */
+ rcu_read_lock();
l3proto = __nf_ct_l3proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num);
if (l3proto && l3proto->destroy)
l3proto->destroy(ct);
- l4proto = __nf_ct_l4proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num, ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);
+ l4proto = __nf_ct_l4proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num,
+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);
if (l4proto && l4proto->destroy)
l4proto->destroy(ct);
- if (nf_conntrack_destroyed)
- nf_conntrack_destroyed(ct);
+ destroyed = rcu_dereference(nf_conntrack_destroyed);
+ if (destroyed)
+ destroyed(ct);
+
+ rcu_read_unlock();
write_lock_bh(&nf_conntrack_lock);
/* Expectations will have been removed in clean_from_lists,
return NULL;
}
+EXPORT_SYMBOL_GPL(__nf_conntrack_find);
/* Find a connection corresponding to a tuple. */
struct nf_conntrack_tuple_hash *
return h;
}
+EXPORT_SYMBOL_GPL(nf_conntrack_find_get);
static void __nf_conntrack_hash_insert(struct nf_conn *ct,
unsigned int hash,
- unsigned int repl_hash)
+ unsigned int repl_hash)
{
ct->id = ++nf_conntrack_next_id;
list_add(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list,
__nf_conntrack_hash_insert(ct, hash, repl_hash);
write_unlock_bh(&nf_conntrack_lock);
}
+EXPORT_SYMBOL_GPL(nf_conntrack_hash_insert);
/* Confirm a connection given skb; places it in hash table */
int
write_unlock_bh(&nf_conntrack_lock);
return NF_DROP;
}
+EXPORT_SYMBOL_GPL(__nf_conntrack_confirm);
/* Returns true if a connection correspondings to the tuple (required
for NAT). */
return h != NULL;
}
+EXPORT_SYMBOL_GPL(nf_conntrack_tuple_taken);
/* There's a small race here where we may free a just-assured
connection. Too bad: we're in trouble anyway. */
if (del_timer(&ct->timeout)) {
death_by_timeout((unsigned long)ct);
dropped = 1;
- NF_CT_STAT_INC(early_drop);
+ NF_CT_STAT_INC_ATOMIC(early_drop);
}
nf_ct_put(ct);
return dropped;
static struct nf_conn *
__nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,
const struct nf_conntrack_tuple *repl,
- const struct nf_conntrack_l3proto *l3proto)
+ const struct nf_conntrack_l3proto *l3proto,
+ u_int32_t features)
{
struct nf_conn *conntrack = NULL;
- u_int32_t features = 0;
struct nf_conntrack_helper *helper;
if (unlikely(!nf_conntrack_hash_rnd_initted)) {
}
/* find features needed by this conntrack. */
- features = l3proto->get_features(orig);
+ features |= l3proto->get_features(orig);
/* FIXME: protect helper list per RCU */
read_lock_bh(&nf_conntrack_lock);
helper = __nf_ct_helper_find(repl);
- if (helper)
+ /* NAT might want to assign a helper later */
+ if (helper || features & NF_CT_F_NAT)
features |= NF_CT_F_HELP;
read_unlock_bh(&nf_conntrack_lock);
const struct nf_conntrack_tuple *repl)
{
struct nf_conntrack_l3proto *l3proto;
+ struct nf_conn *ct;
+ rcu_read_lock();
l3proto = __nf_ct_l3proto_find(orig->src.l3num);
- return __nf_conntrack_alloc(orig, repl, l3proto);
+ ct = __nf_conntrack_alloc(orig, repl, l3proto, 0);
+ rcu_read_unlock();
+
+ return ct;
}
+EXPORT_SYMBOL_GPL(nf_conntrack_alloc);
void nf_conntrack_free(struct nf_conn *conntrack)
{
kmem_cache_free(nf_ct_cache[features].cachep, conntrack);
atomic_dec(&nf_conntrack_count);
}
+EXPORT_SYMBOL_GPL(nf_conntrack_free);
/* Allocate a new conntrack: we return -ENOMEM if classification
failed due to stress. Otherwise it really is unclassifiable. */
struct nf_conn *conntrack;
struct nf_conntrack_tuple repl_tuple;
struct nf_conntrack_expect *exp;
+ u_int32_t features = 0;
if (!nf_ct_invert_tuple(&repl_tuple, tuple, l3proto, l4proto)) {
DEBUGP("Can't invert tuple.\n");
return NULL;
}
- conntrack = __nf_conntrack_alloc(tuple, &repl_tuple, l3proto);
+ read_lock_bh(&nf_conntrack_lock);
+ exp = __nf_conntrack_expect_find(tuple);
+ if (exp && exp->helper)
+ features = NF_CT_F_HELP;
+ read_unlock_bh(&nf_conntrack_lock);
+
+ conntrack = __nf_conntrack_alloc(tuple, &repl_tuple, l3proto, features);
if (conntrack == NULL || IS_ERR(conntrack)) {
DEBUGP("Can't allocate conntrack.\n");
return (struct nf_conntrack_tuple_hash *)conntrack;
/* Welcome, Mr. Bond. We've been expecting you... */
__set_bit(IPS_EXPECTED_BIT, &conntrack->status);
conntrack->master = exp->master;
+ if (exp->helper)
+ nfct_help(conntrack)->helper = exp->helper;
#ifdef CONFIG_NF_CONNTRACK_MARK
conntrack->mark = exp->master->mark;
#endif
/* Previously seen (loopback or untracked)? Ignore. */
if ((*pskb)->nfct) {
- NF_CT_STAT_INC(ignore);
+ NF_CT_STAT_INC_ATOMIC(ignore);
return NF_ACCEPT;
}
+ /* rcu_read_lock()ed by nf_hook_slow */
l3proto = __nf_ct_l3proto_find((u_int16_t)pf);
+
if ((ret = l3proto->prepare(pskb, hooknum, &dataoff, &protonum)) <= 0) {
DEBUGP("not prepared to track yet or error occured\n");
return -ret;
* core what to do with the packet. */
if (l4proto->error != NULL &&
(ret = l4proto->error(*pskb, dataoff, &ctinfo, pf, hooknum)) <= 0) {
- NF_CT_STAT_INC(error);
- NF_CT_STAT_INC(invalid);
+ NF_CT_STAT_INC_ATOMIC(error);
+ NF_CT_STAT_INC_ATOMIC(invalid);
return -ret;
}
&set_reply, &ctinfo);
if (!ct) {
/* Not valid part of a connection */
- NF_CT_STAT_INC(invalid);
+ NF_CT_STAT_INC_ATOMIC(invalid);
return NF_ACCEPT;
}
if (IS_ERR(ct)) {
/* Too stressed to deal. */
- NF_CT_STAT_INC(drop);
+ NF_CT_STAT_INC_ATOMIC(drop);
return NF_DROP;
}
DEBUGP("nf_conntrack_in: Can't track with proto module\n");
nf_conntrack_put((*pskb)->nfct);
(*pskb)->nfct = NULL;
- NF_CT_STAT_INC(invalid);
+ NF_CT_STAT_INC_ATOMIC(invalid);
return -ret;
}
return ret;
}
+EXPORT_SYMBOL_GPL(nf_conntrack_in);
int nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse,
const struct nf_conntrack_tuple *orig)
{
- return nf_ct_invert_tuple(inverse, orig,
- __nf_ct_l3proto_find(orig->src.l3num),
- __nf_ct_l4proto_find(orig->src.l3num,
- orig->dst.protonum));
+ int ret;
+
+ rcu_read_lock();
+ ret = nf_ct_invert_tuple(inverse, orig,
+ __nf_ct_l3proto_find(orig->src.l3num),
+ __nf_ct_l4proto_find(orig->src.l3num,
+ orig->dst.protonum));
+ rcu_read_unlock();
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nf_ct_invert_tuplepr);
+
+/* Alter reply tuple (maybe alter helper). This is for NAT, and is
+ implicitly racy: see __nf_conntrack_confirm */
+void nf_conntrack_alter_reply(struct nf_conn *ct,
+ const struct nf_conntrack_tuple *newreply)
+{
+ struct nf_conn_help *help = nfct_help(ct);
+
+ write_lock_bh(&nf_conntrack_lock);
+ /* Should be unconfirmed, so not in hash table yet */
+ NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
+
+ DEBUGP("Altering reply tuple of %p to ", ct);
+ NF_CT_DUMP_TUPLE(newreply);
+
+ ct->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
+ if (!ct->master && help && help->expecting == 0)
+ help->helper = __nf_ct_helper_find(newreply);
+ write_unlock_bh(&nf_conntrack_lock);
}
+EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);
/* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */
void __nf_ct_refresh_acct(struct nf_conn *ct,
ct->timeout.expires = extra_jiffies;
event = IPCT_REFRESH;
} else {
- /* Need del_timer for race avoidance (may already be dying). */
- if (del_timer(&ct->timeout)) {
- ct->timeout.expires = jiffies + extra_jiffies;
+ unsigned long newtime = jiffies + extra_jiffies;
+
+ /* Only update the timeout if the new timeout is at least
+ HZ jiffies from the old timeout. Need del_timer for race
+ avoidance (may already be dying). */
+ if (newtime - ct->timeout.expires >= HZ
+ && del_timer(&ct->timeout)) {
+ ct->timeout.expires = newtime;
add_timer(&ct->timeout);
event = IPCT_REFRESH;
}
ct->counters[CTINFO2DIR(ctinfo)].packets++;
ct->counters[CTINFO2DIR(ctinfo)].bytes +=
skb->len - (unsigned int)(skb->nh.raw - skb->data);
- if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000)
- || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000))
- event |= IPCT_COUNTER_FILLING;
+
+ if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000)
+ || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000))
+ event |= IPCT_COUNTER_FILLING;
}
#endif
if (event)
nf_conntrack_event_cache(event, skb);
}
+EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct);
#if defined(CONFIG_NF_CT_NETLINK) || \
defined(CONFIG_NF_CT_NETLINK_MODULE)
nfattr_failure:
return -1;
}
+EXPORT_SYMBOL_GPL(nf_ct_port_tuple_to_nfattr);
static const size_t cta_min_proto[CTA_PROTO_MAX] = {
[CTA_PROTO_SRC_PORT-1] = sizeof(u_int16_t),
if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
return -EINVAL;
- t->src.u.tcp.port =
- *(u_int16_t *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]);
- t->dst.u.tcp.port =
- *(u_int16_t *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]);
+ t->src.u.tcp.port = *(__be16 *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]);
+ t->dst.u.tcp.port = *(__be16 *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]);
return 0;
}
+EXPORT_SYMBOL_GPL(nf_ct_port_nfattr_to_tuple);
#endif
/* Used by ipt_REJECT and ip6t_REJECT. */
nskb->nfctinfo = ctinfo;
nf_conntrack_get(nskb->nfct);
}
+EXPORT_SYMBOL_GPL(__nf_conntrack_attach);
static inline int
do_iter(const struct nf_conntrack_tuple_hash *i,
if (iter(ct, data))
goto found;
}
- }
+ }
list_for_each_entry(h, &unconfirmed, list) {
ct = nf_ct_tuplehash_to_ctrack(h);
if (iter(ct, data))
nf_ct_put(ct);
}
}
+EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup);
static int kill_all(struct nf_conn *i, void *data)
{
if (vmalloced)
vfree(hash);
else
- free_pages((unsigned long)hash,
+ free_pages((unsigned long)hash,
get_order(sizeof(struct list_head) * size));
}
-void nf_conntrack_flush()
+void nf_conntrack_flush(void)
{
nf_ct_iterate_cleanup(kill_all, NULL);
}
+EXPORT_SYMBOL_GPL(nf_conntrack_flush);
/* Mishearing the voices in his head, our hero wonders how he's
supposed to kill the mall. */
{
int i;
- ip_ct_attach = NULL;
+ rcu_assign_pointer(ip_ct_attach, NULL);
/* This makes sure all current packets have passed through
netfilter framework. Roll on, two-stage module
free_conntrack_hash(nf_conntrack_hash, nf_conntrack_vmalloc,
nf_conntrack_htable_size);
+ nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_generic);
+
/* free l3proto protocol tables */
for (i = 0; i < PF_MAX; i++)
if (nf_ct_protos[i]) {
struct list_head *hash;
unsigned int i;
- *vmalloced = 0;
- hash = (void*)__get_free_pages(GFP_KERNEL,
+ *vmalloced = 0;
+ hash = (void*)__get_free_pages(GFP_KERNEL,
get_order(sizeof(struct list_head)
* size));
- if (!hash) {
+ if (!hash) {
*vmalloced = 1;
printk(KERN_WARNING "nf_conntrack: falling back to vmalloc.\n");
hash = vmalloc(sizeof(struct list_head) * size);
}
if (hash)
- for (i = 0; i < size; i++)
+ for (i = 0; i < size; i++)
INIT_LIST_HEAD(&hash[i]);
return hash;
goto err_free_conntrack_slab;
}
+ ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_generic);
+ if (ret < 0)
+ goto out_free_expect_slab;
+
/* Don't NEED lock here, but good form anyway. */
write_lock_bh(&nf_conntrack_lock);
- for (i = 0; i < AF_MAX; i++)
+ for (i = 0; i < AF_MAX; i++)
nf_ct_l3protos[i] = &nf_conntrack_l3proto_generic;
- write_unlock_bh(&nf_conntrack_lock);
+ write_unlock_bh(&nf_conntrack_lock);
/* For use by REJECT target */
- ip_ct_attach = __nf_conntrack_attach;
+ rcu_assign_pointer(ip_ct_attach, __nf_conntrack_attach);
/* Set up fake conntrack:
- to never be deleted, not in any hashes */
return ret;
+out_free_expect_slab:
+ kmem_cache_destroy(nf_conntrack_expect_cachep);
err_free_conntrack_slab:
nf_conntrack_unregister_cache(NF_CT_F_BASIC);
err_free_hash: