linux/net/netfilter/nft_quota.c
<<
>>
Prefs
   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        u64             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) >= priv->quota;
  25}
  26
  27static inline bool nft_quota_invert(struct nft_quota *priv)
  28{
  29        return priv->flags & NFT_QUOTA_F_INV;
  30}
  31
  32static inline void nft_quota_do_eval(struct nft_quota *priv,
  33                                     struct nft_regs *regs,
  34                                     const struct nft_pktinfo *pkt)
  35{
  36        if (nft_overquota(priv, pkt->skb) ^ nft_quota_invert(priv))
  37                regs->verdict.code = NFT_BREAK;
  38}
  39
  40static const struct nla_policy nft_quota_policy[NFTA_QUOTA_MAX + 1] = {
  41        [NFTA_QUOTA_BYTES]      = { .type = NLA_U64 },
  42        [NFTA_QUOTA_FLAGS]      = { .type = NLA_U32 },
  43        [NFTA_QUOTA_CONSUMED]   = { .type = NLA_U64 },
  44};
  45
  46#define NFT_QUOTA_DEPLETED_BIT  1       /* From NFT_QUOTA_F_DEPLETED. */
  47
  48static void nft_quota_obj_eval(struct nft_object *obj,
  49                               struct nft_regs *regs,
  50                               const struct nft_pktinfo *pkt)
  51{
  52        struct nft_quota *priv = nft_obj_data(obj);
  53        bool overquota;
  54
  55        overquota = nft_overquota(priv, pkt->skb);
  56        if (overquota ^ nft_quota_invert(priv))
  57                regs->verdict.code = NFT_BREAK;
  58
  59        if (overquota &&
  60            !test_and_set_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags))
  61                nft_obj_notify(nft_net(pkt), obj->key.table, obj, 0, 0,
  62                               NFT_MSG_NEWOBJ, nft_pf(pkt), 0, GFP_ATOMIC);
  63}
  64
  65static int nft_quota_do_init(const struct nlattr * const tb[],
  66                             struct nft_quota *priv)
  67{
  68        unsigned long flags = 0;
  69        u64 quota, consumed = 0;
  70
  71        if (!tb[NFTA_QUOTA_BYTES])
  72                return -EINVAL;
  73
  74        quota = be64_to_cpu(nla_get_be64(tb[NFTA_QUOTA_BYTES]));
  75        if (quota > S64_MAX)
  76                return -EOVERFLOW;
  77
  78        if (tb[NFTA_QUOTA_CONSUMED]) {
  79                consumed = be64_to_cpu(nla_get_be64(tb[NFTA_QUOTA_CONSUMED]));
  80                if (consumed > quota)
  81                        return -EINVAL;
  82        }
  83
  84        if (tb[NFTA_QUOTA_FLAGS]) {
  85                flags = ntohl(nla_get_be32(tb[NFTA_QUOTA_FLAGS]));
  86                if (flags & ~NFT_QUOTA_F_INV)
  87                        return -EINVAL;
  88                if (flags & NFT_QUOTA_F_DEPLETED)
  89                        return -EOPNOTSUPP;
  90        }
  91
  92        priv->quota = quota;
  93        priv->flags = flags;
  94        atomic64_set(&priv->consumed, consumed);
  95
  96        return 0;
  97}
  98
  99static int nft_quota_obj_init(const struct nft_ctx *ctx,
 100                              const struct nlattr * const tb[],
 101                              struct nft_object *obj)
 102{
 103        struct nft_quota *priv = nft_obj_data(obj);
 104
 105        return nft_quota_do_init(tb, priv);
 106}
 107
 108static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv,
 109                             bool reset)
 110{
 111        u64 consumed, consumed_cap;
 112        u32 flags = priv->flags;
 113
 114        /* Since we inconditionally increment consumed quota for each packet
 115         * that we see, don't go over the quota boundary in what we send to
 116         * userspace.
 117         */
 118        consumed = atomic64_read(&priv->consumed);
 119        if (consumed >= priv->quota) {
 120                consumed_cap = priv->quota;
 121                flags |= NFT_QUOTA_F_DEPLETED;
 122        } else {
 123                consumed_cap = consumed;
 124        }
 125
 126        if (nla_put_be64(skb, NFTA_QUOTA_BYTES, cpu_to_be64(priv->quota),
 127                         NFTA_QUOTA_PAD) ||
 128            nla_put_be64(skb, NFTA_QUOTA_CONSUMED, cpu_to_be64(consumed_cap),
 129                         NFTA_QUOTA_PAD) ||
 130            nla_put_be32(skb, NFTA_QUOTA_FLAGS, htonl(flags)))
 131                goto nla_put_failure;
 132
 133        if (reset) {
 134                atomic64_sub(consumed, &priv->consumed);
 135                clear_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags);
 136        }
 137        return 0;
 138
 139nla_put_failure:
 140        return -1;
 141}
 142
 143static int nft_quota_obj_dump(struct sk_buff *skb, struct nft_object *obj,
 144                              bool reset)
 145{
 146        struct nft_quota *priv = nft_obj_data(obj);
 147
 148        return nft_quota_do_dump(skb, priv, reset);
 149}
 150
 151static struct nft_object_type nft_quota_obj_type;
 152static const struct nft_object_ops nft_quota_obj_ops = {
 153        .type           = &nft_quota_obj_type,
 154        .size           = sizeof(struct nft_quota),
 155        .init           = nft_quota_obj_init,
 156        .eval           = nft_quota_obj_eval,
 157        .dump           = nft_quota_obj_dump,
 158};
 159
 160static struct nft_object_type nft_quota_obj_type __read_mostly = {
 161        .type           = NFT_OBJECT_QUOTA,
 162        .ops            = &nft_quota_obj_ops,
 163        .maxattr        = NFTA_QUOTA_MAX,
 164        .policy         = nft_quota_policy,
 165        .owner          = THIS_MODULE,
 166};
 167
 168static void nft_quota_eval(const struct nft_expr *expr,
 169                           struct nft_regs *regs,
 170                           const struct nft_pktinfo *pkt)
 171{
 172        struct nft_quota *priv = nft_expr_priv(expr);
 173
 174        nft_quota_do_eval(priv, regs, pkt);
 175}
 176
 177static int nft_quota_init(const struct nft_ctx *ctx,
 178                          const struct nft_expr *expr,
 179                          const struct nlattr * const tb[])
 180{
 181        struct nft_quota *priv = nft_expr_priv(expr);
 182
 183        return nft_quota_do_init(tb, priv);
 184}
 185
 186static int nft_quota_dump(struct sk_buff *skb, const struct nft_expr *expr)
 187{
 188        struct nft_quota *priv = nft_expr_priv(expr);
 189
 190        return nft_quota_do_dump(skb, priv, false);
 191}
 192
 193static struct nft_expr_type nft_quota_type;
 194static const struct nft_expr_ops nft_quota_ops = {
 195        .type           = &nft_quota_type,
 196        .size           = NFT_EXPR_SIZE(sizeof(struct nft_quota)),
 197        .eval           = nft_quota_eval,
 198        .init           = nft_quota_init,
 199        .dump           = nft_quota_dump,
 200};
 201
 202static struct nft_expr_type nft_quota_type __read_mostly = {
 203        .name           = "quota",
 204        .ops            = &nft_quota_ops,
 205        .policy         = nft_quota_policy,
 206        .maxattr        = NFTA_QUOTA_MAX,
 207        .flags          = NFT_EXPR_STATEFUL,
 208        .owner          = THIS_MODULE,
 209};
 210
 211static int __init nft_quota_module_init(void)
 212{
 213        int err;
 214
 215        err = nft_register_obj(&nft_quota_obj_type);
 216        if (err < 0)
 217                return err;
 218
 219        err = nft_register_expr(&nft_quota_type);
 220        if (err < 0)
 221                goto err1;
 222
 223        return 0;
 224err1:
 225        nft_unregister_obj(&nft_quota_obj_type);
 226        return err;
 227}
 228
 229static void __exit nft_quota_module_exit(void)
 230{
 231        nft_unregister_expr(&nft_quota_type);
 232        nft_unregister_obj(&nft_quota_obj_type);
 233}
 234
 235module_init(nft_quota_module_init);
 236module_exit(nft_quota_module_exit);
 237
 238MODULE_LICENSE("GPL");
 239MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
 240MODULE_ALIAS_NFT_EXPR("quota");
 241MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_QUOTA);
 242