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_limit.c (12238B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
      4 *
      5 * Development of this code funded by Astaro AG (http://www.astaro.com/)
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/init.h>
     10#include <linux/module.h>
     11#include <linux/spinlock.h>
     12#include <linux/netlink.h>
     13#include <linux/netfilter.h>
     14#include <linux/netfilter/nf_tables.h>
     15#include <net/netfilter/nf_tables.h>
     16
     17struct nft_limit {
     18	spinlock_t	lock;
     19	u64		last;
     20	u64		tokens;
     21};
     22
     23struct nft_limit_priv {
     24	struct nft_limit *limit;
     25	u64		tokens_max;
     26	u64		rate;
     27	u64		nsecs;
     28	u32		burst;
     29	bool		invert;
     30};
     31
     32static inline bool nft_limit_eval(struct nft_limit_priv *priv, u64 cost)
     33{
     34	u64 now, tokens;
     35	s64 delta;
     36
     37	spin_lock_bh(&priv->limit->lock);
     38	now = ktime_get_ns();
     39	tokens = priv->limit->tokens + now - priv->limit->last;
     40	if (tokens > priv->tokens_max)
     41		tokens = priv->tokens_max;
     42
     43	priv->limit->last = now;
     44	delta = tokens - cost;
     45	if (delta >= 0) {
     46		priv->limit->tokens = delta;
     47		spin_unlock_bh(&priv->limit->lock);
     48		return priv->invert;
     49	}
     50	priv->limit->tokens = tokens;
     51	spin_unlock_bh(&priv->limit->lock);
     52	return !priv->invert;
     53}
     54
     55/* Use same default as in iptables. */
     56#define NFT_LIMIT_PKT_BURST_DEFAULT	5
     57
     58static int nft_limit_init(struct nft_limit_priv *priv,
     59			  const struct nlattr * const tb[], bool pkts)
     60{
     61	u64 unit, tokens;
     62
     63	if (tb[NFTA_LIMIT_RATE] == NULL ||
     64	    tb[NFTA_LIMIT_UNIT] == NULL)
     65		return -EINVAL;
     66
     67	priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE]));
     68	unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT]));
     69	priv->nsecs = unit * NSEC_PER_SEC;
     70	if (priv->rate == 0 || priv->nsecs < unit)
     71		return -EOVERFLOW;
     72
     73	if (tb[NFTA_LIMIT_BURST])
     74		priv->burst = ntohl(nla_get_be32(tb[NFTA_LIMIT_BURST]));
     75
     76	if (pkts && priv->burst == 0)
     77		priv->burst = NFT_LIMIT_PKT_BURST_DEFAULT;
     78
     79	if (priv->rate + priv->burst < priv->rate)
     80		return -EOVERFLOW;
     81
     82	if (pkts) {
     83		tokens = div64_u64(priv->nsecs, priv->rate) * priv->burst;
     84	} else {
     85		/* The token bucket size limits the number of tokens can be
     86		 * accumulated. tokens_max specifies the bucket size.
     87		 * tokens_max = unit * (rate + burst) / rate.
     88		 */
     89		tokens = div64_u64(priv->nsecs * (priv->rate + priv->burst),
     90				 priv->rate);
     91	}
     92
     93	priv->limit = kmalloc(sizeof(*priv->limit), GFP_KERNEL_ACCOUNT);
     94	if (!priv->limit)
     95		return -ENOMEM;
     96
     97	priv->limit->tokens = tokens;
     98	priv->tokens_max = priv->limit->tokens;
     99
    100	if (tb[NFTA_LIMIT_FLAGS]) {
    101		u32 flags = ntohl(nla_get_be32(tb[NFTA_LIMIT_FLAGS]));
    102
    103		if (flags & NFT_LIMIT_F_INV)
    104			priv->invert = true;
    105	}
    106	priv->limit->last = ktime_get_ns();
    107	spin_lock_init(&priv->limit->lock);
    108
    109	return 0;
    110}
    111
    112static int nft_limit_dump(struct sk_buff *skb, const struct nft_limit_priv *priv,
    113			  enum nft_limit_type type)
    114{
    115	u32 flags = priv->invert ? NFT_LIMIT_F_INV : 0;
    116	u64 secs = div_u64(priv->nsecs, NSEC_PER_SEC);
    117
    118	if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate),
    119			 NFTA_LIMIT_PAD) ||
    120	    nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(secs),
    121			 NFTA_LIMIT_PAD) ||
    122	    nla_put_be32(skb, NFTA_LIMIT_BURST, htonl(priv->burst)) ||
    123	    nla_put_be32(skb, NFTA_LIMIT_TYPE, htonl(type)) ||
    124	    nla_put_be32(skb, NFTA_LIMIT_FLAGS, htonl(flags)))
    125		goto nla_put_failure;
    126	return 0;
    127
    128nla_put_failure:
    129	return -1;
    130}
    131
    132static void nft_limit_destroy(const struct nft_ctx *ctx,
    133			      const struct nft_limit_priv *priv)
    134{
    135	kfree(priv->limit);
    136}
    137
    138static int nft_limit_clone(struct nft_limit_priv *priv_dst,
    139			   const struct nft_limit_priv *priv_src)
    140{
    141	priv_dst->tokens_max = priv_src->tokens_max;
    142	priv_dst->rate = priv_src->rate;
    143	priv_dst->nsecs = priv_src->nsecs;
    144	priv_dst->burst = priv_src->burst;
    145	priv_dst->invert = priv_src->invert;
    146
    147	priv_dst->limit = kmalloc(sizeof(*priv_dst->limit), GFP_ATOMIC);
    148	if (!priv_dst->limit)
    149		return -ENOMEM;
    150
    151	spin_lock_init(&priv_dst->limit->lock);
    152	priv_dst->limit->tokens = priv_src->tokens_max;
    153	priv_dst->limit->last = ktime_get_ns();
    154
    155	return 0;
    156}
    157
    158struct nft_limit_priv_pkts {
    159	struct nft_limit_priv	limit;
    160	u64			cost;
    161};
    162
    163static void nft_limit_pkts_eval(const struct nft_expr *expr,
    164				struct nft_regs *regs,
    165				const struct nft_pktinfo *pkt)
    166{
    167	struct nft_limit_priv_pkts *priv = nft_expr_priv(expr);
    168
    169	if (nft_limit_eval(&priv->limit, priv->cost))
    170		regs->verdict.code = NFT_BREAK;
    171}
    172
    173static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = {
    174	[NFTA_LIMIT_RATE]	= { .type = NLA_U64 },
    175	[NFTA_LIMIT_UNIT]	= { .type = NLA_U64 },
    176	[NFTA_LIMIT_BURST]	= { .type = NLA_U32 },
    177	[NFTA_LIMIT_TYPE]	= { .type = NLA_U32 },
    178	[NFTA_LIMIT_FLAGS]	= { .type = NLA_U32 },
    179};
    180
    181static int nft_limit_pkts_init(const struct nft_ctx *ctx,
    182			       const struct nft_expr *expr,
    183			       const struct nlattr * const tb[])
    184{
    185	struct nft_limit_priv_pkts *priv = nft_expr_priv(expr);
    186	int err;
    187
    188	err = nft_limit_init(&priv->limit, tb, true);
    189	if (err < 0)
    190		return err;
    191
    192	priv->cost = div64_u64(priv->limit.nsecs, priv->limit.rate);
    193	return 0;
    194}
    195
    196static int nft_limit_pkts_dump(struct sk_buff *skb, const struct nft_expr *expr)
    197{
    198	const struct nft_limit_priv_pkts *priv = nft_expr_priv(expr);
    199
    200	return nft_limit_dump(skb, &priv->limit, NFT_LIMIT_PKTS);
    201}
    202
    203static void nft_limit_pkts_destroy(const struct nft_ctx *ctx,
    204				   const struct nft_expr *expr)
    205{
    206	const struct nft_limit_priv_pkts *priv = nft_expr_priv(expr);
    207
    208	nft_limit_destroy(ctx, &priv->limit);
    209}
    210
    211static int nft_limit_pkts_clone(struct nft_expr *dst, const struct nft_expr *src)
    212{
    213	struct nft_limit_priv_pkts *priv_dst = nft_expr_priv(dst);
    214	struct nft_limit_priv_pkts *priv_src = nft_expr_priv(src);
    215
    216	priv_dst->cost = priv_src->cost;
    217
    218	return nft_limit_clone(&priv_dst->limit, &priv_src->limit);
    219}
    220
    221static struct nft_expr_type nft_limit_type;
    222static const struct nft_expr_ops nft_limit_pkts_ops = {
    223	.type		= &nft_limit_type,
    224	.size		= NFT_EXPR_SIZE(sizeof(struct nft_limit_priv_pkts)),
    225	.eval		= nft_limit_pkts_eval,
    226	.init		= nft_limit_pkts_init,
    227	.destroy	= nft_limit_pkts_destroy,
    228	.clone		= nft_limit_pkts_clone,
    229	.dump		= nft_limit_pkts_dump,
    230	.reduce		= NFT_REDUCE_READONLY,
    231};
    232
    233static void nft_limit_bytes_eval(const struct nft_expr *expr,
    234				 struct nft_regs *regs,
    235				 const struct nft_pktinfo *pkt)
    236{
    237	struct nft_limit_priv *priv = nft_expr_priv(expr);
    238	u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate);
    239
    240	if (nft_limit_eval(priv, cost))
    241		regs->verdict.code = NFT_BREAK;
    242}
    243
    244static int nft_limit_bytes_init(const struct nft_ctx *ctx,
    245				const struct nft_expr *expr,
    246				const struct nlattr * const tb[])
    247{
    248	struct nft_limit_priv *priv = nft_expr_priv(expr);
    249
    250	return nft_limit_init(priv, tb, false);
    251}
    252
    253static int nft_limit_bytes_dump(struct sk_buff *skb,
    254				const struct nft_expr *expr)
    255{
    256	const struct nft_limit_priv *priv = nft_expr_priv(expr);
    257
    258	return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES);
    259}
    260
    261static void nft_limit_bytes_destroy(const struct nft_ctx *ctx,
    262				    const struct nft_expr *expr)
    263{
    264	const struct nft_limit_priv *priv = nft_expr_priv(expr);
    265
    266	nft_limit_destroy(ctx, priv);
    267}
    268
    269static int nft_limit_bytes_clone(struct nft_expr *dst, const struct nft_expr *src)
    270{
    271	struct nft_limit_priv *priv_dst = nft_expr_priv(dst);
    272	struct nft_limit_priv *priv_src = nft_expr_priv(src);
    273
    274	return nft_limit_clone(priv_dst, priv_src);
    275}
    276
    277static const struct nft_expr_ops nft_limit_bytes_ops = {
    278	.type		= &nft_limit_type,
    279	.size		= NFT_EXPR_SIZE(sizeof(struct nft_limit_priv)),
    280	.eval		= nft_limit_bytes_eval,
    281	.init		= nft_limit_bytes_init,
    282	.dump		= nft_limit_bytes_dump,
    283	.clone		= nft_limit_bytes_clone,
    284	.destroy	= nft_limit_bytes_destroy,
    285	.reduce		= NFT_REDUCE_READONLY,
    286};
    287
    288static const struct nft_expr_ops *
    289nft_limit_select_ops(const struct nft_ctx *ctx,
    290		     const struct nlattr * const tb[])
    291{
    292	if (tb[NFTA_LIMIT_TYPE] == NULL)
    293		return &nft_limit_pkts_ops;
    294
    295	switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) {
    296	case NFT_LIMIT_PKTS:
    297		return &nft_limit_pkts_ops;
    298	case NFT_LIMIT_PKT_BYTES:
    299		return &nft_limit_bytes_ops;
    300	}
    301	return ERR_PTR(-EOPNOTSUPP);
    302}
    303
    304static struct nft_expr_type nft_limit_type __read_mostly = {
    305	.name		= "limit",
    306	.select_ops	= nft_limit_select_ops,
    307	.policy		= nft_limit_policy,
    308	.maxattr	= NFTA_LIMIT_MAX,
    309	.flags		= NFT_EXPR_STATEFUL,
    310	.owner		= THIS_MODULE,
    311};
    312
    313static void nft_limit_obj_pkts_eval(struct nft_object *obj,
    314				    struct nft_regs *regs,
    315				    const struct nft_pktinfo *pkt)
    316{
    317	struct nft_limit_priv_pkts *priv = nft_obj_data(obj);
    318
    319	if (nft_limit_eval(&priv->limit, priv->cost))
    320		regs->verdict.code = NFT_BREAK;
    321}
    322
    323static int nft_limit_obj_pkts_init(const struct nft_ctx *ctx,
    324				   const struct nlattr * const tb[],
    325				   struct nft_object *obj)
    326{
    327	struct nft_limit_priv_pkts *priv = nft_obj_data(obj);
    328	int err;
    329
    330	err = nft_limit_init(&priv->limit, tb, true);
    331	if (err < 0)
    332		return err;
    333
    334	priv->cost = div64_u64(priv->limit.nsecs, priv->limit.rate);
    335	return 0;
    336}
    337
    338static int nft_limit_obj_pkts_dump(struct sk_buff *skb,
    339				   struct nft_object *obj,
    340				   bool reset)
    341{
    342	const struct nft_limit_priv_pkts *priv = nft_obj_data(obj);
    343
    344	return nft_limit_dump(skb, &priv->limit, NFT_LIMIT_PKTS);
    345}
    346
    347static void nft_limit_obj_pkts_destroy(const struct nft_ctx *ctx,
    348				       struct nft_object *obj)
    349{
    350	struct nft_limit_priv_pkts *priv = nft_obj_data(obj);
    351
    352	nft_limit_destroy(ctx, &priv->limit);
    353}
    354
    355static struct nft_object_type nft_limit_obj_type;
    356static const struct nft_object_ops nft_limit_obj_pkts_ops = {
    357	.type		= &nft_limit_obj_type,
    358	.size		= NFT_EXPR_SIZE(sizeof(struct nft_limit_priv_pkts)),
    359	.init		= nft_limit_obj_pkts_init,
    360	.destroy	= nft_limit_obj_pkts_destroy,
    361	.eval		= nft_limit_obj_pkts_eval,
    362	.dump		= nft_limit_obj_pkts_dump,
    363};
    364
    365static void nft_limit_obj_bytes_eval(struct nft_object *obj,
    366				     struct nft_regs *regs,
    367				     const struct nft_pktinfo *pkt)
    368{
    369	struct nft_limit_priv *priv = nft_obj_data(obj);
    370	u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate);
    371
    372	if (nft_limit_eval(priv, cost))
    373		regs->verdict.code = NFT_BREAK;
    374}
    375
    376static int nft_limit_obj_bytes_init(const struct nft_ctx *ctx,
    377				    const struct nlattr * const tb[],
    378				    struct nft_object *obj)
    379{
    380	struct nft_limit_priv *priv = nft_obj_data(obj);
    381
    382	return nft_limit_init(priv, tb, false);
    383}
    384
    385static int nft_limit_obj_bytes_dump(struct sk_buff *skb,
    386				    struct nft_object *obj,
    387				    bool reset)
    388{
    389	const struct nft_limit_priv *priv = nft_obj_data(obj);
    390
    391	return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES);
    392}
    393
    394static void nft_limit_obj_bytes_destroy(const struct nft_ctx *ctx,
    395					struct nft_object *obj)
    396{
    397	struct nft_limit_priv *priv = nft_obj_data(obj);
    398
    399	nft_limit_destroy(ctx, priv);
    400}
    401
    402static struct nft_object_type nft_limit_obj_type;
    403static const struct nft_object_ops nft_limit_obj_bytes_ops = {
    404	.type		= &nft_limit_obj_type,
    405	.size		= sizeof(struct nft_limit_priv),
    406	.init		= nft_limit_obj_bytes_init,
    407	.destroy	= nft_limit_obj_bytes_destroy,
    408	.eval		= nft_limit_obj_bytes_eval,
    409	.dump		= nft_limit_obj_bytes_dump,
    410};
    411
    412static const struct nft_object_ops *
    413nft_limit_obj_select_ops(const struct nft_ctx *ctx,
    414			 const struct nlattr * const tb[])
    415{
    416	if (!tb[NFTA_LIMIT_TYPE])
    417		return &nft_limit_obj_pkts_ops;
    418
    419	switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) {
    420	case NFT_LIMIT_PKTS:
    421		return &nft_limit_obj_pkts_ops;
    422	case NFT_LIMIT_PKT_BYTES:
    423		return &nft_limit_obj_bytes_ops;
    424	}
    425	return ERR_PTR(-EOPNOTSUPP);
    426}
    427
    428static struct nft_object_type nft_limit_obj_type __read_mostly = {
    429	.select_ops	= nft_limit_obj_select_ops,
    430	.type		= NFT_OBJECT_LIMIT,
    431	.maxattr	= NFTA_LIMIT_MAX,
    432	.policy		= nft_limit_policy,
    433	.owner		= THIS_MODULE,
    434};
    435
    436static int __init nft_limit_module_init(void)
    437{
    438	int err;
    439
    440	err = nft_register_obj(&nft_limit_obj_type);
    441	if (err < 0)
    442		return err;
    443
    444	err = nft_register_expr(&nft_limit_type);
    445	if (err < 0)
    446		goto err1;
    447
    448	return 0;
    449err1:
    450	nft_unregister_obj(&nft_limit_obj_type);
    451	return err;
    452}
    453
    454static void __exit nft_limit_module_exit(void)
    455{
    456	nft_unregister_expr(&nft_limit_type);
    457	nft_unregister_obj(&nft_limit_obj_type);
    458}
    459
    460module_init(nft_limit_module_init);
    461module_exit(nft_limit_module_exit);
    462
    463MODULE_LICENSE("GPL");
    464MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
    465MODULE_ALIAS_NFT_EXPR("limit");
    466MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_LIMIT);
    467MODULE_DESCRIPTION("nftables limit expression support");