nf_conntrack_helper.c 11.8 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 233 234 235 236 237 238
		/* 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;
		}
239 240
	}

241
	rcu_assign_pointer(help->helper, helper);
242 243 244 245 246
out:
	return ret;
}
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);

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

263 264 265 266 267 268 269 270 271 272 273 274 275 276
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();
	}
}

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 325 326 327 328 329 330
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);

331 332
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
333 334 335
	int ret = 0;
	struct nf_conntrack_helper *cur;
	struct hlist_node *n;
336 337
	unsigned int h = helper_hash(&me->tuple);

338 339
	BUG_ON(me->expect_policy == NULL);
	BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
340
	BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
341

342
	mutex_lock(&nf_ct_helper_mutex);
343 344 345 346 347 348 349 350
	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;
		}
	}
351
	hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
352
	nf_ct_helper_count++;
353
out:
354
	mutex_unlock(&nf_ct_helper_mutex);
355
	return ret;
356
}
357
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
358

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

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

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

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

407
	rtnl_lock();
408 409 410
	spin_lock_bh(&nf_conntrack_lock);
	for_each_net(net)
		__nf_conntrack_helper_unregister(me, net);
411
	spin_unlock_bh(&nf_conntrack_lock);
412
	rtnl_unlock();
413
}
414
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
415

416
static struct nf_ct_ext_type helper_extend __read_mostly = {
417 418 419 420 421
	.len	= sizeof(struct nf_conn_help),
	.align	= __alignof__(struct nf_conn_help),
	.id	= NF_CT_EXT_HELPER,
};

422
int nf_conntrack_helper_init(struct net *net)
423
{
424 425
	int err;

426 427
	net->ct.auto_assign_helper_warned = false;
	net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
428

429 430 431 432 433 434 435 436 437 438 439 440 441
	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);
442
	if (err < 0)
443
		goto out_sysctl;
444 445 446

	return 0;

447 448 449
out_sysctl:
	if (net_eq(net, &init_net))
		nf_ct_extend_unregister(&helper_extend);
450
err1:
451
	nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
452
	return err;
453 454
}

455
void nf_conntrack_helper_fini(struct net *net)
456
{
457 458 459 460 461
	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);
	}
462
}