xref: /openbmc/linux/net/netfilter/nft_connlimit.c (revision ba61bb17)
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 
16 struct nft_connlimit {
17 	spinlock_t		lock;
18 	struct hlist_head	hhead;
19 	u32			limit;
20 	bool			invert;
21 };
22 
23 static inline void nft_connlimit_do_eval(struct nft_connlimit *priv,
24 					 struct nft_regs *regs,
25 					 const struct nft_pktinfo *pkt,
26 					 const struct nft_set_ext *ext)
27 {
28 	const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
29 	const struct nf_conntrack_tuple *tuple_ptr;
30 	struct nf_conntrack_tuple tuple;
31 	enum ip_conntrack_info ctinfo;
32 	const struct nf_conn *ct;
33 	unsigned int count;
34 	bool addit;
35 
36 	tuple_ptr = &tuple;
37 
38 	ct = nf_ct_get(pkt->skb, &ctinfo);
39 	if (ct != NULL) {
40 		tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
41 		zone = nf_ct_zone(ct);
42 	} else if (!nf_ct_get_tuplepr(pkt->skb, skb_network_offset(pkt->skb),
43 				      nft_pf(pkt), nft_net(pkt), &tuple)) {
44 		regs->verdict.code = NF_DROP;
45 		return;
46 	}
47 
48 	spin_lock_bh(&priv->lock);
49 	count = nf_conncount_lookup(nft_net(pkt), &priv->hhead, tuple_ptr, zone,
50 				    &addit);
51 
52 	if (!addit)
53 		goto out;
54 
55 	if (!nf_conncount_add(&priv->hhead, tuple_ptr, zone)) {
56 		regs->verdict.code = NF_DROP;
57 		spin_unlock_bh(&priv->lock);
58 		return;
59 	}
60 	count++;
61 out:
62 	spin_unlock_bh(&priv->lock);
63 
64 	if ((count > priv->limit) ^ priv->invert) {
65 		regs->verdict.code = NFT_BREAK;
66 		return;
67 	}
68 }
69 
70 static int nft_connlimit_do_init(const struct nft_ctx *ctx,
71 				 const struct nlattr * const tb[],
72 				 struct nft_connlimit *priv)
73 {
74 	bool invert = false;
75 	u32 flags, limit;
76 
77 	if (!tb[NFTA_CONNLIMIT_COUNT])
78 		return -EINVAL;
79 
80 	limit = ntohl(nla_get_be32(tb[NFTA_CONNLIMIT_COUNT]));
81 
82 	if (tb[NFTA_CONNLIMIT_FLAGS]) {
83 		flags = ntohl(nla_get_be32(tb[NFTA_CONNLIMIT_FLAGS]));
84 		if (flags & ~NFT_CONNLIMIT_F_INV)
85 			return -EOPNOTSUPP;
86 		if (flags & NFT_CONNLIMIT_F_INV)
87 			invert = true;
88 	}
89 
90 	spin_lock_init(&priv->lock);
91 	INIT_HLIST_HEAD(&priv->hhead);
92 	priv->limit	= limit;
93 	priv->invert	= invert;
94 
95 	return nf_ct_netns_get(ctx->net, ctx->family);
96 }
97 
98 static void nft_connlimit_do_destroy(const struct nft_ctx *ctx,
99 				     struct nft_connlimit *priv)
100 {
101 	nf_ct_netns_put(ctx->net, ctx->family);
102 	nf_conncount_cache_free(&priv->hhead);
103 }
104 
105 static int nft_connlimit_do_dump(struct sk_buff *skb,
106 				 struct nft_connlimit *priv)
107 {
108 	if (nla_put_be32(skb, NFTA_CONNLIMIT_COUNT, htonl(priv->limit)))
109 		goto nla_put_failure;
110 	if (priv->invert &&
111 	    nla_put_be32(skb, NFTA_CONNLIMIT_FLAGS, htonl(NFT_CONNLIMIT_F_INV)))
112 		goto nla_put_failure;
113 
114 	return 0;
115 
116 nla_put_failure:
117 	return -1;
118 }
119 
120 static inline void nft_connlimit_obj_eval(struct nft_object *obj,
121 					struct nft_regs *regs,
122 					const struct nft_pktinfo *pkt)
123 {
124 	struct nft_connlimit *priv = nft_obj_data(obj);
125 
126 	nft_connlimit_do_eval(priv, regs, pkt, NULL);
127 }
128 
129 static int nft_connlimit_obj_init(const struct nft_ctx *ctx,
130 				const struct nlattr * const tb[],
131 				struct nft_object *obj)
132 {
133 	struct nft_connlimit *priv = nft_obj_data(obj);
134 
135 	return nft_connlimit_do_init(ctx, tb, priv);
136 }
137 
138 static void nft_connlimit_obj_destroy(const struct nft_ctx *ctx,
139 				      struct nft_object *obj)
140 {
141 	struct nft_connlimit *priv = nft_obj_data(obj);
142 
143 	nft_connlimit_do_destroy(ctx, priv);
144 }
145 
146 static int nft_connlimit_obj_dump(struct sk_buff *skb,
147 				  struct nft_object *obj, bool reset)
148 {
149 	struct nft_connlimit *priv = nft_obj_data(obj);
150 
151 	return nft_connlimit_do_dump(skb, priv);
152 }
153 
154 static const struct nla_policy nft_connlimit_policy[NFTA_CONNLIMIT_MAX + 1] = {
155 	[NFTA_CONNLIMIT_COUNT]	= { .type = NLA_U32 },
156 	[NFTA_CONNLIMIT_FLAGS]	= { .type = NLA_U32 },
157 };
158 
159 static struct nft_object_type nft_connlimit_obj_type;
160 static const struct nft_object_ops nft_connlimit_obj_ops = {
161 	.type		= &nft_connlimit_obj_type,
162 	.size		= sizeof(struct nft_connlimit),
163 	.eval		= nft_connlimit_obj_eval,
164 	.init		= nft_connlimit_obj_init,
165 	.destroy	= nft_connlimit_obj_destroy,
166 	.dump		= nft_connlimit_obj_dump,
167 };
168 
169 static struct nft_object_type nft_connlimit_obj_type __read_mostly = {
170 	.type		= NFT_OBJECT_CONNLIMIT,
171 	.ops		= &nft_connlimit_obj_ops,
172 	.maxattr	= NFTA_CONNLIMIT_MAX,
173 	.policy		= nft_connlimit_policy,
174 	.owner		= THIS_MODULE,
175 };
176 
177 static void nft_connlimit_eval(const struct nft_expr *expr,
178 			       struct nft_regs *regs,
179 			       const struct nft_pktinfo *pkt)
180 {
181 	struct nft_connlimit *priv = nft_expr_priv(expr);
182 
183 	nft_connlimit_do_eval(priv, regs, pkt, NULL);
184 }
185 
186 static int nft_connlimit_dump(struct sk_buff *skb, const struct nft_expr *expr)
187 {
188 	struct nft_connlimit *priv = nft_expr_priv(expr);
189 
190 	return nft_connlimit_do_dump(skb, priv);
191 }
192 
193 static int nft_connlimit_init(const struct nft_ctx *ctx,
194 			      const struct nft_expr *expr,
195 			      const struct nlattr * const tb[])
196 {
197 	struct nft_connlimit *priv = nft_expr_priv(expr);
198 
199 	return nft_connlimit_do_init(ctx, tb, priv);
200 }
201 
202 static void nft_connlimit_destroy(const struct nft_ctx *ctx,
203 				const struct nft_expr *expr)
204 {
205 	struct nft_connlimit *priv = nft_expr_priv(expr);
206 
207 	nft_connlimit_do_destroy(ctx, priv);
208 }
209 
210 static int nft_connlimit_clone(struct nft_expr *dst, const struct nft_expr *src)
211 {
212 	struct nft_connlimit *priv_dst = nft_expr_priv(dst);
213 	struct nft_connlimit *priv_src = nft_expr_priv(src);
214 
215 	spin_lock_init(&priv_dst->lock);
216 	INIT_HLIST_HEAD(&priv_dst->hhead);
217 	priv_dst->limit	 = priv_src->limit;
218 	priv_dst->invert = priv_src->invert;
219 
220 	return 0;
221 }
222 
223 static void nft_connlimit_destroy_clone(const struct nft_ctx *ctx,
224 					const struct nft_expr *expr)
225 {
226 	struct nft_connlimit *priv = nft_expr_priv(expr);
227 
228 	nf_conncount_cache_free(&priv->hhead);
229 }
230 
231 static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr)
232 {
233 	struct nft_connlimit *priv = nft_expr_priv(expr);
234 	bool addit, ret;
235 
236 	spin_lock_bh(&priv->lock);
237 	nf_conncount_lookup(net, &priv->hhead, NULL, &nf_ct_zone_dflt, &addit);
238 
239 	ret = hlist_empty(&priv->hhead);
240 	spin_unlock_bh(&priv->lock);
241 
242 	return ret;
243 }
244 
245 static struct nft_expr_type nft_connlimit_type;
246 static const struct nft_expr_ops nft_connlimit_ops = {
247 	.type		= &nft_connlimit_type,
248 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_connlimit)),
249 	.eval		= nft_connlimit_eval,
250 	.init		= nft_connlimit_init,
251 	.destroy	= nft_connlimit_destroy,
252 	.clone		= nft_connlimit_clone,
253 	.destroy_clone	= nft_connlimit_destroy_clone,
254 	.dump		= nft_connlimit_dump,
255 	.gc		= nft_connlimit_gc,
256 };
257 
258 static struct nft_expr_type nft_connlimit_type __read_mostly = {
259 	.name		= "connlimit",
260 	.ops		= &nft_connlimit_ops,
261 	.policy		= nft_connlimit_policy,
262 	.maxattr	= NFTA_CONNLIMIT_MAX,
263 	.flags		= NFT_EXPR_STATEFUL | NFT_EXPR_GC,
264 	.owner		= THIS_MODULE,
265 };
266 
267 static int __init nft_connlimit_module_init(void)
268 {
269 	int err;
270 
271 	err = nft_register_obj(&nft_connlimit_obj_type);
272 	if (err < 0)
273 		return err;
274 
275 	err = nft_register_expr(&nft_connlimit_type);
276 	if (err < 0)
277 		goto err1;
278 
279 	return 0;
280 err1:
281 	nft_unregister_obj(&nft_connlimit_obj_type);
282 	return err;
283 }
284 
285 static void __exit nft_connlimit_module_exit(void)
286 {
287 	nft_unregister_expr(&nft_connlimit_type);
288 	nft_unregister_obj(&nft_connlimit_obj_type);
289 }
290 
291 module_init(nft_connlimit_module_init);
292 module_exit(nft_connlimit_module_exit);
293 
294 MODULE_LICENSE("GPL");
295 MODULE_AUTHOR("Pablo Neira Ayuso");
296 MODULE_ALIAS_NFT_EXPR("connlimit");
297 MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CONNLIMIT);
298