nf_conntrack_helper.c 3.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/* Helper handling for netfilter. */

/* (C) 1999-2001 Paul `Rusty' Russell
 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/types.h>
#include <linux/netfilter.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/vmalloc.h>
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>

#define ASSERT_READ_LOCK(x)
#define ASSERT_WRITE_LOCK(x)

#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_protocol.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_core.h>

33
static __read_mostly LIST_HEAD(helpers);
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

struct nf_conntrack_helper *
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
{
	struct nf_conntrack_helper *h;

	list_for_each_entry(h, &helpers, list) {
		if (nf_ct_tuple_mask_cmp(tuple, &h->tuple, &h->mask))
			return h;
	}
	return NULL;
}

struct nf_conntrack_helper *
nf_ct_helper_find_get( const struct nf_conntrack_tuple *tuple)
{
	struct nf_conntrack_helper *helper;

	/* need nf_conntrack_lock to assure that helper exists until
	 * try_module_get() is called */
	read_lock_bh(&nf_conntrack_lock);

	helper = __nf_ct_helper_find(tuple);
	if (helper) {
		/* need to increase module usage count to assure helper will
		 * not go away while the caller is e.g. busy putting a
		 * conntrack in the hash that uses the helper */
		if (!try_module_get(helper->me))
			helper = NULL;
	}

	read_unlock_bh(&nf_conntrack_lock);

	return helper;
}

void nf_ct_helper_put(struct nf_conntrack_helper *helper)
{
	module_put(helper->me);
}

struct nf_conntrack_helper *
__nf_conntrack_helper_find_byname(const char *name)
{
	struct nf_conntrack_helper *h;

	list_for_each_entry(h, &helpers, list) {
		if (!strcmp(h->name, name))
			return h;
	}

	return NULL;
}

static inline int unhelp(struct nf_conntrack_tuple_hash *i,
			 const struct nf_conntrack_helper *me)
{
	struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i);
	struct nf_conn_help *help = nfct_help(ct);

	if (help && help->helper == me) {
		nf_conntrack_event(IPCT_HELPER, ct);
		help->helper = NULL;
	}
	return 0;
}

int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
	int ret;
	BUG_ON(me->timeout == 0);

	ret = nf_conntrack_register_cache(NF_CT_F_HELP, "nf_conntrack:help",
					  sizeof(struct nf_conn)
					  + sizeof(struct nf_conn_help)
					  + __alignof__(struct nf_conn_help));
	if (ret < 0) {
		printk(KERN_ERR "nf_conntrack_helper_register: Unable to create slab cache for conntracks\n");
		return ret;
	}
	write_lock_bh(&nf_conntrack_lock);
	list_add(&me->list, &helpers);
	write_unlock_bh(&nf_conntrack_lock);

	return 0;
}

void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
{
	unsigned int i;
	struct nf_conntrack_tuple_hash *h;
	struct nf_conntrack_expect *exp, *tmp;

	/* Need write lock here, to delete helper. */
	write_lock_bh(&nf_conntrack_lock);
	list_del(&me->list);

	/* Get rid of expectations */
	list_for_each_entry_safe(exp, tmp, &nf_conntrack_expect_list, list) {
		struct nf_conn_help *help = nfct_help(exp->master);
		if (help->helper == me && del_timer(&exp->timeout)) {
			nf_ct_unlink_expect(exp);
			nf_conntrack_expect_put(exp);
		}
	}

	/* Get rid of expecteds, set helpers to NULL. */
	list_for_each_entry(h, &unconfirmed, list)
		unhelp(h, me);
	for (i = 0; i < nf_conntrack_htable_size; i++) {
		list_for_each_entry(h, &nf_conntrack_hash[i], list)
			unhelp(h, me);
	}
	write_unlock_bh(&nf_conntrack_lock);

	/* Someone could be still looking at the helper in a bh. */
	synchronize_net();
}