nf_conntrack_helper.c 12.5 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
#include <net/netfilter/nf_log.h>
32

33
static DEFINE_MUTEX(nf_ct_helper_mutex);
34 35 36 37
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);
38 39
static unsigned int nf_ct_helper_count __read_mostly;

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

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

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
	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 */
105 106 107 108 109 110

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

114
static struct nf_conntrack_helper *
115 116
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
{
117
	struct nf_conntrack_helper *helper;
118
	struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
119
	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, &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
	unsigned int i;
137

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

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

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

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

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

193 194 195 196 197 198 199 200 201
	/* 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;

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

	help = nfct_help(ct);
211
	if (net->ct.sysctl_auto_assign_helper && helper == NULL) {
212
		helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
213 214 215 216 217 218 219 220 221
		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;
		}
	}

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

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

		if (tmp && tmp->help != helper->help) {
241 242 243
			RCU_INIT_POINTER(help->helper, NULL);
			goto out;
		}
244 245
	}

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

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

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

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

336 337 338 339 340 341
__printf(3, 4)
void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
		      const char *fmt, ...)
{
	const struct nf_conn_help *help;
	const struct nf_conntrack_helper *helper;
342 343 344 345 346 347 348
	struct va_format vaf;
	va_list args;

	va_start(args, fmt);

	vaf.fmt = fmt;
	vaf.va = &args;
349 350 351 352 353 354 355 356

	/* Called from the helper function, this call never fails */
	help = nfct_help(ct);

	/* rcu_read_lock()ed by nf_hook_slow */
	helper = rcu_dereference(help->helper);

	nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
357 358 359
		      "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);

	va_end(args);
360 361 362
}
EXPORT_SYMBOL_GPL(nf_ct_helper_log);

363 364
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
365 366
	int ret = 0;
	struct nf_conntrack_helper *cur;
367 368
	unsigned int h = helper_hash(&me->tuple);

369 370
	BUG_ON(me->expect_policy == NULL);
	BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
371
	BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
372

373
	mutex_lock(&nf_ct_helper_mutex);
374
	hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
375 376 377 378 379 380 381
		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;
		}
	}
382
	hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
383
	nf_ct_helper_count++;
384
out:
385
	mutex_unlock(&nf_ct_helper_mutex);
386
	return ret;
387
}
388
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
389

390 391
static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
					     struct net *net)
392 393
{
	struct nf_conntrack_tuple_hash *h;
394
	struct nf_conntrack_expect *exp;
395
	const struct hlist_node *next;
396
	const struct hlist_nulls_node *nn;
397
	unsigned int i;
398 399

	/* Get rid of expectations */
400
	for (i = 0; i < nf_ct_expect_hsize; i++) {
401
		hlist_for_each_entry_safe(exp, next,
402
					  &net->ct.expect_hash[i], hnode) {
403
			struct nf_conn_help *help = nfct_help(exp->master);
E
Eric Dumazet 已提交
404 405 406 407
			if ((rcu_dereference_protected(
					help->helper,
					lockdep_is_held(&nf_conntrack_lock)
					) == me || exp->helper == me) &&
408 409 410 411
			    del_timer(&exp->timeout)) {
				nf_ct_unlink_expect(exp);
				nf_ct_expect_put(exp);
			}
412 413 414 415
		}
	}

	/* Get rid of expecteds, set helpers to NULL. */
416
	hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode)
417
		unhelp(h, me);
418
	for (i = 0; i < net->ct.htable_size; i++) {
419
		hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
420 421
			unhelp(h, me);
	}
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
}

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

438
	rtnl_lock();
439 440 441
	spin_lock_bh(&nf_conntrack_lock);
	for_each_net(net)
		__nf_conntrack_helper_unregister(me, net);
442
	spin_unlock_bh(&nf_conntrack_lock);
443
	rtnl_unlock();
444
}
445
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
446

447
static struct nf_ct_ext_type helper_extend __read_mostly = {
448 449 450 451 452
	.len	= sizeof(struct nf_conn_help),
	.align	= __alignof__(struct nf_conn_help),
	.id	= NF_CT_EXT_HELPER,
};

453
int nf_conntrack_helper_pernet_init(struct net *net)
454
{
455 456
	net->ct.auto_assign_helper_warned = false;
	net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
457 458
	return nf_conntrack_helper_init_sysctl(net);
}
459

460 461 462 463
void nf_conntrack_helper_pernet_fini(struct net *net)
{
	nf_conntrack_helper_fini_sysctl(net);
}
464

465 466 467 468 469 470 471 472 473 474 475 476 477
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;
478 479
	}

480
	return 0;
481
out_extend:
482
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
483
	return ret;
484 485
}

486
void nf_conntrack_helper_fini(void)
487
{
488 489
	nf_ct_extend_unregister(&helper_extend);
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
490
}