Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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	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
 59static 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	nf_conncount_list_init(&priv->list);
 80	priv->limit	= limit;
 81	priv->invert	= invert;
 82
 83	return nf_ct_netns_get(ctx->net, ctx->family);
 84}
 85
 86static void nft_connlimit_do_destroy(const struct nft_ctx *ctx,
 87				     struct nft_connlimit *priv)
 88{
 89	nf_ct_netns_put(ctx->net, ctx->family);
 90	nf_conncount_cache_free(&priv->list);
 91}
 92
 93static int nft_connlimit_do_dump(struct sk_buff *skb,
 94				 struct nft_connlimit *priv)
 95{
 96	if (nla_put_be32(skb, NFTA_CONNLIMIT_COUNT, htonl(priv->limit)))
 97		goto nla_put_failure;
 98	if (priv->invert &&
 99	    nla_put_be32(skb, NFTA_CONNLIMIT_FLAGS, htonl(NFT_CONNLIMIT_F_INV)))
100		goto nla_put_failure;
101
102	return 0;
103
104nla_put_failure:
105	return -1;
106}
107
108static inline void nft_connlimit_obj_eval(struct nft_object *obj,
109					struct nft_regs *regs,
110					const struct nft_pktinfo *pkt)
111{
112	struct nft_connlimit *priv = nft_obj_data(obj);
113
114	nft_connlimit_do_eval(priv, regs, pkt, NULL);
115}
116
117static int nft_connlimit_obj_init(const struct nft_ctx *ctx,
118				const struct nlattr * const tb[],
119				struct nft_object *obj)
120{
121	struct nft_connlimit *priv = nft_obj_data(obj);
122
123	return nft_connlimit_do_init(ctx, tb, priv);
124}
125
126static void nft_connlimit_obj_destroy(const struct nft_ctx *ctx,
127				      struct nft_object *obj)
128{
129	struct nft_connlimit *priv = nft_obj_data(obj);
130
131	nft_connlimit_do_destroy(ctx, priv);
132}
133
134static int nft_connlimit_obj_dump(struct sk_buff *skb,
135				  struct nft_object *obj, bool reset)
136{
137	struct nft_connlimit *priv = nft_obj_data(obj);
138
139	return nft_connlimit_do_dump(skb, priv);
140}
141
142static const struct nla_policy nft_connlimit_policy[NFTA_CONNLIMIT_MAX + 1] = {
143	[NFTA_CONNLIMIT_COUNT]	= { .type = NLA_U32 },
144	[NFTA_CONNLIMIT_FLAGS]	= { .type = NLA_U32 },
145};
146
147static struct nft_object_type nft_connlimit_obj_type;
148static const struct nft_object_ops nft_connlimit_obj_ops = {
149	.type		= &nft_connlimit_obj_type,
150	.size		= sizeof(struct nft_connlimit),
151	.eval		= nft_connlimit_obj_eval,
152	.init		= nft_connlimit_obj_init,
153	.destroy	= nft_connlimit_obj_destroy,
154	.dump		= nft_connlimit_obj_dump,
155};
156
157static struct nft_object_type nft_connlimit_obj_type __read_mostly = {
158	.type		= NFT_OBJECT_CONNLIMIT,
159	.ops		= &nft_connlimit_obj_ops,
160	.maxattr	= NFTA_CONNLIMIT_MAX,
161	.policy		= nft_connlimit_policy,
162	.owner		= THIS_MODULE,
163};
164
165static void nft_connlimit_eval(const struct nft_expr *expr,
166			       struct nft_regs *regs,
167			       const struct nft_pktinfo *pkt)
168{
169	struct nft_connlimit *priv = nft_expr_priv(expr);
170
171	nft_connlimit_do_eval(priv, regs, pkt, NULL);
172}
173
174static int nft_connlimit_dump(struct sk_buff *skb, const struct nft_expr *expr)
175{
176	struct nft_connlimit *priv = nft_expr_priv(expr);
177
178	return nft_connlimit_do_dump(skb, priv);
179}
180
181static int nft_connlimit_init(const struct nft_ctx *ctx,
182			      const struct nft_expr *expr,
183			      const struct nlattr * const tb[])
184{
185	struct nft_connlimit *priv = nft_expr_priv(expr);
186
187	return nft_connlimit_do_init(ctx, tb, priv);
188}
189
190static void nft_connlimit_destroy(const struct nft_ctx *ctx,
191				const struct nft_expr *expr)
192{
193	struct nft_connlimit *priv = nft_expr_priv(expr);
194
195	nft_connlimit_do_destroy(ctx, priv);
196}
197
198static int nft_connlimit_clone(struct nft_expr *dst, const struct nft_expr *src)
199{
200	struct nft_connlimit *priv_dst = nft_expr_priv(dst);
201	struct nft_connlimit *priv_src = nft_expr_priv(src);
202
203	nf_conncount_list_init(&priv_dst->list);
204	priv_dst->limit	 = priv_src->limit;
205	priv_dst->invert = priv_src->invert;
206
207	return 0;
208}
209
210static void nft_connlimit_destroy_clone(const struct nft_ctx *ctx,
211					const struct nft_expr *expr)
212{
213	struct nft_connlimit *priv = nft_expr_priv(expr);
214
215	nf_conncount_cache_free(&priv->list);
216}
217
218static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr)
219{
220	struct nft_connlimit *priv = nft_expr_priv(expr);
221	bool ret;
222
223	local_bh_disable();
224	ret = nf_conncount_gc_list(net, &priv->list);
225	local_bh_enable();
226
227	return ret;
228}
229
230static struct nft_expr_type nft_connlimit_type;
231static const struct nft_expr_ops nft_connlimit_ops = {
232	.type		= &nft_connlimit_type,
233	.size		= NFT_EXPR_SIZE(sizeof(struct nft_connlimit)),
234	.eval		= nft_connlimit_eval,
235	.init		= nft_connlimit_init,
236	.destroy	= nft_connlimit_destroy,
237	.clone		= nft_connlimit_clone,
238	.destroy_clone	= nft_connlimit_destroy_clone,
239	.dump		= nft_connlimit_dump,
240	.gc		= nft_connlimit_gc,
241};
242
243static struct nft_expr_type nft_connlimit_type __read_mostly = {
244	.name		= "connlimit",
245	.ops		= &nft_connlimit_ops,
246	.policy		= nft_connlimit_policy,
247	.maxattr	= NFTA_CONNLIMIT_MAX,
248	.flags		= NFT_EXPR_STATEFUL | NFT_EXPR_GC,
249	.owner		= THIS_MODULE,
250};
251
252static int __init nft_connlimit_module_init(void)
253{
254	int err;
255
256	err = nft_register_obj(&nft_connlimit_obj_type);
257	if (err < 0)
258		return err;
259
260	err = nft_register_expr(&nft_connlimit_type);
261	if (err < 0)
262		goto err1;
263
264	return 0;
265err1:
266	nft_unregister_obj(&nft_connlimit_obj_type);
267	return err;
268}
269
270static void __exit nft_connlimit_module_exit(void)
271{
272	nft_unregister_expr(&nft_connlimit_type);
273	nft_unregister_obj(&nft_connlimit_obj_type);
274}
275
276module_init(nft_connlimit_module_init);
277module_exit(nft_connlimit_module_exit);
278
279MODULE_LICENSE("GPL");
280MODULE_AUTHOR("Pablo Neira Ayuso");
281MODULE_ALIAS_NFT_EXPR("connlimit");
282MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CONNLIMIT);
283MODULE_DESCRIPTION("nftables connlimit rule support");