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        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, 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        atomic64_set(&priv->quota, quota);
  94        priv->flags = flags;
  95        atomic64_set(&priv->consumed, consumed);
  96
  97        return 0;
  98}
  99
 100static int nft_quota_obj_init(const struct nft_ctx *ctx,
 101                              const struct nlattr * const tb[],
 102                              struct nft_object *obj)
 103{
 104        struct nft_quota *priv = nft_obj_data(obj);
 105
 106        return nft_quota_do_init(tb, priv);
 107}
 108
 109static void nft_quota_obj_update(struct nft_object *obj,
 110                                 struct nft_object *newobj)
 111{
 112        struct nft_quota *newpriv = nft_obj_data(newobj);
 113        struct nft_quota *priv = nft_obj_data(obj);
 114        u64 newquota;
 115
 116        newquota = atomic64_read(&newpriv->quota);
 117        atomic64_set(&priv->quota, newquota);
 118        priv->flags = newpriv->flags;
 119}
 120
 121static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv,
 122                             bool reset)
 123{
 124        u64 consumed, consumed_cap, quota;
 125        u32 flags = priv->flags;
 126
 127        /* Since we inconditionally increment consumed quota for each packet
 128         * that we see, don't go over the quota boundary in what we send to
 129         * userspace.
 130         */
 131        consumed = atomic64_read(&priv->consumed);
 132        quota = atomic64_read(&priv->quota);
 133        if (consumed >= quota) {
 134                consumed_cap = quota;
 135                flags |= NFT_QUOTA_F_DEPLETED;
 136        } else {
 137                consumed_cap = consumed;
 138        }
 139
 140        if (nla_put_be64(skb, NFTA_QUOTA_BYTES, cpu_to_be64(quota),
 141                         NFTA_QUOTA_PAD) ||
 142            nla_put_be64(skb, NFTA_QUOTA_CONSUMED, cpu_to_be64(consumed_cap),
 143                         NFTA_QUOTA_PAD) ||
 144            nla_put_be32(skb, NFTA_QUOTA_FLAGS, htonl(flags)))
 145                goto nla_put_failure;
 146
 147        if (reset) {
 148                atomic64_sub(consumed, &priv->consumed);
 149                clear_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags);
 150        }
 151        return 0;
 152
 153nla_put_failure:
 154        return -1;
 155}
 156
 157static int nft_quota_obj_dump(struct sk_buff *skb, struct nft_object *obj,
 158                              bool reset)
 159{
 160        struct nft_quota *priv = nft_obj_data(obj);
 161
 162        return nft_quota_do_dump(skb, priv, reset);
 163}
 164
 165static struct nft_object_type nft_quota_obj_type;
 166static const struct nft_object_ops nft_quota_obj_ops = {
 167        .type           = &nft_quota_obj_type,
 168        .size           = sizeof(struct nft_quota),
 169        .init           = nft_quota_obj_init,
 170        .eval           = nft_quota_obj_eval,
 171        .dump           = nft_quota_obj_dump,
 172        .update         = nft_quota_obj_update,
 173};
 174
 175static struct nft_object_type nft_quota_obj_type __read_mostly = {
 176        .type           = NFT_OBJECT_QUOTA,
 177        .ops            = &nft_quota_obj_ops,
 178        .maxattr        = NFTA_QUOTA_MAX,
 179        .policy         = nft_quota_policy,
 180        .owner          = THIS_MODULE,
 181};
 182
 183static void nft_quota_eval(const struct nft_expr *expr,
 184                           struct nft_regs *regs,
 185                           const struct nft_pktinfo *pkt)
 186{
 187        struct nft_quota *priv = nft_expr_priv(expr);
 188
 189        nft_quota_do_eval(priv, regs, pkt);
 190}
 191
 192static int nft_quota_init(const struct nft_ctx *ctx,
 193                          const struct nft_expr *expr,
 194                          const struct nlattr * const tb[])
 195{
 196        struct nft_quota *priv = nft_expr_priv(expr);
 197
 198        return nft_quota_do_init(tb, priv);
 199}
 200
 201static int nft_quota_dump(struct sk_buff *skb, const struct nft_expr *expr)
 202{
 203        struct nft_quota *priv = nft_expr_priv(expr);
 204
 205        return nft_quota_do_dump(skb, priv, false);
 206}
 207
 208static struct nft_expr_type nft_quota_type;
 209static const struct nft_expr_ops nft_quota_ops = {
 210        .type           = &nft_quota_type,
 211        .size           = NFT_EXPR_SIZE(sizeof(struct nft_quota)),
 212        .eval           = nft_quota_eval,
 213        .init           = nft_quota_init,
 214        .dump           = nft_quota_dump,
 215};
 216
 217static struct nft_expr_type nft_quota_type __read_mostly = {
 218        .name           = "quota",
 219        .ops            = &nft_quota_ops,
 220        .policy         = nft_quota_policy,
 221        .maxattr        = NFTA_QUOTA_MAX,
 222        .flags          = NFT_EXPR_STATEFUL,
 223        .owner          = THIS_MODULE,
 224};
 225
 226static int __init nft_quota_module_init(void)
 227{
 228        int err;
 229
 230        err = nft_register_obj(&nft_quota_obj_type);
 231        if (err < 0)
 232                return err;
 233
 234        err = nft_register_expr(&nft_quota_type);
 235        if (err < 0)
 236                goto err1;
 237
 238        return 0;
 239err1:
 240        nft_unregister_obj(&nft_quota_obj_type);
 241        return err;
 242}
 243
 244static void __exit nft_quota_module_exit(void)
 245{
 246        nft_unregister_expr(&nft_quota_type);
 247        nft_unregister_obj(&nft_quota_obj_type);
 248}
 249
 250module_init(nft_quota_module_init);
 251module_exit(nft_quota_module_exit);
 252
 253MODULE_LICENSE("GPL");
 254MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
 255MODULE_ALIAS_NFT_EXPR("quota");
 256MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_QUOTA);
 257MODULE_DESCRIPTION("Netfilter nftables quota module");
 258