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

nfnetlink_cthelper.c (19503B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * (C) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
      4 *
      5 * This software has been sponsored by Vyatta Inc. <http://www.vyatta.com>
      6 */
      7#include <linux/init.h>
      8#include <linux/module.h>
      9#include <linux/kernel.h>
     10#include <linux/skbuff.h>
     11#include <linux/netlink.h>
     12#include <linux/rculist.h>
     13#include <linux/slab.h>
     14#include <linux/types.h>
     15#include <linux/list.h>
     16#include <linux/errno.h>
     17#include <linux/capability.h>
     18#include <net/netlink.h>
     19#include <net/sock.h>
     20
     21#include <net/netfilter/nf_conntrack_helper.h>
     22#include <net/netfilter/nf_conntrack_expect.h>
     23#include <net/netfilter/nf_conntrack_ecache.h>
     24
     25#include <linux/netfilter/nfnetlink.h>
     26#include <linux/netfilter/nfnetlink_conntrack.h>
     27#include <linux/netfilter/nfnetlink_cthelper.h>
     28
     29MODULE_LICENSE("GPL");
     30MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
     31MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers");
     32
     33struct nfnl_cthelper {
     34	struct list_head		list;
     35	struct nf_conntrack_helper	helper;
     36};
     37
     38static LIST_HEAD(nfnl_cthelper_list);
     39
     40static int
     41nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff,
     42			struct nf_conn *ct, enum ip_conntrack_info ctinfo)
     43{
     44	const struct nf_conn_help *help;
     45	struct nf_conntrack_helper *helper;
     46
     47	help = nfct_help(ct);
     48	if (help == NULL)
     49		return NF_DROP;
     50
     51	/* rcu_read_lock()ed by nf_hook_thresh */
     52	helper = rcu_dereference(help->helper);
     53	if (helper == NULL)
     54		return NF_DROP;
     55
     56	/* This is a user-space helper not yet configured, skip. */
     57	if ((helper->flags &
     58	    (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) ==
     59	     NF_CT_HELPER_F_USERSPACE)
     60		return NF_ACCEPT;
     61
     62	/* If the user-space helper is not available, don't block traffic. */
     63	return NF_QUEUE_NR(helper->queue_num) | NF_VERDICT_FLAG_QUEUE_BYPASS;
     64}
     65
     66static const struct nla_policy nfnl_cthelper_tuple_pol[NFCTH_TUPLE_MAX+1] = {
     67	[NFCTH_TUPLE_L3PROTONUM] = { .type = NLA_U16, },
     68	[NFCTH_TUPLE_L4PROTONUM] = { .type = NLA_U8, },
     69};
     70
     71static int
     72nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple,
     73			  const struct nlattr *attr)
     74{
     75	int err;
     76	struct nlattr *tb[NFCTH_TUPLE_MAX+1];
     77
     78	err = nla_parse_nested_deprecated(tb, NFCTH_TUPLE_MAX, attr,
     79					  nfnl_cthelper_tuple_pol, NULL);
     80	if (err < 0)
     81		return err;
     82
     83	if (!tb[NFCTH_TUPLE_L3PROTONUM] || !tb[NFCTH_TUPLE_L4PROTONUM])
     84		return -EINVAL;
     85
     86	/* Not all fields are initialized so first zero the tuple */
     87	memset(tuple, 0, sizeof(struct nf_conntrack_tuple));
     88
     89	tuple->src.l3num = ntohs(nla_get_be16(tb[NFCTH_TUPLE_L3PROTONUM]));
     90	tuple->dst.protonum = nla_get_u8(tb[NFCTH_TUPLE_L4PROTONUM]);
     91
     92	return 0;
     93}
     94
     95static int
     96nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
     97{
     98	struct nf_conn_help *help = nfct_help(ct);
     99
    100	if (attr == NULL)
    101		return -EINVAL;
    102
    103	if (help->helper->data_len == 0)
    104		return -EINVAL;
    105
    106	nla_memcpy(help->data, attr, sizeof(help->data));
    107	return 0;
    108}
    109
    110static int
    111nfnl_cthelper_to_nlattr(struct sk_buff *skb, const struct nf_conn *ct)
    112{
    113	const struct nf_conn_help *help = nfct_help(ct);
    114
    115	if (help->helper->data_len &&
    116	    nla_put(skb, CTA_HELP_INFO, help->helper->data_len, &help->data))
    117		goto nla_put_failure;
    118
    119	return 0;
    120
    121nla_put_failure:
    122	return -ENOSPC;
    123}
    124
    125static const struct nla_policy nfnl_cthelper_expect_pol[NFCTH_POLICY_MAX+1] = {
    126	[NFCTH_POLICY_NAME] = { .type = NLA_NUL_STRING,
    127				.len = NF_CT_HELPER_NAME_LEN-1 },
    128	[NFCTH_POLICY_EXPECT_MAX] = { .type = NLA_U32, },
    129	[NFCTH_POLICY_EXPECT_TIMEOUT] = { .type = NLA_U32, },
    130};
    131
    132static int
    133nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
    134			    const struct nlattr *attr)
    135{
    136	int err;
    137	struct nlattr *tb[NFCTH_POLICY_MAX+1];
    138
    139	err = nla_parse_nested_deprecated(tb, NFCTH_POLICY_MAX, attr,
    140					  nfnl_cthelper_expect_pol, NULL);
    141	if (err < 0)
    142		return err;
    143
    144	if (!tb[NFCTH_POLICY_NAME] ||
    145	    !tb[NFCTH_POLICY_EXPECT_MAX] ||
    146	    !tb[NFCTH_POLICY_EXPECT_TIMEOUT])
    147		return -EINVAL;
    148
    149	nla_strscpy(expect_policy->name,
    150		    tb[NFCTH_POLICY_NAME], NF_CT_HELPER_NAME_LEN);
    151	expect_policy->max_expected =
    152		ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
    153	if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
    154		return -EINVAL;
    155
    156	expect_policy->timeout =
    157		ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT]));
    158
    159	return 0;
    160}
    161
    162static const struct nla_policy
    163nfnl_cthelper_expect_policy_set[NFCTH_POLICY_SET_MAX+1] = {
    164	[NFCTH_POLICY_SET_NUM] = { .type = NLA_U32, },
    165};
    166
    167static int
    168nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
    169				  const struct nlattr *attr)
    170{
    171	int i, ret;
    172	struct nf_conntrack_expect_policy *expect_policy;
    173	struct nlattr *tb[NFCTH_POLICY_SET_MAX+1];
    174	unsigned int class_max;
    175
    176	ret = nla_parse_nested_deprecated(tb, NFCTH_POLICY_SET_MAX, attr,
    177					  nfnl_cthelper_expect_policy_set,
    178					  NULL);
    179	if (ret < 0)
    180		return ret;
    181
    182	if (!tb[NFCTH_POLICY_SET_NUM])
    183		return -EINVAL;
    184
    185	class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
    186	if (class_max == 0)
    187		return -EINVAL;
    188	if (class_max > NF_CT_MAX_EXPECT_CLASSES)
    189		return -EOVERFLOW;
    190
    191	expect_policy = kcalloc(class_max,
    192				sizeof(struct nf_conntrack_expect_policy),
    193				GFP_KERNEL);
    194	if (expect_policy == NULL)
    195		return -ENOMEM;
    196
    197	for (i = 0; i < class_max; i++) {
    198		if (!tb[NFCTH_POLICY_SET+i])
    199			goto err;
    200
    201		ret = nfnl_cthelper_expect_policy(&expect_policy[i],
    202						  tb[NFCTH_POLICY_SET+i]);
    203		if (ret < 0)
    204			goto err;
    205	}
    206
    207	helper->expect_class_max = class_max - 1;
    208	helper->expect_policy = expect_policy;
    209	return 0;
    210err:
    211	kfree(expect_policy);
    212	return -EINVAL;
    213}
    214
    215static int
    216nfnl_cthelper_create(const struct nlattr * const tb[],
    217		     struct nf_conntrack_tuple *tuple)
    218{
    219	struct nf_conntrack_helper *helper;
    220	struct nfnl_cthelper *nfcth;
    221	unsigned int size;
    222	int ret;
    223
    224	if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN])
    225		return -EINVAL;
    226
    227	nfcth = kzalloc(sizeof(*nfcth), GFP_KERNEL);
    228	if (nfcth == NULL)
    229		return -ENOMEM;
    230	helper = &nfcth->helper;
    231
    232	ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]);
    233	if (ret < 0)
    234		goto err1;
    235
    236	nla_strscpy(helper->name,
    237		    tb[NFCTH_NAME], NF_CT_HELPER_NAME_LEN);
    238	size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
    239	if (size > sizeof_field(struct nf_conn_help, data)) {
    240		ret = -ENOMEM;
    241		goto err2;
    242	}
    243	helper->data_len = size;
    244
    245	helper->flags |= NF_CT_HELPER_F_USERSPACE;
    246	memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));
    247
    248	helper->me = THIS_MODULE;
    249	helper->help = nfnl_userspace_cthelper;
    250	helper->from_nlattr = nfnl_cthelper_from_nlattr;
    251	helper->to_nlattr = nfnl_cthelper_to_nlattr;
    252
    253	/* Default to queue number zero, this can be updated at any time. */
    254	if (tb[NFCTH_QUEUE_NUM])
    255		helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));
    256
    257	if (tb[NFCTH_STATUS]) {
    258		int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));
    259
    260		switch(status) {
    261		case NFCT_HELPER_STATUS_ENABLED:
    262			helper->flags |= NF_CT_HELPER_F_CONFIGURED;
    263			break;
    264		case NFCT_HELPER_STATUS_DISABLED:
    265			helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
    266			break;
    267		}
    268	}
    269
    270	ret = nf_conntrack_helper_register(helper);
    271	if (ret < 0)
    272		goto err2;
    273
    274	list_add_tail(&nfcth->list, &nfnl_cthelper_list);
    275	return 0;
    276err2:
    277	kfree(helper->expect_policy);
    278err1:
    279	kfree(nfcth);
    280	return ret;
    281}
    282
    283static int
    284nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy,
    285				struct nf_conntrack_expect_policy *new_policy,
    286				const struct nlattr *attr)
    287{
    288	struct nlattr *tb[NFCTH_POLICY_MAX + 1];
    289	int err;
    290
    291	err = nla_parse_nested_deprecated(tb, NFCTH_POLICY_MAX, attr,
    292					  nfnl_cthelper_expect_pol, NULL);
    293	if (err < 0)
    294		return err;
    295
    296	if (!tb[NFCTH_POLICY_NAME] ||
    297	    !tb[NFCTH_POLICY_EXPECT_MAX] ||
    298	    !tb[NFCTH_POLICY_EXPECT_TIMEOUT])
    299		return -EINVAL;
    300
    301	if (nla_strcmp(tb[NFCTH_POLICY_NAME], policy->name))
    302		return -EBUSY;
    303
    304	new_policy->max_expected =
    305		ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
    306	if (new_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
    307		return -EINVAL;
    308
    309	new_policy->timeout =
    310		ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT]));
    311
    312	return 0;
    313}
    314
    315static int nfnl_cthelper_update_policy_all(struct nlattr *tb[],
    316					   struct nf_conntrack_helper *helper)
    317{
    318	struct nf_conntrack_expect_policy *new_policy;
    319	struct nf_conntrack_expect_policy *policy;
    320	int i, ret = 0;
    321
    322	new_policy = kmalloc_array(helper->expect_class_max + 1,
    323				   sizeof(*new_policy), GFP_KERNEL);
    324	if (!new_policy)
    325		return -ENOMEM;
    326
    327	/* Check first that all policy attributes are well-formed, so we don't
    328	 * leave things in inconsistent state on errors.
    329	 */
    330	for (i = 0; i < helper->expect_class_max + 1; i++) {
    331
    332		if (!tb[NFCTH_POLICY_SET + i]) {
    333			ret = -EINVAL;
    334			goto err;
    335		}
    336
    337		ret = nfnl_cthelper_update_policy_one(&helper->expect_policy[i],
    338						      &new_policy[i],
    339						      tb[NFCTH_POLICY_SET + i]);
    340		if (ret < 0)
    341			goto err;
    342	}
    343	/* Now we can safely update them. */
    344	for (i = 0; i < helper->expect_class_max + 1; i++) {
    345		policy = (struct nf_conntrack_expect_policy *)
    346				&helper->expect_policy[i];
    347		policy->max_expected = new_policy->max_expected;
    348		policy->timeout	= new_policy->timeout;
    349	}
    350
    351err:
    352	kfree(new_policy);
    353	return ret;
    354}
    355
    356static int nfnl_cthelper_update_policy(struct nf_conntrack_helper *helper,
    357				       const struct nlattr *attr)
    358{
    359	struct nlattr *tb[NFCTH_POLICY_SET_MAX + 1];
    360	unsigned int class_max;
    361	int err;
    362
    363	err = nla_parse_nested_deprecated(tb, NFCTH_POLICY_SET_MAX, attr,
    364					  nfnl_cthelper_expect_policy_set,
    365					  NULL);
    366	if (err < 0)
    367		return err;
    368
    369	if (!tb[NFCTH_POLICY_SET_NUM])
    370		return -EINVAL;
    371
    372	class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
    373	if (helper->expect_class_max + 1 != class_max)
    374		return -EBUSY;
    375
    376	return nfnl_cthelper_update_policy_all(tb, helper);
    377}
    378
    379static int
    380nfnl_cthelper_update(const struct nlattr * const tb[],
    381		     struct nf_conntrack_helper *helper)
    382{
    383	u32 size;
    384	int ret;
    385
    386	if (tb[NFCTH_PRIV_DATA_LEN]) {
    387		size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
    388		if (size != helper->data_len)
    389			return -EBUSY;
    390	}
    391
    392	if (tb[NFCTH_POLICY]) {
    393		ret = nfnl_cthelper_update_policy(helper, tb[NFCTH_POLICY]);
    394		if (ret < 0)
    395			return ret;
    396	}
    397	if (tb[NFCTH_QUEUE_NUM])
    398		helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));
    399
    400	if (tb[NFCTH_STATUS]) {
    401		int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));
    402
    403		switch(status) {
    404		case NFCT_HELPER_STATUS_ENABLED:
    405			helper->flags |= NF_CT_HELPER_F_CONFIGURED;
    406			break;
    407		case NFCT_HELPER_STATUS_DISABLED:
    408			helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
    409			break;
    410		}
    411	}
    412	return 0;
    413}
    414
    415static int nfnl_cthelper_new(struct sk_buff *skb, const struct nfnl_info *info,
    416			     const struct nlattr * const tb[])
    417{
    418	const char *helper_name;
    419	struct nf_conntrack_helper *cur, *helper = NULL;
    420	struct nf_conntrack_tuple tuple;
    421	struct nfnl_cthelper *nlcth;
    422	int ret = 0;
    423
    424	if (!capable(CAP_NET_ADMIN))
    425		return -EPERM;
    426
    427	if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
    428		return -EINVAL;
    429
    430	helper_name = nla_data(tb[NFCTH_NAME]);
    431
    432	ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
    433	if (ret < 0)
    434		return ret;
    435
    436	list_for_each_entry(nlcth, &nfnl_cthelper_list, list) {
    437		cur = &nlcth->helper;
    438
    439		if (strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN))
    440			continue;
    441
    442		if ((tuple.src.l3num != cur->tuple.src.l3num ||
    443		     tuple.dst.protonum != cur->tuple.dst.protonum))
    444			continue;
    445
    446		if (info->nlh->nlmsg_flags & NLM_F_EXCL)
    447			return -EEXIST;
    448
    449		helper = cur;
    450		break;
    451	}
    452
    453	if (helper == NULL)
    454		ret = nfnl_cthelper_create(tb, &tuple);
    455	else
    456		ret = nfnl_cthelper_update(tb, helper);
    457
    458	return ret;
    459}
    460
    461static int
    462nfnl_cthelper_dump_tuple(struct sk_buff *skb,
    463			 struct nf_conntrack_helper *helper)
    464{
    465	struct nlattr *nest_parms;
    466
    467	nest_parms = nla_nest_start(skb, NFCTH_TUPLE);
    468	if (nest_parms == NULL)
    469		goto nla_put_failure;
    470
    471	if (nla_put_be16(skb, NFCTH_TUPLE_L3PROTONUM,
    472			 htons(helper->tuple.src.l3num)))
    473		goto nla_put_failure;
    474
    475	if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->tuple.dst.protonum))
    476		goto nla_put_failure;
    477
    478	nla_nest_end(skb, nest_parms);
    479	return 0;
    480
    481nla_put_failure:
    482	return -1;
    483}
    484
    485static int
    486nfnl_cthelper_dump_policy(struct sk_buff *skb,
    487			struct nf_conntrack_helper *helper)
    488{
    489	int i;
    490	struct nlattr *nest_parms1, *nest_parms2;
    491
    492	nest_parms1 = nla_nest_start(skb, NFCTH_POLICY);
    493	if (nest_parms1 == NULL)
    494		goto nla_put_failure;
    495
    496	if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM,
    497			 htonl(helper->expect_class_max + 1)))
    498		goto nla_put_failure;
    499
    500	for (i = 0; i < helper->expect_class_max + 1; i++) {
    501		nest_parms2 = nla_nest_start(skb, (NFCTH_POLICY_SET + i));
    502		if (nest_parms2 == NULL)
    503			goto nla_put_failure;
    504
    505		if (nla_put_string(skb, NFCTH_POLICY_NAME,
    506				   helper->expect_policy[i].name))
    507			goto nla_put_failure;
    508
    509		if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_MAX,
    510				 htonl(helper->expect_policy[i].max_expected)))
    511			goto nla_put_failure;
    512
    513		if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_TIMEOUT,
    514				 htonl(helper->expect_policy[i].timeout)))
    515			goto nla_put_failure;
    516
    517		nla_nest_end(skb, nest_parms2);
    518	}
    519	nla_nest_end(skb, nest_parms1);
    520	return 0;
    521
    522nla_put_failure:
    523	return -1;
    524}
    525
    526static int
    527nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
    528			int event, struct nf_conntrack_helper *helper)
    529{
    530	struct nlmsghdr *nlh;
    531	unsigned int flags = portid ? NLM_F_MULTI : 0;
    532	int status;
    533
    534	event = nfnl_msg_type(NFNL_SUBSYS_CTHELPER, event);
    535	nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC,
    536			   NFNETLINK_V0, 0);
    537	if (!nlh)
    538		goto nlmsg_failure;
    539
    540	if (nla_put_string(skb, NFCTH_NAME, helper->name))
    541		goto nla_put_failure;
    542
    543	if (nla_put_be32(skb, NFCTH_QUEUE_NUM, htonl(helper->queue_num)))
    544		goto nla_put_failure;
    545
    546	if (nfnl_cthelper_dump_tuple(skb, helper) < 0)
    547		goto nla_put_failure;
    548
    549	if (nfnl_cthelper_dump_policy(skb, helper) < 0)
    550		goto nla_put_failure;
    551
    552	if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len)))
    553		goto nla_put_failure;
    554
    555	if (helper->flags & NF_CT_HELPER_F_CONFIGURED)
    556		status = NFCT_HELPER_STATUS_ENABLED;
    557	else
    558		status = NFCT_HELPER_STATUS_DISABLED;
    559
    560	if (nla_put_be32(skb, NFCTH_STATUS, htonl(status)))
    561		goto nla_put_failure;
    562
    563	nlmsg_end(skb, nlh);
    564	return skb->len;
    565
    566nlmsg_failure:
    567nla_put_failure:
    568	nlmsg_cancel(skb, nlh);
    569	return -1;
    570}
    571
    572static int
    573nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
    574{
    575	struct nf_conntrack_helper *cur, *last;
    576
    577	rcu_read_lock();
    578	last = (struct nf_conntrack_helper *)cb->args[1];
    579	for (; cb->args[0] < nf_ct_helper_hsize; cb->args[0]++) {
    580restart:
    581		hlist_for_each_entry_rcu(cur,
    582				&nf_ct_helper_hash[cb->args[0]], hnode) {
    583
    584			/* skip non-userspace conntrack helpers. */
    585			if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
    586				continue;
    587
    588			if (cb->args[1]) {
    589				if (cur != last)
    590					continue;
    591				cb->args[1] = 0;
    592			}
    593			if (nfnl_cthelper_fill_info(skb,
    594					    NETLINK_CB(cb->skb).portid,
    595					    cb->nlh->nlmsg_seq,
    596					    NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
    597					    NFNL_MSG_CTHELPER_NEW, cur) < 0) {
    598				cb->args[1] = (unsigned long)cur;
    599				goto out;
    600			}
    601		}
    602	}
    603	if (cb->args[1]) {
    604		cb->args[1] = 0;
    605		goto restart;
    606	}
    607out:
    608	rcu_read_unlock();
    609	return skb->len;
    610}
    611
    612static int nfnl_cthelper_get(struct sk_buff *skb, const struct nfnl_info *info,
    613			     const struct nlattr * const tb[])
    614{
    615	int ret = -ENOENT;
    616	struct nf_conntrack_helper *cur;
    617	struct sk_buff *skb2;
    618	char *helper_name = NULL;
    619	struct nf_conntrack_tuple tuple;
    620	struct nfnl_cthelper *nlcth;
    621	bool tuple_set = false;
    622
    623	if (!capable(CAP_NET_ADMIN))
    624		return -EPERM;
    625
    626	if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
    627		struct netlink_dump_control c = {
    628			.dump = nfnl_cthelper_dump_table,
    629		};
    630		return netlink_dump_start(info->sk, skb, info->nlh, &c);
    631	}
    632
    633	if (tb[NFCTH_NAME])
    634		helper_name = nla_data(tb[NFCTH_NAME]);
    635
    636	if (tb[NFCTH_TUPLE]) {
    637		ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
    638		if (ret < 0)
    639			return ret;
    640
    641		tuple_set = true;
    642	}
    643
    644	list_for_each_entry(nlcth, &nfnl_cthelper_list, list) {
    645		cur = &nlcth->helper;
    646		if (helper_name &&
    647		    strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN))
    648			continue;
    649
    650		if (tuple_set &&
    651		    (tuple.src.l3num != cur->tuple.src.l3num ||
    652		     tuple.dst.protonum != cur->tuple.dst.protonum))
    653			continue;
    654
    655		skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
    656		if (skb2 == NULL) {
    657			ret = -ENOMEM;
    658			break;
    659		}
    660
    661		ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).portid,
    662					      info->nlh->nlmsg_seq,
    663					      NFNL_MSG_TYPE(info->nlh->nlmsg_type),
    664					      NFNL_MSG_CTHELPER_NEW, cur);
    665		if (ret <= 0) {
    666			kfree_skb(skb2);
    667			break;
    668		}
    669
    670		ret = nfnetlink_unicast(skb2, info->net, NETLINK_CB(skb).portid);
    671		break;
    672	}
    673
    674	return ret;
    675}
    676
    677static int nfnl_cthelper_del(struct sk_buff *skb, const struct nfnl_info *info,
    678			     const struct nlattr * const tb[])
    679{
    680	char *helper_name = NULL;
    681	struct nf_conntrack_helper *cur;
    682	struct nf_conntrack_tuple tuple;
    683	bool tuple_set = false, found = false;
    684	struct nfnl_cthelper *nlcth, *n;
    685	int j = 0, ret;
    686
    687	if (!capable(CAP_NET_ADMIN))
    688		return -EPERM;
    689
    690	if (tb[NFCTH_NAME])
    691		helper_name = nla_data(tb[NFCTH_NAME]);
    692
    693	if (tb[NFCTH_TUPLE]) {
    694		ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
    695		if (ret < 0)
    696			return ret;
    697
    698		tuple_set = true;
    699	}
    700
    701	ret = -ENOENT;
    702	list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) {
    703		cur = &nlcth->helper;
    704		j++;
    705
    706		if (helper_name &&
    707		    strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN))
    708			continue;
    709
    710		if (tuple_set &&
    711		    (tuple.src.l3num != cur->tuple.src.l3num ||
    712		     tuple.dst.protonum != cur->tuple.dst.protonum))
    713			continue;
    714
    715		if (refcount_dec_if_one(&cur->refcnt)) {
    716			found = true;
    717			nf_conntrack_helper_unregister(cur);
    718			kfree(cur->expect_policy);
    719
    720			list_del(&nlcth->list);
    721			kfree(nlcth);
    722		} else {
    723			ret = -EBUSY;
    724		}
    725	}
    726
    727	/* Make sure we return success if we flush and there is no helpers */
    728	return (found || j == 0) ? 0 : ret;
    729}
    730
    731static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = {
    732	[NFCTH_NAME] = { .type = NLA_NUL_STRING,
    733			 .len = NF_CT_HELPER_NAME_LEN-1 },
    734	[NFCTH_QUEUE_NUM] = { .type = NLA_U32, },
    735	[NFCTH_PRIV_DATA_LEN] = { .type = NLA_U32, },
    736	[NFCTH_STATUS] = { .type = NLA_U32, },
    737};
    738
    739static const struct nfnl_callback nfnl_cthelper_cb[NFNL_MSG_CTHELPER_MAX] = {
    740	[NFNL_MSG_CTHELPER_NEW]	= {
    741		.call		= nfnl_cthelper_new,
    742		.type		= NFNL_CB_MUTEX,
    743		.attr_count	= NFCTH_MAX,
    744		.policy		= nfnl_cthelper_policy
    745	},
    746	[NFNL_MSG_CTHELPER_GET] = {
    747		.call		= nfnl_cthelper_get,
    748		.type		= NFNL_CB_MUTEX,
    749		.attr_count	= NFCTH_MAX,
    750		.policy		= nfnl_cthelper_policy
    751	},
    752	[NFNL_MSG_CTHELPER_DEL]	= {
    753		.call		= nfnl_cthelper_del,
    754		.type		= NFNL_CB_MUTEX,
    755		.attr_count	= NFCTH_MAX,
    756		.policy		= nfnl_cthelper_policy
    757	},
    758};
    759
    760static const struct nfnetlink_subsystem nfnl_cthelper_subsys = {
    761	.name				= "cthelper",
    762	.subsys_id			= NFNL_SUBSYS_CTHELPER,
    763	.cb_count			= NFNL_MSG_CTHELPER_MAX,
    764	.cb				= nfnl_cthelper_cb,
    765};
    766
    767MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTHELPER);
    768
    769static int __init nfnl_cthelper_init(void)
    770{
    771	int ret;
    772
    773	ret = nfnetlink_subsys_register(&nfnl_cthelper_subsys);
    774	if (ret < 0) {
    775		pr_err("nfnl_cthelper: cannot register with nfnetlink.\n");
    776		goto err_out;
    777	}
    778	return 0;
    779err_out:
    780	return ret;
    781}
    782
    783static void __exit nfnl_cthelper_exit(void)
    784{
    785	struct nf_conntrack_helper *cur;
    786	struct nfnl_cthelper *nlcth, *n;
    787
    788	nfnetlink_subsys_unregister(&nfnl_cthelper_subsys);
    789
    790	list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) {
    791		cur = &nlcth->helper;
    792
    793		nf_conntrack_helper_unregister(cur);
    794		kfree(cur->expect_policy);
    795		kfree(nlcth);
    796	}
    797}
    798
    799module_init(nfnl_cthelper_init);
    800module_exit(nfnl_cthelper_exit);