nfnl_cthelper_create(const struct nlattr * const tb[],
		     struct nf_conntrack_tuple *tuple)
{
	struct nf_conntrack_helper *helper;
	struct nfnl_cthelper *nfcth;
	unsigned int size;
	int ret;

	if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN])
		return -EINVAL;

	nfcth = kzalloc(sizeof(*nfcth), GFP_KERNEL);
	if (nfcth == NULL)
		return -ENOMEM;
	helper = &nfcth->helper;

	ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]);
	if (ret < 0)
		goto err1;

	strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
	size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
	if (size > FIELD_SIZEOF(struct nf_conn_help, data)) {
		ret = -ENOMEM;
		goto err2;
	}

	helper->flags |= NF_CT_HELPER_F_USERSPACE;
	memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));

	helper->me = THIS_MODULE;
	helper->help = nfnl_userspace_cthelper;
	helper->from_nlattr = nfnl_cthelper_from_nlattr;
	helper->to_nlattr = nfnl_cthelper_to_nlattr;

	/* Default to queue number zero, this can be updated at any time. */
	if (tb[NFCTH_QUEUE_NUM])
		helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));

	if (tb[NFCTH_STATUS]) {
		int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));

		switch(status) {
		case NFCT_HELPER_STATUS_ENABLED:
			helper->flags |= NF_CT_HELPER_F_CONFIGURED;
			break;
		case NFCT_HELPER_STATUS_DISABLED:
			helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
			break;
		}
	}

	ret = nf_conntrack_helper_register(helper);
	if (ret < 0)
		goto err2;

	list_add_tail(&nfcth->list, &nfnl_cthelper_list);
	return 0;
err2:
	kfree(helper->expect_policy);
err1:
	kfree(nfcth);
	return ret;
}
nfnl_cthelper_create(const struct nlattr * const tb[],
		     struct nf_conntrack_tuple *tuple)
{
	struct nf_conntrack_helper *helper;
	struct nfnl_cthelper *nfcth;
	unsigned int size;
	int ret;

	if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN])
		return -EINVAL;

	nfcth = kzalloc(sizeof(*nfcth), GFP_KERNEL);
	if (nfcth == NULL)
		return -ENOMEM;
	helper = &nfcth->helper;

	ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]);
	if (ret < 0)
		goto err1;

	strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
	size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
	if (size > FIELD_SIZEOF(struct nf_conn_help, data)) {
		ret = -ENOMEM;
		goto err2;
	}

	helper->flags |= NF_CT_HELPER_F_USERSPACE;
	memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));

	helper->me = THIS_MODULE;
	helper->help = nfnl_userspace_cthelper;
	helper->from_nlattr = nfnl_cthelper_from_nlattr;
	helper->to_nlattr = nfnl_cthelper_to_nlattr;

	 
	if (tb[NFCTH_QUEUE_NUM])
		helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));

	if (tb[NFCTH_STATUS]) {
		int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));

		switch(status) {
		case NFCT_HELPER_STATUS_ENABLED:
			helper->flags |= NF_CT_HELPER_F_CONFIGURED;
			break;
		case NFCT_HELPER_STATUS_DISABLED:
			helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
			break;
		}
	}

	ret = nf_conntrack_helper_register(helper);
	if (ret < 0)
		goto err2;

	list_add_tail(&nfcth->list, &nfnl_cthelper_list);
	return 0;
err2:
	kfree(helper->expect_policy);
err1:
	kfree(nfcth);
	return ret;
}