cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nf_conntrack_helper.c (15130B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* Helper handling for netfilter. */
      3
      4/* (C) 1999-2001 Paul `Rusty' Russell
      5 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
      6 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
      7 * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
      8 */
      9
     10#include <linux/types.h>
     11#include <linux/netfilter.h>
     12#include <linux/module.h>
     13#include <linux/skbuff.h>
     14#include <linux/vmalloc.h>
     15#include <linux/stddef.h>
     16#include <linux/random.h>
     17#include <linux/err.h>
     18#include <linux/kernel.h>
     19#include <linux/netdevice.h>
     20#include <linux/rculist.h>
     21#include <linux/rtnetlink.h>
     22
     23#include <net/netfilter/nf_conntrack.h>
     24#include <net/netfilter/nf_conntrack_core.h>
     25#include <net/netfilter/nf_conntrack_ecache.h>
     26#include <net/netfilter/nf_conntrack_extend.h>
     27#include <net/netfilter/nf_conntrack_helper.h>
     28#include <net/netfilter/nf_conntrack_l4proto.h>
     29#include <net/netfilter/nf_log.h>
     30
     31static DEFINE_MUTEX(nf_ct_helper_mutex);
     32struct hlist_head *nf_ct_helper_hash __read_mostly;
     33EXPORT_SYMBOL_GPL(nf_ct_helper_hash);
     34unsigned int nf_ct_helper_hsize __read_mostly;
     35EXPORT_SYMBOL_GPL(nf_ct_helper_hsize);
     36static unsigned int nf_ct_helper_count __read_mostly;
     37
     38static bool nf_ct_auto_assign_helper __read_mostly = false;
     39module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644);
     40MODULE_PARM_DESC(nf_conntrack_helper,
     41		 "Enable automatic conntrack helper assignment (default 0)");
     42
     43static DEFINE_MUTEX(nf_ct_nat_helpers_mutex);
     44static struct list_head nf_ct_nat_helpers __read_mostly;
     45
     46/* Stupid hash, but collision free for the default registrations of the
     47 * helpers currently in the kernel. */
     48static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
     49{
     50	return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^
     51		(__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
     52}
     53
     54static struct nf_conntrack_helper *
     55__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
     56{
     57	struct nf_conntrack_helper *helper;
     58	struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
     59	unsigned int h;
     60
     61	if (!nf_ct_helper_count)
     62		return NULL;
     63
     64	h = helper_hash(tuple);
     65	hlist_for_each_entry_rcu(helper, &nf_ct_helper_hash[h], hnode) {
     66		if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
     67			return helper;
     68	}
     69	return NULL;
     70}
     71
     72struct nf_conntrack_helper *
     73__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
     74{
     75	struct nf_conntrack_helper *h;
     76	unsigned int i;
     77
     78	for (i = 0; i < nf_ct_helper_hsize; i++) {
     79		hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) {
     80			if (strcmp(h->name, name))
     81				continue;
     82
     83			if (h->tuple.src.l3num != NFPROTO_UNSPEC &&
     84			    h->tuple.src.l3num != l3num)
     85				continue;
     86
     87			if (h->tuple.dst.protonum == protonum)
     88				return h;
     89		}
     90	}
     91	return NULL;
     92}
     93EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
     94
     95struct nf_conntrack_helper *
     96nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
     97{
     98	struct nf_conntrack_helper *h;
     99
    100	rcu_read_lock();
    101
    102	h = __nf_conntrack_helper_find(name, l3num, protonum);
    103#ifdef CONFIG_MODULES
    104	if (h == NULL) {
    105		rcu_read_unlock();
    106		if (request_module("nfct-helper-%s", name) == 0) {
    107			rcu_read_lock();
    108			h = __nf_conntrack_helper_find(name, l3num, protonum);
    109		} else {
    110			return h;
    111		}
    112	}
    113#endif
    114	if (h != NULL && !try_module_get(h->me))
    115		h = NULL;
    116	if (h != NULL && !refcount_inc_not_zero(&h->refcnt)) {
    117		module_put(h->me);
    118		h = NULL;
    119	}
    120
    121	rcu_read_unlock();
    122
    123	return h;
    124}
    125EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get);
    126
    127void nf_conntrack_helper_put(struct nf_conntrack_helper *helper)
    128{
    129	refcount_dec(&helper->refcnt);
    130	module_put(helper->me);
    131}
    132EXPORT_SYMBOL_GPL(nf_conntrack_helper_put);
    133
    134static struct nf_conntrack_nat_helper *
    135nf_conntrack_nat_helper_find(const char *mod_name)
    136{
    137	struct nf_conntrack_nat_helper *cur;
    138	bool found = false;
    139
    140	list_for_each_entry_rcu(cur, &nf_ct_nat_helpers, list) {
    141		if (!strcmp(cur->mod_name, mod_name)) {
    142			found = true;
    143			break;
    144		}
    145	}
    146	return found ? cur : NULL;
    147}
    148
    149int
    150nf_nat_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
    151{
    152	struct nf_conntrack_helper *h;
    153	struct nf_conntrack_nat_helper *nat;
    154	char mod_name[NF_CT_HELPER_NAME_LEN];
    155	int ret = 0;
    156
    157	rcu_read_lock();
    158	h = __nf_conntrack_helper_find(name, l3num, protonum);
    159	if (!h) {
    160		rcu_read_unlock();
    161		return -ENOENT;
    162	}
    163
    164	nat = nf_conntrack_nat_helper_find(h->nat_mod_name);
    165	if (!nat) {
    166		snprintf(mod_name, sizeof(mod_name), "%s", h->nat_mod_name);
    167		rcu_read_unlock();
    168		request_module(mod_name);
    169
    170		rcu_read_lock();
    171		nat = nf_conntrack_nat_helper_find(mod_name);
    172		if (!nat) {
    173			rcu_read_unlock();
    174			return -ENOENT;
    175		}
    176	}
    177
    178	if (!try_module_get(nat->module))
    179		ret = -ENOENT;
    180
    181	rcu_read_unlock();
    182	return ret;
    183}
    184EXPORT_SYMBOL_GPL(nf_nat_helper_try_module_get);
    185
    186void nf_nat_helper_put(struct nf_conntrack_helper *helper)
    187{
    188	struct nf_conntrack_nat_helper *nat;
    189
    190	nat = nf_conntrack_nat_helper_find(helper->nat_mod_name);
    191	if (WARN_ON_ONCE(!nat))
    192		return;
    193
    194	module_put(nat->module);
    195}
    196EXPORT_SYMBOL_GPL(nf_nat_helper_put);
    197
    198struct nf_conn_help *
    199nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
    200{
    201	struct nf_conn_help *help;
    202
    203	help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);
    204	if (help)
    205		INIT_HLIST_HEAD(&help->expectations);
    206	else
    207		pr_debug("failed to add helper extension area");
    208	return help;
    209}
    210EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
    211
    212static struct nf_conntrack_helper *
    213nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
    214{
    215	struct nf_conntrack_net *cnet = nf_ct_pernet(net);
    216
    217	if (!cnet->sysctl_auto_assign_helper) {
    218		if (cnet->auto_assign_helper_warned)
    219			return NULL;
    220		if (!__nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple))
    221			return NULL;
    222		pr_info("nf_conntrack: default automatic helper assignment "
    223			"has been turned off for security reasons and CT-based "
    224			"firewall rule not found. Use the iptables CT target "
    225			"to attach helpers instead.\n");
    226		cnet->auto_assign_helper_warned = true;
    227		return NULL;
    228	}
    229
    230	return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
    231}
    232
    233int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
    234			      gfp_t flags)
    235{
    236	struct nf_conntrack_helper *helper = NULL;
    237	struct nf_conn_help *help;
    238	struct net *net = nf_ct_net(ct);
    239
    240	/* We already got a helper explicitly attached. The function
    241	 * nf_conntrack_alter_reply - in case NAT is in use - asks for looking
    242	 * the helper up again. Since now the user is in full control of
    243	 * making consistent helper configurations, skip this automatic
    244	 * re-lookup, otherwise we'll lose the helper.
    245	 */
    246	if (test_bit(IPS_HELPER_BIT, &ct->status))
    247		return 0;
    248
    249	if (tmpl != NULL) {
    250		help = nfct_help(tmpl);
    251		if (help != NULL) {
    252			helper = help->helper;
    253			set_bit(IPS_HELPER_BIT, &ct->status);
    254		}
    255	}
    256
    257	help = nfct_help(ct);
    258
    259	if (helper == NULL) {
    260		helper = nf_ct_lookup_helper(ct, net);
    261		if (helper == NULL) {
    262			if (help)
    263				RCU_INIT_POINTER(help->helper, NULL);
    264			return 0;
    265		}
    266	}
    267
    268	if (help == NULL) {
    269		help = nf_ct_helper_ext_add(ct, flags);
    270		if (help == NULL)
    271			return -ENOMEM;
    272	} else {
    273		/* We only allow helper re-assignment of the same sort since
    274		 * we cannot reallocate the helper extension area.
    275		 */
    276		struct nf_conntrack_helper *tmp = rcu_dereference(help->helper);
    277
    278		if (tmp && tmp->help != helper->help) {
    279			RCU_INIT_POINTER(help->helper, NULL);
    280			return 0;
    281		}
    282	}
    283
    284	rcu_assign_pointer(help->helper, helper);
    285
    286	return 0;
    287}
    288EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
    289
    290/* appropriate ct lock protecting must be taken by caller */
    291static int unhelp(struct nf_conn *ct, void *me)
    292{
    293	struct nf_conn_help *help = nfct_help(ct);
    294
    295	if (help && rcu_dereference_raw(help->helper) == me) {
    296		nf_conntrack_event(IPCT_HELPER, ct);
    297		RCU_INIT_POINTER(help->helper, NULL);
    298	}
    299
    300	/* We are not intended to delete this conntrack. */
    301	return 0;
    302}
    303
    304void nf_ct_helper_destroy(struct nf_conn *ct)
    305{
    306	struct nf_conn_help *help = nfct_help(ct);
    307	struct nf_conntrack_helper *helper;
    308
    309	if (help) {
    310		rcu_read_lock();
    311		helper = rcu_dereference(help->helper);
    312		if (helper && helper->destroy)
    313			helper->destroy(ct);
    314		rcu_read_unlock();
    315	}
    316}
    317
    318static LIST_HEAD(nf_ct_helper_expectfn_list);
    319
    320void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n)
    321{
    322	spin_lock_bh(&nf_conntrack_expect_lock);
    323	list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
    324	spin_unlock_bh(&nf_conntrack_expect_lock);
    325}
    326EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register);
    327
    328void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
    329{
    330	spin_lock_bh(&nf_conntrack_expect_lock);
    331	list_del_rcu(&n->head);
    332	spin_unlock_bh(&nf_conntrack_expect_lock);
    333}
    334EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
    335
    336/* Caller should hold the rcu lock */
    337struct nf_ct_helper_expectfn *
    338nf_ct_helper_expectfn_find_by_name(const char *name)
    339{
    340	struct nf_ct_helper_expectfn *cur;
    341	bool found = false;
    342
    343	list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
    344		if (!strcmp(cur->name, name)) {
    345			found = true;
    346			break;
    347		}
    348	}
    349	return found ? cur : NULL;
    350}
    351EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name);
    352
    353/* Caller should hold the rcu lock */
    354struct nf_ct_helper_expectfn *
    355nf_ct_helper_expectfn_find_by_symbol(const void *symbol)
    356{
    357	struct nf_ct_helper_expectfn *cur;
    358	bool found = false;
    359
    360	list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
    361		if (cur->expectfn == symbol) {
    362			found = true;
    363			break;
    364		}
    365	}
    366	return found ? cur : NULL;
    367}
    368EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
    369
    370__printf(3, 4)
    371void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
    372		      const char *fmt, ...)
    373{
    374	const struct nf_conn_help *help;
    375	const struct nf_conntrack_helper *helper;
    376	struct va_format vaf;
    377	va_list args;
    378
    379	va_start(args, fmt);
    380
    381	vaf.fmt = fmt;
    382	vaf.va = &args;
    383
    384	/* Called from the helper function, this call never fails */
    385	help = nfct_help(ct);
    386
    387	/* rcu_read_lock()ed by nf_hook_thresh */
    388	helper = rcu_dereference(help->helper);
    389
    390	nf_log_packet(nf_ct_net(ct), nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
    391		      "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);
    392
    393	va_end(args);
    394}
    395EXPORT_SYMBOL_GPL(nf_ct_helper_log);
    396
    397int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
    398{
    399	struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
    400	unsigned int h = helper_hash(&me->tuple);
    401	struct nf_conntrack_helper *cur;
    402	int ret = 0, i;
    403
    404	BUG_ON(me->expect_policy == NULL);
    405	BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
    406	BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
    407
    408	if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
    409		return -EINVAL;
    410
    411	mutex_lock(&nf_ct_helper_mutex);
    412	for (i = 0; i < nf_ct_helper_hsize; i++) {
    413		hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
    414			if (!strcmp(cur->name, me->name) &&
    415			    (cur->tuple.src.l3num == NFPROTO_UNSPEC ||
    416			     cur->tuple.src.l3num == me->tuple.src.l3num) &&
    417			    cur->tuple.dst.protonum == me->tuple.dst.protonum) {
    418				ret = -EEXIST;
    419				goto out;
    420			}
    421		}
    422	}
    423
    424	/* avoid unpredictable behaviour for auto_assign_helper */
    425	if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
    426		hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
    427			if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
    428						     &mask)) {
    429				ret = -EEXIST;
    430				goto out;
    431			}
    432		}
    433	}
    434	refcount_set(&me->refcnt, 1);
    435	hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
    436	nf_ct_helper_count++;
    437out:
    438	mutex_unlock(&nf_ct_helper_mutex);
    439	return ret;
    440}
    441EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
    442
    443static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
    444{
    445	struct nf_conn_help *help = nfct_help(exp->master);
    446	const struct nf_conntrack_helper *me = data;
    447	const struct nf_conntrack_helper *this;
    448
    449	if (exp->helper == me)
    450		return true;
    451
    452	this = rcu_dereference_protected(help->helper,
    453					 lockdep_is_held(&nf_conntrack_expect_lock));
    454	return this == me;
    455}
    456
    457void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
    458{
    459	mutex_lock(&nf_ct_helper_mutex);
    460	hlist_del_rcu(&me->hnode);
    461	nf_ct_helper_count--;
    462	mutex_unlock(&nf_ct_helper_mutex);
    463
    464	/* Make sure every nothing is still using the helper unless its a
    465	 * connection in the hash.
    466	 */
    467	synchronize_rcu();
    468
    469	nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
    470	nf_ct_iterate_destroy(unhelp, me);
    471}
    472EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
    473
    474void nf_ct_helper_init(struct nf_conntrack_helper *helper,
    475		       u16 l3num, u16 protonum, const char *name,
    476		       u16 default_port, u16 spec_port, u32 id,
    477		       const struct nf_conntrack_expect_policy *exp_pol,
    478		       u32 expect_class_max,
    479		       int (*help)(struct sk_buff *skb, unsigned int protoff,
    480				   struct nf_conn *ct,
    481				   enum ip_conntrack_info ctinfo),
    482		       int (*from_nlattr)(struct nlattr *attr,
    483					  struct nf_conn *ct),
    484		       struct module *module)
    485{
    486	helper->tuple.src.l3num = l3num;
    487	helper->tuple.dst.protonum = protonum;
    488	helper->tuple.src.u.all = htons(spec_port);
    489	helper->expect_policy = exp_pol;
    490	helper->expect_class_max = expect_class_max;
    491	helper->help = help;
    492	helper->from_nlattr = from_nlattr;
    493	helper->me = module;
    494	snprintf(helper->nat_mod_name, sizeof(helper->nat_mod_name),
    495		 NF_NAT_HELPER_PREFIX "%s", name);
    496
    497	if (spec_port == default_port)
    498		snprintf(helper->name, sizeof(helper->name), "%s", name);
    499	else
    500		snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id);
    501}
    502EXPORT_SYMBOL_GPL(nf_ct_helper_init);
    503
    504int nf_conntrack_helpers_register(struct nf_conntrack_helper *helper,
    505				  unsigned int n)
    506{
    507	unsigned int i;
    508	int err = 0;
    509
    510	for (i = 0; i < n; i++) {
    511		err = nf_conntrack_helper_register(&helper[i]);
    512		if (err < 0)
    513			goto err;
    514	}
    515
    516	return err;
    517err:
    518	if (i > 0)
    519		nf_conntrack_helpers_unregister(helper, i);
    520	return err;
    521}
    522EXPORT_SYMBOL_GPL(nf_conntrack_helpers_register);
    523
    524void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper,
    525				unsigned int n)
    526{
    527	while (n-- > 0)
    528		nf_conntrack_helper_unregister(&helper[n]);
    529}
    530EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister);
    531
    532void nf_nat_helper_register(struct nf_conntrack_nat_helper *nat)
    533{
    534	mutex_lock(&nf_ct_nat_helpers_mutex);
    535	list_add_rcu(&nat->list, &nf_ct_nat_helpers);
    536	mutex_unlock(&nf_ct_nat_helpers_mutex);
    537}
    538EXPORT_SYMBOL_GPL(nf_nat_helper_register);
    539
    540void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat)
    541{
    542	mutex_lock(&nf_ct_nat_helpers_mutex);
    543	list_del_rcu(&nat->list);
    544	mutex_unlock(&nf_ct_nat_helpers_mutex);
    545}
    546EXPORT_SYMBOL_GPL(nf_nat_helper_unregister);
    547
    548void nf_ct_set_auto_assign_helper_warned(struct net *net)
    549{
    550	nf_ct_pernet(net)->auto_assign_helper_warned = true;
    551}
    552EXPORT_SYMBOL_GPL(nf_ct_set_auto_assign_helper_warned);
    553
    554void nf_conntrack_helper_pernet_init(struct net *net)
    555{
    556	struct nf_conntrack_net *cnet = nf_ct_pernet(net);
    557
    558	cnet->sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
    559}
    560
    561int nf_conntrack_helper_init(void)
    562{
    563	nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
    564	nf_ct_helper_hash =
    565		nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
    566	if (!nf_ct_helper_hash)
    567		return -ENOMEM;
    568
    569	INIT_LIST_HEAD(&nf_ct_nat_helpers);
    570	return 0;
    571}
    572
    573void nf_conntrack_helper_fini(void)
    574{
    575	kvfree(nf_ct_helper_hash);
    576}