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