nf_conntrack_helper.c 8.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* 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/random.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
22
#include <linux/rculist.h>
23
#include <linux/rtnetlink.h>
24 25 26

#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_l3proto.h>
27
#include <net/netfilter/nf_conntrack_l4proto.h>
28 29
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_core.h>
30
#include <net/netfilter/nf_conntrack_extend.h>
31

32
static DEFINE_MUTEX(nf_ct_helper_mutex);
33 34 35 36 37 38 39 40 41 42
static struct hlist_head *nf_ct_helper_hash __read_mostly;
static unsigned int nf_ct_helper_hsize __read_mostly;
static unsigned int nf_ct_helper_count __read_mostly;


/* Stupid hash, but collision free for the default registrations of the
 * helpers currently in the kernel. */
static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
{
	return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^
A
Al Viro 已提交
43
		(__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
44
}
45

46
static struct nf_conntrack_helper *
47 48
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
{
49
	struct nf_conntrack_helper *helper;
50
	struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
51 52
	struct hlist_node *n;
	unsigned int h;
53

54 55 56 57
	if (!nf_ct_helper_count)
		return NULL;

	h = helper_hash(tuple);
58
	hlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) {
59 60
		if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
			return helper;
61 62 63 64 65
	}
	return NULL;
}

struct nf_conntrack_helper *
66
__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
67 68
{
	struct nf_conntrack_helper *h;
69 70
	struct hlist_node *n;
	unsigned int i;
71

72
	for (i = 0; i < nf_ct_helper_hsize; i++) {
73
		hlist_for_each_entry_rcu(h, n, &nf_ct_helper_hash[i], hnode) {
74 75 76
			if (!strcmp(h->name, name) &&
			    h->tuple.src.l3num == l3num &&
			    h->tuple.dst.protonum == protonum)
77 78
				return h;
		}
79 80 81
	}
	return NULL;
}
82
EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
83

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
struct nf_conntrack_helper *
nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
{
	struct nf_conntrack_helper *h;

	h = __nf_conntrack_helper_find(name, l3num, protonum);
#ifdef CONFIG_MODULES
	if (h == NULL) {
		if (request_module("nfct-helper-%s", name) == 0)
			h = __nf_conntrack_helper_find(name, l3num, protonum);
	}
#endif
	if (h != NULL && !try_module_get(h->me))
		h = NULL;

	return h;
}
EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get);

103 104 105 106 107 108 109 110 111 112 113 114 115
struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
{
	struct nf_conn_help *help;

	help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);
	if (help)
		INIT_HLIST_HEAD(&help->expectations);
	else
		pr_debug("failed to add helper extension area");
	return help;
}
EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);

116 117
int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
			      gfp_t flags)
118
{
119 120
	struct nf_conntrack_helper *helper = NULL;
	struct nf_conn_help *help;
121 122
	int ret = 0;

123 124 125 126 127 128 129 130 131
	if (tmpl != NULL) {
		help = nfct_help(tmpl);
		if (help != NULL)
			helper = help->helper;
	}

	help = nfct_help(ct);
	if (helper == NULL)
		helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
132 133
	if (helper == NULL) {
		if (help)
134
			RCU_INIT_POINTER(help->helper, NULL);
135 136 137 138 139 140 141 142 143 144 145 146 147
		goto out;
	}

	if (help == NULL) {
		help = nf_ct_helper_ext_add(ct, flags);
		if (help == NULL) {
			ret = -ENOMEM;
			goto out;
		}
	} else {
		memset(&help->help, 0, sizeof(help->help));
	}

148
	rcu_assign_pointer(help->helper, helper);
149 150 151 152 153
out:
	return ret;
}
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);

154 155 156 157 158 159
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);

E
Eric Dumazet 已提交
160 161 162 163
	if (help && rcu_dereference_protected(
			help->helper,
			lockdep_is_held(&nf_conntrack_lock)
			) == me) {
164
		nf_conntrack_event(IPCT_HELPER, ct);
165
		RCU_INIT_POINTER(help->helper, NULL);
166 167 168 169
	}
	return 0;
}

170 171 172 173 174 175 176 177 178 179 180 181 182 183
void nf_ct_helper_destroy(struct nf_conn *ct)
{
	struct nf_conn_help *help = nfct_help(ct);
	struct nf_conntrack_helper *helper;

	if (help) {
		rcu_read_lock();
		helper = rcu_dereference(help->helper);
		if (helper && helper->destroy)
			helper->destroy(ct);
		rcu_read_unlock();
	}
}

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
static LIST_HEAD(nf_ct_helper_expectfn_list);

void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n)
{
	spin_lock_bh(&nf_conntrack_lock);
	list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
	spin_unlock_bh(&nf_conntrack_lock);
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register);

void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
{
	spin_lock_bh(&nf_conntrack_lock);
	list_del_rcu(&n->head);
	spin_unlock_bh(&nf_conntrack_lock);
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);

struct nf_ct_helper_expectfn *
nf_ct_helper_expectfn_find_by_name(const char *name)
{
	struct nf_ct_helper_expectfn *cur;
	bool found = false;

	rcu_read_lock();
	list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
		if (!strcmp(cur->name, name)) {
			found = true;
			break;
		}
	}
	rcu_read_unlock();
	return found ? cur : NULL;
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name);

struct nf_ct_helper_expectfn *
nf_ct_helper_expectfn_find_by_symbol(const void *symbol)
{
	struct nf_ct_helper_expectfn *cur;
	bool found = false;

	rcu_read_lock();
	list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
		if (cur->expectfn == symbol) {
			found = true;
			break;
		}
	}
	rcu_read_unlock();
	return found ? cur : NULL;
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);

238 239
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
240 241
	unsigned int h = helper_hash(&me->tuple);

242 243
	BUG_ON(me->expect_policy == NULL);
	BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
244
	BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
245

246 247
	mutex_lock(&nf_ct_helper_mutex);
	hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
248
	nf_ct_helper_count++;
249
	mutex_unlock(&nf_ct_helper_mutex);
250 251 252

	return 0;
}
253
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
254

255 256
static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
					     struct net *net)
257 258
{
	struct nf_conntrack_tuple_hash *h;
259
	struct nf_conntrack_expect *exp;
260
	const struct hlist_node *n, *next;
261
	const struct hlist_nulls_node *nn;
262
	unsigned int i;
263 264

	/* Get rid of expectations */
265 266
	for (i = 0; i < nf_ct_expect_hsize; i++) {
		hlist_for_each_entry_safe(exp, n, next,
267
					  &net->ct.expect_hash[i], hnode) {
268
			struct nf_conn_help *help = nfct_help(exp->master);
E
Eric Dumazet 已提交
269 270 271 272
			if ((rcu_dereference_protected(
					help->helper,
					lockdep_is_held(&nf_conntrack_lock)
					) == me || exp->helper == me) &&
273 274 275 276
			    del_timer(&exp->timeout)) {
				nf_ct_unlink_expect(exp);
				nf_ct_expect_put(exp);
			}
277 278 279 280
		}
	}

	/* Get rid of expecteds, set helpers to NULL. */
281
	hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode)
282
		unhelp(h, me);
283
	for (i = 0; i < net->ct.htable_size; i++) {
284
		hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
285 286
			unhelp(h, me);
	}
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
}

void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
{
	struct net *net;

	mutex_lock(&nf_ct_helper_mutex);
	hlist_del_rcu(&me->hnode);
	nf_ct_helper_count--;
	mutex_unlock(&nf_ct_helper_mutex);

	/* Make sure every nothing is still using the helper unless its a
	 * connection in the hash.
	 */
	synchronize_rcu();

303
	rtnl_lock();
304 305 306
	spin_lock_bh(&nf_conntrack_lock);
	for_each_net(net)
		__nf_conntrack_helper_unregister(me, net);
307
	spin_unlock_bh(&nf_conntrack_lock);
308
	rtnl_unlock();
309
}
310
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
311

312
static struct nf_ct_ext_type helper_extend __read_mostly = {
313 314 315 316 317
	.len	= sizeof(struct nf_conn_help),
	.align	= __alignof__(struct nf_conn_help),
	.id	= NF_CT_EXT_HELPER,
};

318
int nf_conntrack_helper_init(void)
319
{
320 321 322
	int err;

	nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
323
	nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
324 325 326 327 328 329 330 331 332 333
	if (!nf_ct_helper_hash)
		return -ENOMEM;

	err = nf_ct_extend_register(&helper_extend);
	if (err < 0)
		goto err1;

	return 0;

err1:
334
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
335
	return err;
336 337
}

338
void nf_conntrack_helper_fini(void)
339 340
{
	nf_ct_extend_unregister(&helper_extend);
341
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
342
}