2019-06-04 08:11:33 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2006-11-29 01:34:59 +00:00
|
|
|
/* 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>
|
2013-04-06 13:24:29 +00:00
|
|
|
* (C) 2006-2012 Patrick McHardy <kaber@trash.net>
|
2006-11-29 01:34:59 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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>
|
2008-05-13 13:37:05 +00:00
|
|
|
#include <linux/rculist.h>
|
2008-11-05 11:03:18 +00:00
|
|
|
#include <linux/rtnetlink.h>
|
2006-11-29 01:34:59 +00:00
|
|
|
|
|
|
|
#include <net/netfilter/nf_conntrack.h>
|
|
|
|
#include <net/netfilter/nf_conntrack_core.h>
|
2019-09-13 08:13:05 +00:00
|
|
|
#include <net/netfilter/nf_conntrack_ecache.h>
|
2007-07-08 05:23:42 +00:00
|
|
|
#include <net/netfilter/nf_conntrack_extend.h>
|
2019-09-13 08:13:05 +00:00
|
|
|
#include <net/netfilter/nf_conntrack_helper.h>
|
|
|
|
#include <net/netfilter/nf_conntrack_l4proto.h>
|
2022-11-06 20:34:14 +00:00
|
|
|
#include <net/netfilter/nf_conntrack_seqadj.h>
|
2013-02-10 17:56:56 +00:00
|
|
|
#include <net/netfilter/nf_log.h>
|
2022-11-06 20:34:14 +00:00
|
|
|
#include <net/ip.h>
|
2006-11-29 01:34:59 +00:00
|
|
|
|
2008-01-31 12:36:54 +00:00
|
|
|
static DEFINE_MUTEX(nf_ct_helper_mutex);
|
2012-05-13 19:44:54 +00:00
|
|
|
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);
|
2007-07-08 05:36:46 +00:00
|
|
|
static unsigned int nf_ct_helper_count __read_mostly;
|
|
|
|
|
2019-04-17 14:46:15 +00:00
|
|
|
static DEFINE_MUTEX(nf_ct_nat_helpers_mutex);
|
|
|
|
static struct list_head nf_ct_nat_helpers __read_mostly;
|
|
|
|
|
2007-07-08 05:36:46 +00:00
|
|
|
/* 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) ^
|
2007-07-26 16:33:19 +00:00
|
|
|
(__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
|
2007-07-08 05:36:46 +00:00
|
|
|
}
|
2006-11-29 01:34:59 +00:00
|
|
|
|
|
|
|
struct nf_conntrack_helper *
|
2010-02-03 12:41:29 +00:00
|
|
|
__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
|
2006-11-29 01:34:59 +00:00
|
|
|
{
|
|
|
|
struct nf_conntrack_helper *h;
|
2007-07-08 05:36:46 +00:00
|
|
|
unsigned int i;
|
2006-11-29 01:34:59 +00:00
|
|
|
|
2007-07-08 05:36:46 +00:00
|
|
|
for (i = 0; i < nf_ct_helper_hsize; i++) {
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 01:06:00 +00:00
|
|
|
hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) {
|
2016-11-03 13:44:42 +00:00
|
|
|
if (strcmp(h->name, name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (h->tuple.src.l3num != NFPROTO_UNSPEC &&
|
|
|
|
h->tuple.src.l3num != l3num)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (h->tuple.dst.protonum == protonum)
|
2007-07-08 05:36:46 +00:00
|
|
|
return h;
|
|
|
|
}
|
2006-11-29 01:34:59 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-02-03 12:41:29 +00:00
|
|
|
EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
|
2006-11-29 01:34:59 +00:00
|
|
|
|
2010-02-03 16:17:06 +00:00
|
|
|
struct nf_conntrack_helper *
|
|
|
|
nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
|
|
|
|
{
|
|
|
|
struct nf_conntrack_helper *h;
|
|
|
|
|
2017-03-29 11:11:27 +00:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2010-02-03 16:17:06 +00:00
|
|
|
h = __nf_conntrack_helper_find(name, l3num, protonum);
|
|
|
|
#ifdef CONFIG_MODULES
|
|
|
|
if (h == NULL) {
|
2017-03-29 11:11:27 +00:00
|
|
|
rcu_read_unlock();
|
|
|
|
if (request_module("nfct-helper-%s", name) == 0) {
|
|
|
|
rcu_read_lock();
|
2010-02-03 16:17:06 +00:00
|
|
|
h = __nf_conntrack_helper_find(name, l3num, protonum);
|
2017-03-29 11:11:27 +00:00
|
|
|
} else {
|
|
|
|
return h;
|
|
|
|
}
|
2010-02-03 16:17:06 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (h != NULL && !try_module_get(h->me))
|
|
|
|
h = NULL;
|
2017-05-07 14:01:56 +00:00
|
|
|
if (h != NULL && !refcount_inc_not_zero(&h->refcnt)) {
|
|
|
|
module_put(h->me);
|
|
|
|
h = NULL;
|
|
|
|
}
|
2010-02-03 16:17:06 +00:00
|
|
|
|
2017-03-29 11:11:27 +00:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2010-02-03 16:17:06 +00:00
|
|
|
return h;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get);
|
|
|
|
|
2017-05-07 14:01:55 +00:00
|
|
|
void nf_conntrack_helper_put(struct nf_conntrack_helper *helper)
|
|
|
|
{
|
2017-05-07 14:01:56 +00:00
|
|
|
refcount_dec(&helper->refcnt);
|
2017-05-07 14:01:55 +00:00
|
|
|
module_put(helper->me);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_helper_put);
|
|
|
|
|
2019-04-17 14:46:15 +00:00
|
|
|
static struct nf_conntrack_nat_helper *
|
|
|
|
nf_conntrack_nat_helper_find(const char *mod_name)
|
|
|
|
{
|
|
|
|
struct nf_conntrack_nat_helper *cur;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
list_for_each_entry_rcu(cur, &nf_ct_nat_helpers, list) {
|
|
|
|
if (!strcmp(cur->mod_name, mod_name)) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found ? cur : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nf_nat_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
|
|
|
|
{
|
|
|
|
struct nf_conntrack_helper *h;
|
|
|
|
struct nf_conntrack_nat_helper *nat;
|
|
|
|
char mod_name[NF_CT_HELPER_NAME_LEN];
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
h = __nf_conntrack_helper_find(name, l3num, protonum);
|
|
|
|
if (!h) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
nat = nf_conntrack_nat_helper_find(h->nat_mod_name);
|
|
|
|
if (!nat) {
|
|
|
|
snprintf(mod_name, sizeof(mod_name), "%s", h->nat_mod_name);
|
|
|
|
rcu_read_unlock();
|
2022-06-09 22:16:31 +00:00
|
|
|
request_module("%s", mod_name);
|
2019-04-17 14:46:15 +00:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
nat = nf_conntrack_nat_helper_find(mod_name);
|
|
|
|
if (!nat) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!try_module_get(nat->module))
|
|
|
|
ret = -ENOENT;
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_nat_helper_try_module_get);
|
|
|
|
|
|
|
|
void nf_nat_helper_put(struct nf_conntrack_helper *helper)
|
|
|
|
{
|
|
|
|
struct nf_conntrack_nat_helper *nat;
|
|
|
|
|
|
|
|
nat = nf_conntrack_nat_helper_find(helper->nat_mod_name);
|
|
|
|
if (WARN_ON_ONCE(!nat))
|
|
|
|
return;
|
|
|
|
|
|
|
|
module_put(nat->module);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_nat_helper_put);
|
|
|
|
|
2012-06-07 10:11:50 +00:00
|
|
|
struct nf_conn_help *
|
2018-07-09 10:06:33 +00:00
|
|
|
nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
|
2007-07-08 05:35:56 +00:00
|
|
|
{
|
|
|
|
struct nf_conn_help *help;
|
|
|
|
|
2017-04-15 23:29:17 +00:00
|
|
|
help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);
|
2007-07-08 05:35:56 +00:00
|
|
|
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);
|
|
|
|
|
2010-02-03 13:13:03 +00:00
|
|
|
int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
|
|
|
|
gfp_t flags)
|
2008-11-18 10:54:05 +00:00
|
|
|
{
|
2010-02-03 13:13:03 +00:00
|
|
|
struct nf_conntrack_helper *helper = NULL;
|
|
|
|
struct nf_conn_help *help;
|
2008-11-18 10:54:05 +00:00
|
|
|
|
2012-05-03 02:17:45 +00:00
|
|
|
/* 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;
|
|
|
|
|
2022-08-26 06:49:16 +00:00
|
|
|
if (WARN_ON_ONCE(!tmpl))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
help = nfct_help(tmpl);
|
|
|
|
if (help != NULL) {
|
|
|
|
helper = rcu_dereference(help->helper);
|
|
|
|
set_bit(IPS_HELPER_BIT, &ct->status);
|
2010-02-03 13:13:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
help = nfct_help(ct);
|
2012-04-18 09:20:41 +00:00
|
|
|
|
2008-11-18 10:54:05 +00:00
|
|
|
if (helper == NULL) {
|
2022-08-26 06:49:16 +00:00
|
|
|
if (help)
|
|
|
|
RCU_INIT_POINTER(help->helper, NULL);
|
|
|
|
return 0;
|
2008-11-18 10:54:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (help == NULL) {
|
2018-07-09 10:06:33 +00:00
|
|
|
help = nf_ct_helper_ext_add(ct, flags);
|
2016-09-09 12:01:26 +00:00
|
|
|
if (help == NULL)
|
|
|
|
return -ENOMEM;
|
2008-11-18 10:54:05 +00:00
|
|
|
} else {
|
2012-06-18 15:29:53 +00:00
|
|
|
/* We only allow helper re-assignment of the same sort since
|
|
|
|
* we cannot reallocate the helper extension area.
|
|
|
|
*/
|
2013-02-12 05:59:53 +00:00
|
|
|
struct nf_conntrack_helper *tmp = rcu_dereference(help->helper);
|
|
|
|
|
|
|
|
if (tmp && tmp->help != helper->help) {
|
2012-06-18 15:29:53 +00:00
|
|
|
RCU_INIT_POINTER(help->helper, NULL);
|
2016-09-09 12:01:26 +00:00
|
|
|
return 0;
|
2012-06-18 15:29:53 +00:00
|
|
|
}
|
2008-11-18 10:54:05 +00:00
|
|
|
}
|
|
|
|
|
2012-01-12 04:41:32 +00:00
|
|
|
rcu_assign_pointer(help->helper, helper);
|
2016-09-09 12:01:26 +00:00
|
|
|
|
|
|
|
return 0;
|
2008-11-18 10:54:05 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
|
|
|
|
|
2022-11-06 20:34:14 +00:00
|
|
|
/* 'skb' should already be pulled to nh_ofs. */
|
|
|
|
int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct,
|
|
|
|
enum ip_conntrack_info ctinfo, u16 proto)
|
|
|
|
{
|
|
|
|
const struct nf_conntrack_helper *helper;
|
|
|
|
const struct nf_conn_help *help;
|
|
|
|
unsigned int protoff;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (ctinfo == IP_CT_RELATED_REPLY)
|
|
|
|
return NF_ACCEPT;
|
|
|
|
|
|
|
|
help = nfct_help(ct);
|
|
|
|
if (!help)
|
|
|
|
return NF_ACCEPT;
|
|
|
|
|
|
|
|
helper = rcu_dereference(help->helper);
|
|
|
|
if (!helper)
|
|
|
|
return NF_ACCEPT;
|
|
|
|
|
|
|
|
if (helper->tuple.src.l3num != NFPROTO_UNSPEC &&
|
|
|
|
helper->tuple.src.l3num != proto)
|
|
|
|
return NF_ACCEPT;
|
|
|
|
|
|
|
|
switch (proto) {
|
|
|
|
case NFPROTO_IPV4:
|
|
|
|
protoff = ip_hdrlen(skb);
|
|
|
|
proto = ip_hdr(skb)->protocol;
|
|
|
|
break;
|
|
|
|
case NFPROTO_IPV6: {
|
|
|
|
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
|
|
|
|
__be16 frag_off;
|
|
|
|
int ofs;
|
|
|
|
|
|
|
|
ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
|
|
|
|
&frag_off);
|
|
|
|
if (ofs < 0 || (frag_off & htons(~0x7)) != 0) {
|
|
|
|
pr_debug("proto header not found\n");
|
|
|
|
return NF_ACCEPT;
|
|
|
|
}
|
|
|
|
protoff = ofs;
|
|
|
|
proto = nexthdr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
WARN_ONCE(1, "helper invoked on non-IP family!");
|
|
|
|
return NF_DROP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (helper->tuple.dst.protonum != proto)
|
|
|
|
return NF_ACCEPT;
|
|
|
|
|
|
|
|
err = helper->help(skb, protoff, ct, ctinfo);
|
|
|
|
if (err != NF_ACCEPT)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* Adjust seqs after helper. This is needed due to some helpers (e.g.,
|
|
|
|
* FTP with NAT) adusting the TCP payload size when mangling IP
|
|
|
|
* addresses and/or port numbers in the text-based control connection.
|
|
|
|
*/
|
|
|
|
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
|
|
|
|
!nf_ct_seq_adjust(skb, ct, ctinfo, protoff))
|
|
|
|
return NF_DROP;
|
|
|
|
return NF_ACCEPT;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_ct_helper);
|
|
|
|
|
2022-11-06 20:34:15 +00:00
|
|
|
int nf_ct_add_helper(struct nf_conn *ct, const char *name, u8 family,
|
|
|
|
u8 proto, bool nat, struct nf_conntrack_helper **hp)
|
|
|
|
{
|
|
|
|
struct nf_conntrack_helper *helper;
|
|
|
|
struct nf_conn_help *help;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
helper = nf_conntrack_helper_try_module_get(name, family, proto);
|
|
|
|
if (!helper)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
|
|
|
|
if (!help) {
|
|
|
|
nf_conntrack_helper_put(helper);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_NF_NAT)
|
|
|
|
if (nat) {
|
|
|
|
ret = nf_nat_helper_try_module_get(name, family, proto);
|
|
|
|
if (ret) {
|
|
|
|
nf_conntrack_helper_put(helper);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
rcu_assign_pointer(help->helper, helper);
|
|
|
|
*hp = helper;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_ct_add_helper);
|
|
|
|
|
2014-10-30 05:57:19 +00:00
|
|
|
/* appropriate ct lock protecting must be taken by caller */
|
2017-05-28 14:35:52 +00:00
|
|
|
static int unhelp(struct nf_conn *ct, void *me)
|
2006-11-29 01:34:59 +00:00
|
|
|
{
|
|
|
|
struct nf_conn_help *help = nfct_help(ct);
|
|
|
|
|
2014-03-03 13:46:01 +00:00
|
|
|
if (help && rcu_dereference_raw(help->helper) == me) {
|
2006-11-29 01:34:59 +00:00
|
|
|
nf_conntrack_event(IPCT_HELPER, ct);
|
2011-08-01 16:19:00 +00:00
|
|
|
RCU_INIT_POINTER(help->helper, NULL);
|
2006-11-29 01:34:59 +00:00
|
|
|
}
|
2017-05-28 14:35:52 +00:00
|
|
|
|
|
|
|
/* We are not intended to delete this conntrack. */
|
2006-11-29 01:34:59 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-13 10:28:22 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-05 02:44:51 +00:00
|
|
|
static LIST_HEAD(nf_ct_helper_expectfn_list);
|
|
|
|
|
|
|
|
void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n)
|
|
|
|
{
|
2014-03-03 13:46:01 +00:00
|
|
|
spin_lock_bh(&nf_conntrack_expect_lock);
|
2012-02-05 02:44:51 +00:00
|
|
|
list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
|
2014-03-03 13:46:01 +00:00
|
|
|
spin_unlock_bh(&nf_conntrack_expect_lock);
|
2012-02-05 02:44:51 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register);
|
|
|
|
|
|
|
|
void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
|
|
|
|
{
|
2014-03-03 13:46:01 +00:00
|
|
|
spin_lock_bh(&nf_conntrack_expect_lock);
|
2012-02-05 02:44:51 +00:00
|
|
|
list_del_rcu(&n->head);
|
2014-03-03 13:46:01 +00:00
|
|
|
spin_unlock_bh(&nf_conntrack_expect_lock);
|
2012-02-05 02:44:51 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
|
|
|
|
|
2017-03-29 11:11:27 +00:00
|
|
|
/* Caller should hold the rcu lock */
|
2012-02-05 02:44:51 +00:00
|
|
|
struct nf_ct_helper_expectfn *
|
|
|
|
nf_ct_helper_expectfn_find_by_name(const char *name)
|
|
|
|
{
|
|
|
|
struct nf_ct_helper_expectfn *cur;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
|
|
|
|
if (!strcmp(cur->name, name)) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found ? cur : NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name);
|
|
|
|
|
2017-03-29 11:11:27 +00:00
|
|
|
/* Caller should hold the rcu lock */
|
2012-02-05 02:44:51 +00:00
|
|
|
struct nf_ct_helper_expectfn *
|
|
|
|
nf_ct_helper_expectfn_find_by_symbol(const void *symbol)
|
|
|
|
{
|
|
|
|
struct nf_ct_helper_expectfn *cur;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
|
|
|
|
if (cur->expectfn == symbol) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found ? cur : NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
|
|
|
|
|
2013-02-10 17:56:56 +00:00
|
|
|
__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;
|
2013-02-18 21:51:07 +00:00
|
|
|
struct va_format vaf;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
|
|
|
vaf.fmt = fmt;
|
|
|
|
vaf.va = &args;
|
2013-02-10 17:56:56 +00:00
|
|
|
|
|
|
|
/* Called from the helper function, this call never fails */
|
|
|
|
help = nfct_help(ct);
|
|
|
|
|
2016-09-21 15:35:04 +00:00
|
|
|
/* rcu_read_lock()ed by nf_hook_thresh */
|
2013-02-10 17:56:56 +00:00
|
|
|
helper = rcu_dereference(help->helper);
|
|
|
|
|
2013-03-24 23:50:40 +00:00
|
|
|
nf_log_packet(nf_ct_net(ct), nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
|
2013-02-18 21:51:07 +00:00
|
|
|
"nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);
|
|
|
|
|
|
|
|
va_end(args);
|
2013-02-10 17:56:56 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_ct_helper_log);
|
|
|
|
|
2006-11-29 01:34:59 +00:00
|
|
|
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
|
|
|
|
{
|
2016-05-24 09:23:51 +00:00
|
|
|
struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
|
2007-07-08 05:36:46 +00:00
|
|
|
unsigned int h = helper_hash(&me->tuple);
|
2016-05-24 09:23:51 +00:00
|
|
|
struct nf_conntrack_helper *cur;
|
netfilter: nf_ct_helper: permit cthelpers with different names via nfnetlink
cthelpers added via nfnetlink may have the same tuple, i.e. except for
the l3proto and l4proto, other fields are all zero. So even with the
different names, we will also fail to add them:
# nfct helper add ssdp inet udp
# nfct helper add tftp inet udp
nfct v1.4.3: netlink error: File exists
So in order to avoid unpredictable behaviour, we should:
1. cthelpers can be selected by nft ct helper obj or xt_CT target, so
report error if duplicated { name, l3proto, l4proto } tuple exist.
2. cthelpers can be selected by nf_ct_tuple_src_mask_cmp when
nf_ct_auto_assign_helper is enabled, so also report error if duplicated
{ l3proto, l4proto, src-port } tuple exist.
Also note, if the cthelper is added from userspace, then the src-port will
always be zero, it's invalid for nf_ct_auto_assign_helper, so there's no
need to check the second point listed above.
Fixes: 893e093c786c ("netfilter: nf_ct_helper: bail out on duplicated helpers")
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2017-04-15 11:27:42 +00:00
|
|
|
int ret = 0, i;
|
2007-07-08 05:36:46 +00:00
|
|
|
|
2008-03-26 03:09:15 +00:00
|
|
|
BUG_ON(me->expect_policy == NULL);
|
|
|
|
BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
|
2009-03-25 17:44:01 +00:00
|
|
|
BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
|
2006-11-29 01:34:59 +00:00
|
|
|
|
2017-03-24 13:32:19 +00:00
|
|
|
if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-01-31 12:36:54 +00:00
|
|
|
mutex_lock(&nf_ct_helper_mutex);
|
netfilter: nf_ct_helper: permit cthelpers with different names via nfnetlink
cthelpers added via nfnetlink may have the same tuple, i.e. except for
the l3proto and l4proto, other fields are all zero. So even with the
different names, we will also fail to add them:
# nfct helper add ssdp inet udp
# nfct helper add tftp inet udp
nfct v1.4.3: netlink error: File exists
So in order to avoid unpredictable behaviour, we should:
1. cthelpers can be selected by nft ct helper obj or xt_CT target, so
report error if duplicated { name, l3proto, l4proto } tuple exist.
2. cthelpers can be selected by nf_ct_tuple_src_mask_cmp when
nf_ct_auto_assign_helper is enabled, so also report error if duplicated
{ l3proto, l4proto, src-port } tuple exist.
Also note, if the cthelper is added from userspace, then the src-port will
always be zero, it's invalid for nf_ct_auto_assign_helper, so there's no
need to check the second point listed above.
Fixes: 893e093c786c ("netfilter: nf_ct_helper: bail out on duplicated helpers")
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2017-04-15 11:27:42 +00:00
|
|
|
for (i = 0; i < nf_ct_helper_hsize; i++) {
|
|
|
|
hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
|
|
|
|
if (!strcmp(cur->name, me->name) &&
|
|
|
|
(cur->tuple.src.l3num == NFPROTO_UNSPEC ||
|
|
|
|
cur->tuple.src.l3num == me->tuple.src.l3num) &&
|
|
|
|
cur->tuple.dst.protonum == me->tuple.dst.protonum) {
|
|
|
|
ret = -EEXIST;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* avoid unpredictable behaviour for auto_assign_helper */
|
|
|
|
if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
|
|
|
|
hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
|
|
|
|
if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
|
|
|
|
&mask)) {
|
|
|
|
ret = -EEXIST;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-05-13 19:44:54 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-07 14:01:56 +00:00
|
|
|
refcount_set(&me->refcnt, 1);
|
2008-01-31 12:36:54 +00:00
|
|
|
hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
|
2007-07-08 05:36:46 +00:00
|
|
|
nf_ct_helper_count++;
|
2012-05-13 19:44:54 +00:00
|
|
|
out:
|
2008-01-31 12:36:54 +00:00
|
|
|
mutex_unlock(&nf_ct_helper_mutex);
|
2012-05-13 19:44:54 +00:00
|
|
|
return ret;
|
2006-11-29 01:34:59 +00:00
|
|
|
}
|
2006-12-03 06:11:25 +00:00
|
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
|
2006-11-29 01:34:59 +00:00
|
|
|
|
2017-07-25 22:02:31 +00:00
|
|
|
static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
|
2006-11-29 01:34:59 +00:00
|
|
|
{
|
2017-07-25 22:02:31 +00:00
|
|
|
struct nf_conn_help *help = nfct_help(exp->master);
|
|
|
|
const struct nf_conntrack_helper *me = data;
|
|
|
|
const struct nf_conntrack_helper *this;
|
|
|
|
|
|
|
|
if (exp->helper == me)
|
|
|
|
return true;
|
2016-05-15 17:50:14 +00:00
|
|
|
|
2017-07-25 22:02:31 +00:00
|
|
|
this = rcu_dereference_protected(help->helper,
|
|
|
|
lockdep_is_held(&nf_conntrack_expect_lock));
|
|
|
|
return this == me;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
|
|
|
|
{
|
2016-05-15 17:50:14 +00:00
|
|
|
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();
|
2006-11-29 01:34:59 +00:00
|
|
|
|
2017-07-25 22:02:31 +00:00
|
|
|
nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
|
2017-05-28 14:35:52 +00:00
|
|
|
nf_ct_iterate_destroy(unhelp, me);
|
2008-10-08 09:35:06 +00:00
|
|
|
}
|
2006-12-03 06:11:25 +00:00
|
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
|
2007-07-08 05:23:42 +00:00
|
|
|
|
2016-07-18 03:39:23 +00:00
|
|
|
void nf_ct_helper_init(struct nf_conntrack_helper *helper,
|
|
|
|
u16 l3num, u16 protonum, const char *name,
|
|
|
|
u16 default_port, u16 spec_port, u32 id,
|
|
|
|
const struct nf_conntrack_expect_policy *exp_pol,
|
2017-04-15 23:29:17 +00:00
|
|
|
u32 expect_class_max,
|
2016-07-18 03:39:23 +00:00
|
|
|
int (*help)(struct sk_buff *skb, unsigned int protoff,
|
|
|
|
struct nf_conn *ct,
|
|
|
|
enum ip_conntrack_info ctinfo),
|
|
|
|
int (*from_nlattr)(struct nlattr *attr,
|
|
|
|
struct nf_conn *ct),
|
|
|
|
struct module *module)
|
|
|
|
{
|
|
|
|
helper->tuple.src.l3num = l3num;
|
|
|
|
helper->tuple.dst.protonum = protonum;
|
|
|
|
helper->tuple.src.u.all = htons(spec_port);
|
|
|
|
helper->expect_policy = exp_pol;
|
|
|
|
helper->expect_class_max = expect_class_max;
|
|
|
|
helper->help = help;
|
|
|
|
helper->from_nlattr = from_nlattr;
|
|
|
|
helper->me = module;
|
2019-04-17 14:46:15 +00:00
|
|
|
snprintf(helper->nat_mod_name, sizeof(helper->nat_mod_name),
|
|
|
|
NF_NAT_HELPER_PREFIX "%s", name);
|
2016-07-18 03:39:23 +00:00
|
|
|
|
|
|
|
if (spec_port == default_port)
|
|
|
|
snprintf(helper->name, sizeof(helper->name), "%s", name);
|
|
|
|
else
|
|
|
|
snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_ct_helper_init);
|
|
|
|
|
|
|
|
int nf_conntrack_helpers_register(struct nf_conntrack_helper *helper,
|
|
|
|
unsigned int n)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
err = nf_conntrack_helper_register(&helper[i]);
|
|
|
|
if (err < 0)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
err:
|
|
|
|
if (i > 0)
|
|
|
|
nf_conntrack_helpers_unregister(helper, i);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_helpers_register);
|
|
|
|
|
|
|
|
void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper,
|
|
|
|
unsigned int n)
|
|
|
|
{
|
|
|
|
while (n-- > 0)
|
|
|
|
nf_conntrack_helper_unregister(&helper[n]);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister);
|
|
|
|
|
2019-04-17 14:46:15 +00:00
|
|
|
void nf_nat_helper_register(struct nf_conntrack_nat_helper *nat)
|
|
|
|
{
|
|
|
|
mutex_lock(&nf_ct_nat_helpers_mutex);
|
|
|
|
list_add_rcu(&nat->list, &nf_ct_nat_helpers);
|
|
|
|
mutex_unlock(&nf_ct_nat_helpers_mutex);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_nat_helper_register);
|
|
|
|
|
|
|
|
void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat)
|
|
|
|
{
|
|
|
|
mutex_lock(&nf_ct_nat_helpers_mutex);
|
|
|
|
list_del_rcu(&nat->list);
|
|
|
|
mutex_unlock(&nf_ct_nat_helpers_mutex);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nf_nat_helper_unregister);
|
|
|
|
|
2013-01-21 22:10:30 +00:00
|
|
|
int nf_conntrack_helper_init(void)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2019-04-17 14:46:15 +00:00
|
|
|
INIT_LIST_HEAD(&nf_ct_nat_helpers);
|
2007-07-08 05:36:46 +00:00
|
|
|
return 0;
|
2007-07-08 05:23:42 +00:00
|
|
|
}
|
|
|
|
|
2013-01-21 22:10:30 +00:00
|
|
|
void nf_conntrack_helper_fini(void)
|
2007-07-08 05:23:42 +00:00
|
|
|
{
|
netfilter: use kvmalloc_array to allocate memory for hashtable
nf_ct_alloc_hashtable is used to allocate memory for conntrack,
NAT bysrc and expectation hashtable. Assuming 64k bucket size,
which means 7th order page allocation, __get_free_pages, called
by nf_ct_alloc_hashtable, will trigger the direct memory reclaim
and stall for a long time, when system has lots of memory stress
so replace combination of __get_free_pages and vzalloc with
kvmalloc_array, which provides a overflow check and a fallback
if no high order memory is available, and do not retry to reclaim
memory, reduce stall
and remove nf_ct_free_hashtable, since it is just a kvfree
Signed-off-by: Zhang Yu <zhangyu31@baidu.com>
Signed-off-by: Wang Li <wangli39@baidu.com>
Signed-off-by: Li RongQing <lirongqing@baidu.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2018-07-25 07:52:13 +00:00
|
|
|
kvfree(nf_ct_helper_hash);
|
2007-07-08 05:23:42 +00:00
|
|
|
}
|