// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * (C) 2011 Pablo Neira Ayuso <pablo@netfilter.org>
 * (C) 2011 Intra2net AG <https://www.intra2net.com>
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/skbuff.h>

#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/nfnetlink_acct.h>
#include <linux/netfilter/xt_nfacct.h>

MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_DESCRIPTION("Xtables: match for the extended accounting infrastructure");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_nfacct");
MODULE_ALIAS("ip6t_nfacct");

static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
	int overquota;
	const struct xt_nfacct_match_info *info = par->targinfo;

	nfnl_acct_update(skb, info->nfacct);

	overquota = nfnl_acct_overquota(xt_net(par), info->nfacct);

	return overquota == NFACCT_UNDERQUOTA ? false : true;
}

static int
nfacct_mt_checkentry(const struct xt_mtchk_param *par)
{
	struct xt_nfacct_match_info *info = par->matchinfo;
	struct nf_acct *nfacct;

	nfacct = nfnl_acct_find_get(par->net, info->name);
	if (nfacct == NULL) {
		pr_info_ratelimited("accounting object `%s' does not exists\n",
				    info->name);
		return -ENOENT;
	}
	info->nfacct = nfacct;
	return 0;
}

static void
nfacct_mt_destroy(const struct xt_mtdtor_param *par)
{
	const struct xt_nfacct_match_info *info = par->matchinfo;

	nfnl_acct_put(info->nfacct);
}

static struct xt_match nfacct_mt_reg[] __read_mostly = {
	{
		.name       = "nfacct",
		.revision   = 0,
		.family     = NFPROTO_UNSPEC,
		.checkentry = nfacct_mt_checkentry,
		.match      = nfacct_mt,
		.destroy    = nfacct_mt_destroy,
		.matchsize  = sizeof(struct xt_nfacct_match_info),
		.usersize   = offsetof(struct xt_nfacct_match_info, nfacct),
		.me         = THIS_MODULE,
	},
	{
		.name       = "nfacct",
		.revision   = 1,
		.family     = NFPROTO_UNSPEC,
		.checkentry = nfacct_mt_checkentry,
		.match      = nfacct_mt,
		.destroy    = nfacct_mt_destroy,
		.matchsize  = sizeof(struct xt_nfacct_match_info_v1),
		.usersize   = offsetof(struct xt_nfacct_match_info_v1, nfacct),
		.me         = THIS_MODULE,
	},
};

static int __init nfacct_mt_init(void)
{
	return xt_register_matches(nfacct_mt_reg, ARRAY_SIZE(nfacct_mt_reg));
}

static void __exit nfacct_mt_exit(void)
{
	xt_unregister_matches(nfacct_mt_reg, ARRAY_SIZE(nfacct_mt_reg));
}

module_init(nfacct_mt_init);
module_exit(nfacct_mt_exit);
