netfilter: nft_quota: dump consumed quota
[linux-2.6-block.git] / net / netfilter / nft_quota.c
CommitLineData
3d2f30a1
PNA
1/*
2 * Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/kernel.h>
10#include <linux/init.h>
11#include <linux/module.h>
12#include <linux/atomic.h>
13#include <linux/netlink.h>
14#include <linux/netfilter.h>
15#include <linux/netfilter/nf_tables.h>
16#include <net/netfilter/nf_tables.h>
17
18struct nft_quota {
19 u64 quota;
20 bool invert;
795595f6 21 atomic64_t consumed;
3d2f30a1
PNA
22};
23
22609b43 24static inline bool nft_overquota(struct nft_quota *priv,
795595f6 25 const struct sk_buff *skb)
3d2f30a1 26{
795595f6 27 return atomic64_add_return(skb->len, &priv->consumed) >= priv->quota;
3d2f30a1
PNA
28}
29
173705d9
PNA
30static inline void nft_quota_do_eval(struct nft_quota *priv,
31 struct nft_regs *regs,
32 const struct nft_pktinfo *pkt)
3d2f30a1 33{
795595f6 34 if (nft_overquota(priv, pkt->skb) ^ priv->invert)
3d2f30a1
PNA
35 regs->verdict.code = NFT_BREAK;
36}
37
38static const struct nla_policy nft_quota_policy[NFTA_QUOTA_MAX + 1] = {
39 [NFTA_QUOTA_BYTES] = { .type = NLA_U64 },
40 [NFTA_QUOTA_FLAGS] = { .type = NLA_U32 },
41};
42
173705d9
PNA
43static void nft_quota_obj_eval(struct nft_object *obj,
44 struct nft_regs *regs,
45 const struct nft_pktinfo *pkt)
46{
47 struct nft_quota *priv = nft_obj_data(obj);
48
49 nft_quota_do_eval(priv, regs, pkt);
50}
51
52static int nft_quota_do_init(const struct nlattr * const tb[],
53 struct nft_quota *priv)
3d2f30a1 54{
3d2f30a1
PNA
55 u32 flags = 0;
56 u64 quota;
57
58 if (!tb[NFTA_QUOTA_BYTES])
59 return -EINVAL;
60
61 quota = be64_to_cpu(nla_get_be64(tb[NFTA_QUOTA_BYTES]));
62 if (quota > S64_MAX)
63 return -EOVERFLOW;
64
65 if (tb[NFTA_QUOTA_FLAGS]) {
66 flags = ntohl(nla_get_be32(tb[NFTA_QUOTA_FLAGS]));
67 if (flags & ~NFT_QUOTA_F_INV)
68 return -EINVAL;
69 }
70
71 priv->quota = quota;
72 priv->invert = (flags & NFT_QUOTA_F_INV) ? true : false;
795595f6 73 atomic64_set(&priv->consumed, 0);
3d2f30a1
PNA
74
75 return 0;
76}
77
173705d9
PNA
78static int nft_quota_obj_init(const struct nlattr * const tb[],
79 struct nft_object *obj)
80{
81 struct nft_quota *priv = nft_obj_data(obj);
82
83 return nft_quota_do_init(tb, priv);
84}
85
86static int nft_quota_do_dump(struct sk_buff *skb, const struct nft_quota *priv)
3d2f30a1 87{
3d2f30a1 88 u32 flags = priv->invert ? NFT_QUOTA_F_INV : 0;
795595f6
PNA
89 u64 consumed;
90
91 consumed = atomic64_read(&priv->consumed);
92 /* Since we inconditionally increment consumed quota for each packet
93 * that we see, don't go over the quota boundary in what we send to
94 * userspace.
95 */
96 if (consumed > priv->quota)
97 consumed = priv->quota;
3d2f30a1
PNA
98
99 if (nla_put_be64(skb, NFTA_QUOTA_BYTES, cpu_to_be64(priv->quota),
100 NFTA_QUOTA_PAD) ||
795595f6
PNA
101 nla_put_be64(skb, NFTA_QUOTA_CONSUMED, cpu_to_be64(consumed),
102 NFTA_QUOTA_PAD) ||
3d2f30a1
PNA
103 nla_put_be32(skb, NFTA_QUOTA_FLAGS, htonl(flags)))
104 goto nla_put_failure;
105 return 0;
106
107nla_put_failure:
108 return -1;
109}
110
173705d9
PNA
111static int nft_quota_obj_dump(struct sk_buff *skb, const struct nft_object *obj)
112{
113 struct nft_quota *priv = nft_obj_data(obj);
114
115 return nft_quota_do_dump(skb, priv);
116}
117
118static struct nft_object_type nft_quota_obj __read_mostly = {
119 .type = NFT_OBJECT_QUOTA,
120 .size = sizeof(struct nft_quota),
121 .maxattr = NFTA_QUOTA_MAX,
122 .policy = nft_quota_policy,
123 .init = nft_quota_obj_init,
124 .eval = nft_quota_obj_eval,
125 .dump = nft_quota_obj_dump,
126 .owner = THIS_MODULE,
127};
128
129static void nft_quota_eval(const struct nft_expr *expr,
130 struct nft_regs *regs,
131 const struct nft_pktinfo *pkt)
132{
133 struct nft_quota *priv = nft_expr_priv(expr);
134
135 nft_quota_do_eval(priv, regs, pkt);
136}
137
138static int nft_quota_init(const struct nft_ctx *ctx,
139 const struct nft_expr *expr,
140 const struct nlattr * const tb[])
141{
142 struct nft_quota *priv = nft_expr_priv(expr);
143
144 return nft_quota_do_init(tb, priv);
145}
146
147static int nft_quota_dump(struct sk_buff *skb, const struct nft_expr *expr)
148{
149 const struct nft_quota *priv = nft_expr_priv(expr);
150
151 return nft_quota_do_dump(skb, priv);
152}
153
3d2f30a1
PNA
154static struct nft_expr_type nft_quota_type;
155static const struct nft_expr_ops nft_quota_ops = {
156 .type = &nft_quota_type,
157 .size = NFT_EXPR_SIZE(sizeof(struct nft_quota)),
158 .eval = nft_quota_eval,
159 .init = nft_quota_init,
160 .dump = nft_quota_dump,
161};
162
163static struct nft_expr_type nft_quota_type __read_mostly = {
164 .name = "quota",
165 .ops = &nft_quota_ops,
166 .policy = nft_quota_policy,
167 .maxattr = NFTA_QUOTA_MAX,
168 .flags = NFT_EXPR_STATEFUL,
169 .owner = THIS_MODULE,
170};
171
172static int __init nft_quota_module_init(void)
173{
173705d9
PNA
174 int err;
175
176 err = nft_register_obj(&nft_quota_obj);
177 if (err < 0)
178 return err;
179
180 err = nft_register_expr(&nft_quota_type);
181 if (err < 0)
182 goto err1;
183
184 return 0;
185err1:
186 nft_unregister_obj(&nft_quota_obj);
187 return err;
3d2f30a1
PNA
188}
189
190static void __exit nft_quota_module_exit(void)
191{
173705d9
PNA
192 nft_unregister_expr(&nft_quota_type);
193 nft_unregister_obj(&nft_quota_obj);
3d2f30a1
PNA
194}
195
196module_init(nft_quota_module_init);
197module_exit(nft_quota_module_exit);
198
199MODULE_LICENSE("GPL");
200MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
201MODULE_ALIAS_NFT_EXPR("quota");
173705d9 202MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_QUOTA);