nf_conntrack_helper.c 11.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
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 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
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;

	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 */
100 101 102 103 104 105

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

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

117 118 119 120
	if (!nf_ct_helper_count)
		return NULL;

	h = helper_hash(tuple);
121
	hlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) {
122 123
		if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
			return helper;
124 125 126 127 128
	}
	return NULL;
}

struct nf_conntrack_helper *
129
__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
130 131
{
	struct nf_conntrack_helper *h;
132 133
	struct hlist_node *n;
	unsigned int i;
134

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

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

166 167 168
struct nf_conn_help *
nf_ct_helper_ext_add(struct nf_conn *ct,
		     struct nf_conntrack_helper *helper, gfp_t gfp)
169 170 171
{
	struct nf_conn_help *help;

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

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

190 191 192 193 194 195 196 197 198
	/* 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;

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

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

219 220
	if (helper == NULL) {
		if (help)
221
			RCU_INIT_POINTER(help->helper, NULL);
222 223 224 225
		goto out;
	}

	if (help == NULL) {
226
		help = nf_ct_helper_ext_add(ct, helper, flags);
227 228 229 230 231
		if (help == NULL) {
			ret = -ENOMEM;
			goto out;
		}
	} else {
232
		memset(help->data, 0, helper->data_len);
233 234
	}

235
	rcu_assign_pointer(help->helper, helper);
236 237 238 239 240
out:
	return ret;
}
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);

241 242 243 244 245 246
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 已提交
247 248 249 250
	if (help && rcu_dereference_protected(
			help->helper,
			lockdep_is_held(&nf_conntrack_lock)
			) == me) {
251
		nf_conntrack_event(IPCT_HELPER, ct);
252
		RCU_INIT_POINTER(help->helper, NULL);
253 254 255 256
	}
	return 0;
}

257 258 259 260 261 262 263 264 265 266 267 268 269 270
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();
	}
}

271 272 273 274 275 276 277 278 279 280 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
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);

325 326
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
327 328 329
	int ret = 0;
	struct nf_conntrack_helper *cur;
	struct hlist_node *n;
330 331
	unsigned int h = helper_hash(&me->tuple);

332 333
	BUG_ON(me->expect_policy == NULL);
	BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
334
	BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
335

336
	mutex_lock(&nf_ct_helper_mutex);
337 338 339 340 341 342 343 344
	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;
		}
	}
345
	hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
346
	nf_ct_helper_count++;
347
out:
348
	mutex_unlock(&nf_ct_helper_mutex);
349
	return ret;
350
}
351
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
352

353 354
static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
					     struct net *net)
355 356
{
	struct nf_conntrack_tuple_hash *h;
357
	struct nf_conntrack_expect *exp;
358
	const struct hlist_node *n, *next;
359
	const struct hlist_nulls_node *nn;
360
	unsigned int i;
361 362

	/* Get rid of expectations */
363 364
	for (i = 0; i < nf_ct_expect_hsize; i++) {
		hlist_for_each_entry_safe(exp, n, next,
365
					  &net->ct.expect_hash[i], hnode) {
366
			struct nf_conn_help *help = nfct_help(exp->master);
E
Eric Dumazet 已提交
367 368 369 370
			if ((rcu_dereference_protected(
					help->helper,
					lockdep_is_held(&nf_conntrack_lock)
					) == me || exp->helper == me) &&
371 372 373 374
			    del_timer(&exp->timeout)) {
				nf_ct_unlink_expect(exp);
				nf_ct_expect_put(exp);
			}
375 376 377 378
		}
	}

	/* Get rid of expecteds, set helpers to NULL. */
379
	hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode)
380
		unhelp(h, me);
381
	for (i = 0; i < net->ct.htable_size; i++) {
382
		hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
383 384
			unhelp(h, me);
	}
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
}

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

401
	rtnl_lock();
402 403 404
	spin_lock_bh(&nf_conntrack_lock);
	for_each_net(net)
		__nf_conntrack_helper_unregister(me, net);
405
	spin_unlock_bh(&nf_conntrack_lock);
406
	rtnl_unlock();
407
}
408
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
409

410
static struct nf_ct_ext_type helper_extend __read_mostly = {
411 412 413 414 415
	.len	= sizeof(struct nf_conn_help),
	.align	= __alignof__(struct nf_conn_help),
	.id	= NF_CT_EXT_HELPER,
};

416
int nf_conntrack_helper_init(struct net *net)
417
{
418 419
	int err;

420 421
	net->ct.auto_assign_helper_warned = false;
	net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
422

423 424 425 426 427 428 429 430 431 432 433 434 435
	if (net_eq(net, &init_net)) {
		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;

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

	err = nf_conntrack_helper_init_sysctl(net);
436
	if (err < 0)
437
		goto out_sysctl;
438 439 440

	return 0;

441 442 443
out_sysctl:
	if (net_eq(net, &init_net))
		nf_ct_extend_unregister(&helper_extend);
444
err1:
445
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
446
	return err;
447 448
}

449
void nf_conntrack_helper_fini(struct net *net)
450
{
451 452 453 454 455
	nf_conntrack_helper_fini_sysctl(net);
	if (net_eq(net, &init_net)) {
		nf_ct_extend_unregister(&helper_extend);
		nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
	}
456
}