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

nft_quota.c (7482B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org>
      4 */
      5
      6#include <linux/kernel.h>
      7#include <linux/init.h>
      8#include <linux/module.h>
      9#include <linux/atomic.h>
     10#include <linux/netlink.h>
     11#include <linux/netfilter.h>
     12#include <linux/netfilter/nf_tables.h>
     13#include <net/netfilter/nf_tables.h>
     14
     15struct nft_quota {
     16	atomic64_t	quota;
     17	unsigned long	flags;
     18	atomic64_t	*consumed;
     19};
     20
     21static inline bool nft_overquota(struct nft_quota *priv,
     22				 const struct sk_buff *skb)
     23{
     24	return atomic64_add_return(skb->len, priv->consumed) >=
     25	       atomic64_read(&priv->quota);
     26}
     27
     28static inline bool nft_quota_invert(struct nft_quota *priv)
     29{
     30	return priv->flags & NFT_QUOTA_F_INV;
     31}
     32
     33static inline void nft_quota_do_eval(struct nft_quota *priv,
     34				     struct nft_regs *regs,
     35				     const struct nft_pktinfo *pkt)
     36{
     37	if (nft_overquota(priv, pkt->skb) ^ nft_quota_invert(priv))
     38		regs->verdict.code = NFT_BREAK;
     39}
     40
     41static const struct nla_policy nft_quota_policy[NFTA_QUOTA_MAX + 1] = {
     42	[NFTA_QUOTA_BYTES]	= { .type = NLA_U64 },
     43	[NFTA_QUOTA_FLAGS]	= { .type = NLA_U32 },
     44	[NFTA_QUOTA_CONSUMED]	= { .type = NLA_U64 },
     45};
     46
     47#define NFT_QUOTA_DEPLETED_BIT	1	/* From NFT_QUOTA_F_DEPLETED. */
     48
     49static void nft_quota_obj_eval(struct nft_object *obj,
     50			       struct nft_regs *regs,
     51			       const struct nft_pktinfo *pkt)
     52{
     53	struct nft_quota *priv = nft_obj_data(obj);
     54	bool overquota;
     55
     56	overquota = nft_overquota(priv, pkt->skb);
     57	if (overquota ^ nft_quota_invert(priv))
     58		regs->verdict.code = NFT_BREAK;
     59
     60	if (overquota &&
     61	    !test_and_set_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags))
     62		nft_obj_notify(nft_net(pkt), obj->key.table, obj, 0, 0,
     63			       NFT_MSG_NEWOBJ, 0, nft_pf(pkt), 0, GFP_ATOMIC);
     64}
     65
     66static int nft_quota_do_init(const struct nlattr * const tb[],
     67			     struct nft_quota *priv)
     68{
     69	unsigned long flags = 0;
     70	u64 quota, consumed = 0;
     71
     72	if (!tb[NFTA_QUOTA_BYTES])
     73		return -EINVAL;
     74
     75	quota = be64_to_cpu(nla_get_be64(tb[NFTA_QUOTA_BYTES]));
     76	if (quota > S64_MAX)
     77		return -EOVERFLOW;
     78
     79	if (tb[NFTA_QUOTA_CONSUMED]) {
     80		consumed = be64_to_cpu(nla_get_be64(tb[NFTA_QUOTA_CONSUMED]));
     81		if (consumed > quota)
     82			return -EINVAL;
     83	}
     84
     85	if (tb[NFTA_QUOTA_FLAGS]) {
     86		flags = ntohl(nla_get_be32(tb[NFTA_QUOTA_FLAGS]));
     87		if (flags & ~NFT_QUOTA_F_INV)
     88			return -EINVAL;
     89		if (flags & NFT_QUOTA_F_DEPLETED)
     90			return -EOPNOTSUPP;
     91	}
     92
     93	priv->consumed = kmalloc(sizeof(*priv->consumed), GFP_KERNEL_ACCOUNT);
     94	if (!priv->consumed)
     95		return -ENOMEM;
     96
     97	atomic64_set(&priv->quota, quota);
     98	priv->flags = flags;
     99	atomic64_set(priv->consumed, consumed);
    100
    101	return 0;
    102}
    103
    104static void nft_quota_do_destroy(const struct nft_ctx *ctx,
    105				 struct nft_quota *priv)
    106{
    107	kfree(priv->consumed);
    108}
    109
    110static int nft_quota_obj_init(const struct nft_ctx *ctx,
    111			      const struct nlattr * const tb[],
    112			      struct nft_object *obj)
    113{
    114	struct nft_quota *priv = nft_obj_data(obj);
    115
    116	return nft_quota_do_init(tb, priv);
    117}
    118
    119static void nft_quota_obj_update(struct nft_object *obj,
    120				 struct nft_object *newobj)
    121{
    122	struct nft_quota *newpriv = nft_obj_data(newobj);
    123	struct nft_quota *priv = nft_obj_data(obj);
    124	u64 newquota;
    125
    126	newquota = atomic64_read(&newpriv->quota);
    127	atomic64_set(&priv->quota, newquota);
    128	priv->flags = newpriv->flags;
    129}
    130
    131static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv,
    132			     bool reset)
    133{
    134	u64 consumed, consumed_cap, quota;
    135	u32 flags = priv->flags;
    136
    137	/* Since we inconditionally increment consumed quota for each packet
    138	 * that we see, don't go over the quota boundary in what we send to
    139	 * userspace.
    140	 */
    141	consumed = atomic64_read(priv->consumed);
    142	quota = atomic64_read(&priv->quota);
    143	if (consumed >= quota) {
    144		consumed_cap = quota;
    145		flags |= NFT_QUOTA_F_DEPLETED;
    146	} else {
    147		consumed_cap = consumed;
    148	}
    149
    150	if (nla_put_be64(skb, NFTA_QUOTA_BYTES, cpu_to_be64(quota),
    151			 NFTA_QUOTA_PAD) ||
    152	    nla_put_be64(skb, NFTA_QUOTA_CONSUMED, cpu_to_be64(consumed_cap),
    153			 NFTA_QUOTA_PAD) ||
    154	    nla_put_be32(skb, NFTA_QUOTA_FLAGS, htonl(flags)))
    155		goto nla_put_failure;
    156
    157	if (reset) {
    158		atomic64_sub(consumed, priv->consumed);
    159		clear_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags);
    160	}
    161	return 0;
    162
    163nla_put_failure:
    164	return -1;
    165}
    166
    167static int nft_quota_obj_dump(struct sk_buff *skb, struct nft_object *obj,
    168			      bool reset)
    169{
    170	struct nft_quota *priv = nft_obj_data(obj);
    171
    172	return nft_quota_do_dump(skb, priv, reset);
    173}
    174
    175static void nft_quota_obj_destroy(const struct nft_ctx *ctx,
    176				  struct nft_object *obj)
    177{
    178	struct nft_quota *priv = nft_obj_data(obj);
    179
    180	return nft_quota_do_destroy(ctx, priv);
    181}
    182
    183static struct nft_object_type nft_quota_obj_type;
    184static const struct nft_object_ops nft_quota_obj_ops = {
    185	.type		= &nft_quota_obj_type,
    186	.size		= sizeof(struct nft_quota),
    187	.init		= nft_quota_obj_init,
    188	.destroy	= nft_quota_obj_destroy,
    189	.eval		= nft_quota_obj_eval,
    190	.dump		= nft_quota_obj_dump,
    191	.update		= nft_quota_obj_update,
    192};
    193
    194static struct nft_object_type nft_quota_obj_type __read_mostly = {
    195	.type		= NFT_OBJECT_QUOTA,
    196	.ops		= &nft_quota_obj_ops,
    197	.maxattr	= NFTA_QUOTA_MAX,
    198	.policy		= nft_quota_policy,
    199	.owner		= THIS_MODULE,
    200};
    201
    202static void nft_quota_eval(const struct nft_expr *expr,
    203			   struct nft_regs *regs,
    204			   const struct nft_pktinfo *pkt)
    205{
    206	struct nft_quota *priv = nft_expr_priv(expr);
    207
    208	nft_quota_do_eval(priv, regs, pkt);
    209}
    210
    211static int nft_quota_init(const struct nft_ctx *ctx,
    212			  const struct nft_expr *expr,
    213			  const struct nlattr * const tb[])
    214{
    215	struct nft_quota *priv = nft_expr_priv(expr);
    216
    217	return nft_quota_do_init(tb, priv);
    218}
    219
    220static int nft_quota_dump(struct sk_buff *skb, const struct nft_expr *expr)
    221{
    222	struct nft_quota *priv = nft_expr_priv(expr);
    223
    224	return nft_quota_do_dump(skb, priv, false);
    225}
    226
    227static void nft_quota_destroy(const struct nft_ctx *ctx,
    228			      const struct nft_expr *expr)
    229{
    230	struct nft_quota *priv = nft_expr_priv(expr);
    231
    232	return nft_quota_do_destroy(ctx, priv);
    233}
    234
    235static int nft_quota_clone(struct nft_expr *dst, const struct nft_expr *src)
    236{
    237	struct nft_quota *priv_dst = nft_expr_priv(dst);
    238
    239	priv_dst->consumed = kmalloc(sizeof(*priv_dst->consumed), GFP_ATOMIC);
    240	if (!priv_dst->consumed)
    241		return -ENOMEM;
    242
    243	atomic64_set(priv_dst->consumed, 0);
    244
    245	return 0;
    246}
    247
    248static struct nft_expr_type nft_quota_type;
    249static const struct nft_expr_ops nft_quota_ops = {
    250	.type		= &nft_quota_type,
    251	.size		= NFT_EXPR_SIZE(sizeof(struct nft_quota)),
    252	.eval		= nft_quota_eval,
    253	.init		= nft_quota_init,
    254	.destroy	= nft_quota_destroy,
    255	.clone		= nft_quota_clone,
    256	.dump		= nft_quota_dump,
    257	.reduce		= NFT_REDUCE_READONLY,
    258};
    259
    260static struct nft_expr_type nft_quota_type __read_mostly = {
    261	.name		= "quota",
    262	.ops		= &nft_quota_ops,
    263	.policy		= nft_quota_policy,
    264	.maxattr	= NFTA_QUOTA_MAX,
    265	.flags		= NFT_EXPR_STATEFUL,
    266	.owner		= THIS_MODULE,
    267};
    268
    269static int __init nft_quota_module_init(void)
    270{
    271	int err;
    272
    273	err = nft_register_obj(&nft_quota_obj_type);
    274	if (err < 0)
    275		return err;
    276
    277	err = nft_register_expr(&nft_quota_type);
    278	if (err < 0)
    279		goto err1;
    280
    281	return 0;
    282err1:
    283	nft_unregister_obj(&nft_quota_obj_type);
    284	return err;
    285}
    286
    287static void __exit nft_quota_module_exit(void)
    288{
    289	nft_unregister_expr(&nft_quota_type);
    290	nft_unregister_obj(&nft_quota_obj_type);
    291}
    292
    293module_init(nft_quota_module_init);
    294module_exit(nft_quota_module_exit);
    295
    296MODULE_LICENSE("GPL");
    297MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
    298MODULE_ALIAS_NFT_EXPR("quota");
    299MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_QUOTA);
    300MODULE_DESCRIPTION("Netfilter nftables quota module");