nf_conntrack_helper.c 11.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
/* 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
struct hlist_head *nf_ct_helper_hash __read_mostly;
EXPORT_SYMBOL_GPL(nf_ct_helper_hash);
unsigned int nf_ct_helper_hsize __read_mostly;
EXPORT_SYMBOL_GPL(nf_ct_helper_hsize);
37 38
static unsigned int nf_ct_helper_count __read_mostly;

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
static bool nf_ct_auto_assign_helper __read_mostly = true;
module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644);
MODULE_PARM_DESC(nf_conntrack_helper,
		 "Enable automatic conntrack helper assignment (default 1)");

#ifdef CONFIG_SYSCTL
static struct ctl_table helper_sysctl_table[] = {
	{
		.procname	= "nf_conntrack_helper",
		.data		= &init_net.ct.sysctl_auto_assign_helper,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec,
	},
	{}
};

static int nf_conntrack_helper_init_sysctl(struct net *net)
{
	struct ctl_table *table;

	table = kmemdup(helper_sysctl_table, sizeof(helper_sysctl_table),
			GFP_KERNEL);
	if (!table)
		goto out;

	table[0].data = &net->ct.sysctl_auto_assign_helper;

67 68 69 70
	/* Don't export sysctls to unprivileged users */
	if (net->user_ns != &init_user_ns)
		table[0].procname = NULL;

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
	net->ct.helper_sysctl_header =
		register_net_sysctl(net, "net/netfilter", table);

	if (!net->ct.helper_sysctl_header) {
		pr_err("nf_conntrack_helper: can't register to sysctl.\n");
		goto out_register;
	}
	return 0;

out_register:
	kfree(table);
out:
	return -ENOMEM;
}

static void nf_conntrack_helper_fini_sysctl(struct net *net)
{
	struct ctl_table *table;

	table = net->ct.helper_sysctl_header->ctl_table_arg;
	unregister_net_sysctl_table(net->ct.helper_sysctl_header);
	kfree(table);
}
#else
static int nf_conntrack_helper_init_sysctl(struct net *net)
{
	return 0;
}

static void nf_conntrack_helper_fini_sysctl(struct net *net)
{
}
#endif /* CONFIG_SYSCTL */
104 105 106 107 108 109

/* 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 已提交
110
		(__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
111
}
112

113
static struct nf_conntrack_helper *
114 115
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
{
116
	struct nf_conntrack_helper *helper;
117
	struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
118 119
	struct hlist_node *n;
	unsigned int h;
120

121 122 123 124
	if (!nf_ct_helper_count)
		return NULL;

	h = helper_hash(tuple);
125
	hlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) {
126 127
		if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
			return helper;
128 129 130 131 132
	}
	return NULL;
}

struct nf_conntrack_helper *
133
__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
134 135
{
	struct nf_conntrack_helper *h;
136 137
	struct hlist_node *n;
	unsigned int i;
138

139
	for (i = 0; i < nf_ct_helper_hsize; i++) {
140
		hlist_for_each_entry_rcu(h, n, &nf_ct_helper_hash[i], hnode) {
141 142 143
			if (!strcmp(h->name, name) &&
			    h->tuple.src.l3num == l3num &&
			    h->tuple.dst.protonum == protonum)
144 145
				return h;
		}
146 147 148
	}
	return NULL;
}
149
EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
150

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
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);

170 171 172
struct nf_conn_help *
nf_ct_helper_ext_add(struct nf_conn *ct,
		     struct nf_conntrack_helper *helper, gfp_t gfp)
173 174 175
{
	struct nf_conn_help *help;

176 177
	help = nf_ct_ext_add_length(ct, NF_CT_EXT_HELPER,
				    helper->data_len, gfp);
178 179 180 181 182 183 184 185
	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);

186 187
int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
			      gfp_t flags)
188
{
189 190
	struct nf_conntrack_helper *helper = NULL;
	struct nf_conn_help *help;
191
	struct net *net = nf_ct_net(ct);
192 193
	int ret = 0;

194 195 196 197 198 199 200 201 202
	/* We already got a helper explicitly attached. The function
	 * nf_conntrack_alter_reply - in case NAT is in use - asks for looking
	 * the helper up again. Since now the user is in full control of
	 * making consistent helper configurations, skip this automatic
	 * re-lookup, otherwise we'll lose the helper.
	 */
	if (test_bit(IPS_HELPER_BIT, &ct->status))
		return 0;

203 204
	if (tmpl != NULL) {
		help = nfct_help(tmpl);
205
		if (help != NULL) {
206
			helper = help->helper;
207 208
			set_bit(IPS_HELPER_BIT, &ct->status);
		}
209 210 211
	}

	help = nfct_help(ct);
212
	if (net->ct.sysctl_auto_assign_helper && helper == NULL) {
213
		helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
214 215 216 217 218 219 220 221 222
		if (unlikely(!net->ct.auto_assign_helper_warned && helper)) {
			pr_info("nf_conntrack: automatic helper "
				"assignment is deprecated and it will "
				"be removed soon. Use the iptables CT target "
				"to attach helpers instead.\n");
			net->ct.auto_assign_helper_warned = true;
		}
	}

223 224
	if (helper == NULL) {
		if (help)
225
			RCU_INIT_POINTER(help->helper, NULL);
226 227 228 229
		goto out;
	}

	if (help == NULL) {
230
		help = nf_ct_helper_ext_add(ct, helper, flags);
231 232 233 234 235
		if (help == NULL) {
			ret = -ENOMEM;
			goto out;
		}
	} else {
236 237 238 239 240 241 242
		/* We only allow helper re-assignment of the same sort since
		 * we cannot reallocate the helper extension area.
		 */
		if (help->helper != helper) {
			RCU_INIT_POINTER(help->helper, NULL);
			goto out;
		}
243 244
	}

245
	rcu_assign_pointer(help->helper, helper);
246 247 248 249 250
out:
	return ret;
}
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);

251 252 253 254 255 256
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 已提交
257 258 259 260
	if (help && rcu_dereference_protected(
			help->helper,
			lockdep_is_held(&nf_conntrack_lock)
			) == me) {
261
		nf_conntrack_event(IPCT_HELPER, ct);
262
		RCU_INIT_POINTER(help->helper, NULL);
263 264 265 266
	}
	return 0;
}

267 268 269 270 271 272 273 274 275 276 277 278 279 280
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();
	}
}

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
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);

335 336
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
337 338 339
	int ret = 0;
	struct nf_conntrack_helper *cur;
	struct hlist_node *n;
340 341
	unsigned int h = helper_hash(&me->tuple);

342 343
	BUG_ON(me->expect_policy == NULL);
	BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
344
	BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
345

346
	mutex_lock(&nf_ct_helper_mutex);
347 348 349 350 351 352 353 354
	hlist_for_each_entry(cur, n, &nf_ct_helper_hash[h], hnode) {
		if (strncmp(cur->name, me->name, NF_CT_HELPER_NAME_LEN) == 0 &&
		    cur->tuple.src.l3num == me->tuple.src.l3num &&
		    cur->tuple.dst.protonum == me->tuple.dst.protonum) {
			ret = -EEXIST;
			goto out;
		}
	}
355
	hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
356
	nf_ct_helper_count++;
357
out:
358
	mutex_unlock(&nf_ct_helper_mutex);
359
	return ret;
360
}
361
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
362

363 364
static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
					     struct net *net)
365 366
{
	struct nf_conntrack_tuple_hash *h;
367
	struct nf_conntrack_expect *exp;
368
	const struct hlist_node *n, *next;
369
	const struct hlist_nulls_node *nn;
370
	unsigned int i;
371 372

	/* Get rid of expectations */
373 374
	for (i = 0; i < nf_ct_expect_hsize; i++) {
		hlist_for_each_entry_safe(exp, n, next,
375
					  &net->ct.expect_hash[i], hnode) {
376
			struct nf_conn_help *help = nfct_help(exp->master);
E
Eric Dumazet 已提交
377 378 379 380
			if ((rcu_dereference_protected(
					help->helper,
					lockdep_is_held(&nf_conntrack_lock)
					) == me || exp->helper == me) &&
381 382 383 384
			    del_timer(&exp->timeout)) {
				nf_ct_unlink_expect(exp);
				nf_ct_expect_put(exp);
			}
385 386 387 388
		}
	}

	/* Get rid of expecteds, set helpers to NULL. */
389
	hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode)
390
		unhelp(h, me);
391
	for (i = 0; i < net->ct.htable_size; i++) {
392
		hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
393 394
			unhelp(h, me);
	}
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
}

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();

411
	rtnl_lock();
412 413 414
	spin_lock_bh(&nf_conntrack_lock);
	for_each_net(net)
		__nf_conntrack_helper_unregister(me, net);
415
	spin_unlock_bh(&nf_conntrack_lock);
416
	rtnl_unlock();
417
}
418
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
419

420
static struct nf_ct_ext_type helper_extend __read_mostly = {
421 422 423 424 425
	.len	= sizeof(struct nf_conn_help),
	.align	= __alignof__(struct nf_conn_help),
	.id	= NF_CT_EXT_HELPER,
};

426
int nf_conntrack_helper_pernet_init(struct net *net)
427
{
428 429
	net->ct.auto_assign_helper_warned = false;
	net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
430 431
	return nf_conntrack_helper_init_sysctl(net);
}
432

433 434 435 436
void nf_conntrack_helper_pernet_fini(struct net *net)
{
	nf_conntrack_helper_fini_sysctl(net);
}
437

438 439 440 441 442 443 444 445 446 447 448 449 450
int nf_conntrack_helper_init(void)
{
	int ret;
	nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
	nf_ct_helper_hash =
		nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
	if (!nf_ct_helper_hash)
		return -ENOMEM;

	ret = nf_ct_extend_register(&helper_extend);
	if (ret < 0) {
		pr_err("nf_ct_helper: Unable to register helper extension.\n");
		goto out_extend;
451 452
	}

453
	return 0;
454
out_extend:
455
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
456
	return ret;
457 458
}

459
void nf_conntrack_helper_fini(void)
460
{
461 462
	nf_ct_extend_unregister(&helper_extend);
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
463
}