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 120
	struct hlist_node *n;
	unsigned int h;
121

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 336 337
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);

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
__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;

	/* 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,
		      "nf_ct_%s: dropping packet: %s ", helper->name, fmt);
}
EXPORT_SYMBOL_GPL(nf_ct_helper_log);

356 357
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
358 359 360
	int ret = 0;
	struct nf_conntrack_helper *cur;
	struct hlist_node *n;
361 362
	unsigned int h = helper_hash(&me->tuple);

363 364
	BUG_ON(me->expect_policy == NULL);
	BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
365
	BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
366

367
	mutex_lock(&nf_ct_helper_mutex);
368 369 370 371 372 373 374 375
	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;
		}
	}
376
	hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
377
	nf_ct_helper_count++;
378
out:
379
	mutex_unlock(&nf_ct_helper_mutex);
380
	return ret;
381
}
382
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
383

384 385
static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
					     struct net *net)
386 387
{
	struct nf_conntrack_tuple_hash *h;
388
	struct nf_conntrack_expect *exp;
389
	const struct hlist_node *n, *next;
390
	const struct hlist_nulls_node *nn;
391
	unsigned int i;
392 393

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

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

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

432
	rtnl_lock();
433 434 435
	spin_lock_bh(&nf_conntrack_lock);
	for_each_net(net)
		__nf_conntrack_helper_unregister(me, net);
436
	spin_unlock_bh(&nf_conntrack_lock);
437
	rtnl_unlock();
438
}
439
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
440

441
static struct nf_ct_ext_type helper_extend __read_mostly = {
442 443 444 445 446
	.len	= sizeof(struct nf_conn_help),
	.align	= __alignof__(struct nf_conn_help),
	.id	= NF_CT_EXT_HELPER,
};

447
int nf_conntrack_helper_pernet_init(struct net *net)
448
{
449 450
	net->ct.auto_assign_helper_warned = false;
	net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
451 452
	return nf_conntrack_helper_init_sysctl(net);
}
453

454 455 456 457
void nf_conntrack_helper_pernet_fini(struct net *net)
{
	nf_conntrack_helper_fini_sysctl(net);
}
458

459 460 461 462 463 464 465 466 467 468 469 470 471
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;
472 473
	}

474
	return 0;
475
out_extend:
476
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
477
	return ret;
478 479
}

480
void nf_conntrack_helper_fini(void)
481
{
482 483
	nf_ct_extend_unregister(&helper_extend);
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
484
}