File Index
Files and directories
Sign in
FILE

nft_connlimit.c

File preview with syntax highlighting.

linux-6.19.14/net/netfilter/nft_connlimit.c C
  1/* SPDX-License-Identifier: GPL-2.0 */
  2#include <linux/kernel.h>
  3#include <linux/init.h>
  4#include <linux/module.h>
  5#include <linux/spinlock.h>
  6#include <linux/netlink.h>
  7#include <linux/netfilter.h>
  8#include <linux/netfilter/nf_tables.h>
  9#include <net/netfilter/nf_tables.h>
 10#include <net/netfilter/nf_conntrack.h>
 11#include <net/netfilter/nf_conntrack_count.h>
 12#include <net/netfilter/nf_conntrack_core.h>
 13#include <net/netfilter/nf_conntrack_tuple.h>
 14#include <net/netfilter/nf_conntrack_zones.h>
 15
 16struct nft_connlimit {
 17	struct nf_conncount_list	*list;
 18	u32				limit;
 19	bool				invert;
 20};
 21
 22static inline void nft_connlimit_do_eval(struct nft_connlimit *priv,
 23					 struct nft_regs *regs,
 24					 const struct nft_pktinfo *pkt,
 25					 const struct nft_set_ext *ext)
 26{
 27	unsigned int count;
 28	int err;
 29
 30	err = nf_conncount_add_skb(nft_net(pkt), pkt->skb, nft_pf(pkt), priv->list);
 31	if (err) {
 32		if (err == -EEXIST) {
 33			/* Call gc to update the list count if any connection has
 34			 * been closed already. This is useful for softlimit
 35			 * connections like limiting bandwidth based on a number
 36			 * of open connections.
 37			 */
 38			nf_conncount_gc_list(nft_net(pkt), priv->list);
 39		} else {
 40			regs->verdict.code = NF_DROP;
 41			return;
 42		}
 43	}
 44
 45	count = READ_ONCE(priv->list->count);
 46
 47	if ((count > READ_ONCE(priv->limit)) ^ READ_ONCE(priv->invert)) {
 48		regs->verdict.code = NFT_BREAK;
 49		return;
 50	}
 51}
 52
 53static int nft_connlimit_do_init(const struct nft_ctx *ctx,
 54				 const struct nlattr * const tb[],
 55				 struct nft_connlimit *priv)
 56{
 57	bool invert = false;
 58	u32 flags, limit;
 59	int err;
 60
 61	if (!tb[NFTA_CONNLIMIT_COUNT])
 62		return -EINVAL;
 63
 64	limit = ntohl(nla_get_be32(tb[NFTA_CONNLIMIT_COUNT]));
 65
 66	if (tb[NFTA_CONNLIMIT_FLAGS]) {
 67		flags = ntohl(nla_get_be32(tb[NFTA_CONNLIMIT_FLAGS]));
 68		if (flags & ~NFT_CONNLIMIT_F_INV)
 69			return -EOPNOTSUPP;
 70		if (flags & NFT_CONNLIMIT_F_INV)
 71			invert = true;
 72	}
 73
 74	priv->list = kmalloc(sizeof(*priv->list), GFP_KERNEL_ACCOUNT);
 75	if (!priv->list)
 76		return -ENOMEM;
 77
 78	nf_conncount_list_init(priv->list);
 79	priv->limit	= limit;
 80	priv->invert	= invert;
 81
 82	err = nf_ct_netns_get(ctx->net, ctx->family);
 83	if (err < 0)
 84		goto err_netns;
 85
 86	return 0;
 87err_netns:
 88	kfree(priv->list);
 89
 90	return err;
 91}
 92
 93static void nft_connlimit_do_destroy(const struct nft_ctx *ctx,
 94				     struct nft_connlimit *priv)
 95{
 96	nf_ct_netns_put(ctx->net, ctx->family);
 97	nf_conncount_cache_free(priv->list);
 98	kfree(priv->list);
 99}
100
101static int nft_connlimit_do_dump(struct sk_buff *skb,
102				 struct nft_connlimit *priv)
103{
104	if (nla_put_be32(skb, NFTA_CONNLIMIT_COUNT, htonl(priv->limit)))
105		goto nla_put_failure;
106	if (priv->invert &&
107	    nla_put_be32(skb, NFTA_CONNLIMIT_FLAGS, htonl(NFT_CONNLIMIT_F_INV)))
108		goto nla_put_failure;
109
110	return 0;
111
112nla_put_failure:
113	return -1;
114}
115
116static inline void nft_connlimit_obj_eval(struct nft_object *obj,
117					struct nft_regs *regs,
118					const struct nft_pktinfo *pkt)
119{
120	struct nft_connlimit *priv = nft_obj_data(obj);
121
122	nft_connlimit_do_eval(priv, regs, pkt, NULL);
123}
124
125static int nft_connlimit_obj_init(const struct nft_ctx *ctx,
126				const struct nlattr * const tb[],
127				struct nft_object *obj)
128{
129	struct nft_connlimit *priv = nft_obj_data(obj);
130
131	return nft_connlimit_do_init(ctx, tb, priv);
132}
133
134static void nft_connlimit_obj_update(struct nft_object *obj,
135				     struct nft_object *newobj)
136{
137	struct nft_connlimit *newpriv = nft_obj_data(newobj);
138	struct nft_connlimit *priv = nft_obj_data(obj);
139
140	WRITE_ONCE(priv->limit, newpriv->limit);
141	WRITE_ONCE(priv->invert, newpriv->invert);
142}
143
144static void nft_connlimit_obj_destroy(const struct nft_ctx *ctx,
145				      struct nft_object *obj)
146{
147	struct nft_connlimit *priv = nft_obj_data(obj);
148
149	nft_connlimit_do_destroy(ctx, priv);
150}
151
152static int nft_connlimit_obj_dump(struct sk_buff *skb,
153				  struct nft_object *obj, bool reset)
154{
155	struct nft_connlimit *priv = nft_obj_data(obj);
156
157	return nft_connlimit_do_dump(skb, priv);
158}
159
160static const struct nla_policy nft_connlimit_policy[NFTA_CONNLIMIT_MAX + 1] = {
161	[NFTA_CONNLIMIT_COUNT]	= { .type = NLA_U32 },
162	[NFTA_CONNLIMIT_FLAGS]	= { .type = NLA_U32 },
163};
164
165static struct nft_object_type nft_connlimit_obj_type;
166static const struct nft_object_ops nft_connlimit_obj_ops = {
167	.type		= &nft_connlimit_obj_type,
168	.size		= sizeof(struct nft_connlimit),
169	.eval		= nft_connlimit_obj_eval,
170	.init		= nft_connlimit_obj_init,
171	.destroy	= nft_connlimit_obj_destroy,
172	.dump		= nft_connlimit_obj_dump,
173	.update		= nft_connlimit_obj_update,
174};
175
176static struct nft_object_type nft_connlimit_obj_type __read_mostly = {
177	.type		= NFT_OBJECT_CONNLIMIT,
178	.ops		= &nft_connlimit_obj_ops,
179	.maxattr	= NFTA_CONNLIMIT_MAX,
180	.policy		= nft_connlimit_policy,
181	.owner		= THIS_MODULE,
182};
183
184static void nft_connlimit_eval(const struct nft_expr *expr,
185			       struct nft_regs *regs,
186			       const struct nft_pktinfo *pkt)
187{
188	struct nft_connlimit *priv = nft_expr_priv(expr);
189
190	nft_connlimit_do_eval(priv, regs, pkt, NULL);
191}
192
193static int nft_connlimit_dump(struct sk_buff *skb,
194			      const struct nft_expr *expr, bool reset)
195{
196	struct nft_connlimit *priv = nft_expr_priv(expr);
197
198	return nft_connlimit_do_dump(skb, priv);
199}
200
201static int nft_connlimit_init(const struct nft_ctx *ctx,
202			      const struct nft_expr *expr,
203			      const struct nlattr * const tb[])
204{
205	struct nft_connlimit *priv = nft_expr_priv(expr);
206
207	return nft_connlimit_do_init(ctx, tb, priv);
208}
209
210static void nft_connlimit_destroy(const struct nft_ctx *ctx,
211				const struct nft_expr *expr)
212{
213	struct nft_connlimit *priv = nft_expr_priv(expr);
214
215	nft_connlimit_do_destroy(ctx, priv);
216}
217
218static int nft_connlimit_clone(struct nft_expr *dst, const struct nft_expr *src, gfp_t gfp)
219{
220	struct nft_connlimit *priv_dst = nft_expr_priv(dst);
221	struct nft_connlimit *priv_src = nft_expr_priv(src);
222
223	priv_dst->list = kmalloc(sizeof(*priv_dst->list), gfp);
224	if (!priv_dst->list)
225		return -ENOMEM;
226
227	nf_conncount_list_init(priv_dst->list);
228	priv_dst->limit	 = priv_src->limit;
229	priv_dst->invert = priv_src->invert;
230
231	return 0;
232}
233
234static void nft_connlimit_destroy_clone(const struct nft_ctx *ctx,
235					const struct nft_expr *expr)
236{
237	struct nft_connlimit *priv = nft_expr_priv(expr);
238
239	nf_conncount_cache_free(priv->list);
240	kfree(priv->list);
241}
242
243static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr)
244{
245	struct nft_connlimit *priv = nft_expr_priv(expr);
246
247	return nf_conncount_gc_list(net, priv->list);
248}
249
250static struct nft_expr_type nft_connlimit_type;
251static const struct nft_expr_ops nft_connlimit_ops = {
252	.type		= &nft_connlimit_type,
253	.size		= NFT_EXPR_SIZE(sizeof(struct nft_connlimit)),
254	.eval		= nft_connlimit_eval,
255	.init		= nft_connlimit_init,
256	.destroy	= nft_connlimit_destroy,
257	.clone		= nft_connlimit_clone,
258	.destroy_clone	= nft_connlimit_destroy_clone,
259	.dump		= nft_connlimit_dump,
260	.gc		= nft_connlimit_gc,
261	.reduce		= NFT_REDUCE_READONLY,
262};
263
264static struct nft_expr_type nft_connlimit_type __read_mostly = {
265	.name		= "connlimit",
266	.ops		= &nft_connlimit_ops,
267	.policy		= nft_connlimit_policy,
268	.maxattr	= NFTA_CONNLIMIT_MAX,
269	.flags		= NFT_EXPR_STATEFUL | NFT_EXPR_GC,
270	.owner		= THIS_MODULE,
271};
272
273static int __init nft_connlimit_module_init(void)
274{
275	int err;
276
277	err = nft_register_obj(&nft_connlimit_obj_type);
278	if (err < 0)
279		return err;
280
281	err = nft_register_expr(&nft_connlimit_type);
282	if (err < 0)
283		goto err1;
284
285	return 0;
286err1:
287	nft_unregister_obj(&nft_connlimit_obj_type);
288	return err;
289}
290
291static void __exit nft_connlimit_module_exit(void)
292{
293	nft_unregister_expr(&nft_connlimit_type);
294	nft_unregister_obj(&nft_connlimit_obj_type);
295}
296
297module_init(nft_connlimit_module_init);
298module_exit(nft_connlimit_module_exit);
299
300MODULE_LICENSE("GPL");
301MODULE_AUTHOR("Pablo Neira Ayuso");
302MODULE_ALIAS_NFT_EXPR("connlimit");
303MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CONNLIMIT);
304MODULE_DESCRIPTION("nftables connlimit rule support");
~/ Read-only file preview.