Blame view

net/netfilter/xt_connlimit.c 12.5 KB
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
1
2
3
4
5
6
  /*
   * netfilter module to limit the number of parallel tcp
   * connections per IP address.
   *   (c) 2000 Gerd Knorr <kraxel@bytesex.org>
   *   Nov 2002: Martin Bene <martin.bene@icomedias.com>:
   *		only ignore TIME_WAIT or gone connections
ba5dc2756   Jan Engelhardt   [NETFILTER]: Copy...
7
   *   (C) CC Computer Consultants GmbH, 2007
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
8
9
10
11
12
13
   *
   * based on ...
   *
   * Kernel module to match connection tracking information.
   * GPL (C) 1999  Rusty Russell (rusty@rustcorp.com.au).
   */
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
14
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
15
16
17
18
19
  #include <linux/in.h>
  #include <linux/in6.h>
  #include <linux/ip.h>
  #include <linux/ipv6.h>
  #include <linux/jhash.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
20
  #include <linux/slab.h>
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
21
  #include <linux/list.h>
7d0848777   Florian Westphal   netfilter: connli...
22
  #include <linux/rbtree.h>
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
23
24
25
26
27
28
29
30
31
32
  #include <linux/module.h>
  #include <linux/random.h>
  #include <linux/skbuff.h>
  #include <linux/spinlock.h>
  #include <linux/netfilter/nf_conntrack_tcp.h>
  #include <linux/netfilter/x_tables.h>
  #include <linux/netfilter/xt_connlimit.h>
  #include <net/netfilter/nf_conntrack.h>
  #include <net/netfilter/nf_conntrack_core.h>
  #include <net/netfilter/nf_conntrack_tuple.h>
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
33
  #include <net/netfilter/nf_conntrack_zones.h>
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
34

e00b437b3   Florian Westphal   netfilter: connli...
35
36
37
38
39
40
41
  #define CONNLIMIT_SLOTS		256U
  
  #ifdef CONFIG_LOCKDEP
  #define CONNLIMIT_LOCK_SLOTS	8U
  #else
  #define CONNLIMIT_LOCK_SLOTS	256U
  #endif
7d0848777   Florian Westphal   netfilter: connli...
42
  #define CONNLIMIT_GC_MAX_NODES	8
1442e7507   Florian Westphal   netfilter: connli...
43

370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
44
45
  /* we will save the tuples of all connections we care about */
  struct xt_connlimit_conn {
3e0d5149e   Changli Gao   netfilter: xt_con...
46
  	struct hlist_node		node;
8183e3a88   Changli Gao   netfilter: xt_con...
47
48
  	struct nf_conntrack_tuple	tuple;
  	union nf_inet_addr		addr;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
49
  };
7d0848777   Florian Westphal   netfilter: connli...
50
51
52
53
54
  struct xt_connlimit_rb {
  	struct rb_node node;
  	struct hlist_head hhead; /* connections/hosts in same subnet */
  	union nf_inet_addr addr; /* search key */
  };
e00b437b3   Florian Westphal   netfilter: connli...
55
  static spinlock_t xt_connlimit_locks[CONNLIMIT_LOCK_SLOTS] __cacheline_aligned_in_smp;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
56
  struct xt_connlimit_data {
7d0848777   Florian Westphal   netfilter: connli...
57
58
  	struct rb_root climit_root4[CONNLIMIT_SLOTS];
  	struct rb_root climit_root6[CONNLIMIT_SLOTS];
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
59
  };
294188ae3   Jan Engelhardt   netfilter: xtable...
60
  static u_int32_t connlimit_rnd __read_mostly;
7d0848777   Florian Westphal   netfilter: connli...
61
  static struct kmem_cache *connlimit_rb_cachep __read_mostly;
14e1a9777   Florian Westphal   netfilter: connli...
62
  static struct kmem_cache *connlimit_conn_cachep __read_mostly;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
63

a34c45896   Al Viro   netfilter endian ...
64
  static inline unsigned int connlimit_iphash(__be32 addr)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
65
  {
1442e7507   Florian Westphal   netfilter: connli...
66
67
  	return jhash_1word((__force __u32)addr,
  			    connlimit_rnd) % CONNLIMIT_SLOTS;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
68
69
70
  }
  
  static inline unsigned int
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
71
72
  connlimit_iphash6(const union nf_inet_addr *addr,
                    const union nf_inet_addr *mask)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
73
  {
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
74
  	union nf_inet_addr res;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
75
  	unsigned int i;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
76
77
  	for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
  		res.ip6[i] = addr->ip6[i] & mask->ip6[i];
1442e7507   Florian Westphal   netfilter: connli...
78
79
  	return jhash2((u32 *)res.ip6, ARRAY_SIZE(res.ip6),
  		       connlimit_rnd) % CONNLIMIT_SLOTS;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
80
81
82
83
  }
  
  static inline bool already_closed(const struct nf_conn *conn)
  {
5e8fbe2ac   Patrick McHardy   [NETFILTER]: nf_c...
84
  	if (nf_ct_protonum(conn) == IPPROTO_TCP)
d2ee3f2c4   Dong Wei   netfilter: xt_con...
85
86
  		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT ||
  		       conn->proto.tcp.state == TCP_CONNTRACK_CLOSE;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
87
88
89
  	else
  		return 0;
  }
50e0e9b12   Florian Westphal   netfilter: connli...
90
  static int
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
91
92
  same_source_net(const union nf_inet_addr *addr,
  		const union nf_inet_addr *mask,
76108cea0   Jan Engelhardt   netfilter: Use un...
93
  		const union nf_inet_addr *u3, u_int8_t family)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
94
  {
ee999d8b9   Jan Engelhardt   netfilter: x_tabl...
95
  	if (family == NFPROTO_IPV4) {
50e0e9b12   Florian Westphal   netfilter: connli...
96
97
  		return ntohl(addr->ip & mask->ip) -
  		       ntohl(u3->ip & mask->ip);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
98
  	} else {
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
99
  		union nf_inet_addr lh, rh;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
100
101
102
103
104
105
  		unsigned int i;
  
  		for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) {
  			lh.ip6[i] = addr->ip6[i] & mask->ip6[i];
  			rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
  		}
50e0e9b12   Florian Westphal   netfilter: connli...
106
  		return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6));
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
107
108
  	}
  }
7d0848777   Florian Westphal   netfilter: connli...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  static bool add_hlist(struct hlist_head *head,
  		      const struct nf_conntrack_tuple *tuple,
  		      const union nf_inet_addr *addr)
  {
  	struct xt_connlimit_conn *conn;
  
  	conn = kmem_cache_alloc(connlimit_conn_cachep, GFP_ATOMIC);
  	if (conn == NULL)
  		return false;
  	conn->tuple = *tuple;
  	conn->addr = *addr;
  	hlist_add_head(&conn->node, head);
  	return true;
  }
  
  static unsigned int check_hlist(struct net *net,
  				struct hlist_head *head,
  				const struct nf_conntrack_tuple *tuple,
308ac9143   Daniel Borkmann   netfilter: nf_con...
127
  				const struct nf_conntrack_zone *zone,
7d0848777   Florian Westphal   netfilter: connli...
128
  				bool *addit)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
129
  {
3cf93c96a   Jan Engelhardt   [NETFILTER]: anno...
130
  	const struct nf_conntrack_tuple_hash *found;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
131
  	struct xt_connlimit_conn *conn;
b67bfe0d4   Sasha Levin   hlist: drop the n...
132
  	struct hlist_node *n;
ea781f197   Eric Dumazet   netfilter: nf_con...
133
  	struct nf_conn *found_ct;
7d0848777   Florian Westphal   netfilter: connli...
134
  	unsigned int length = 0;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
135

7d0848777   Florian Westphal   netfilter: connli...
136
  	*addit = true;
76507f69c   Patrick McHardy   [NETFILTER]: nf_c...
137
  	rcu_read_lock();
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
138
139
  
  	/* check the saved connections */
15cfd5289   Florian Westphal   netfilter: connli...
140
  	hlist_for_each_entry_safe(conn, n, head, node) {
e59ea3df3   Florian Westphal   netfilter: xt_con...
141
  		found = nf_conntrack_find_get(net, zone, &conn->tuple);
d9ec4f1ee   Florian Westphal   netfilter: connli...
142
143
  		if (found == NULL) {
  			hlist_del(&conn->node);
14e1a9777   Florian Westphal   netfilter: connli...
144
  			kmem_cache_free(connlimit_conn_cachep, conn);
d9ec4f1ee   Florian Westphal   netfilter: connli...
145
146
  			continue;
  		}
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
147

d9ec4f1ee   Florian Westphal   netfilter: connli...
148
  		found_ct = nf_ct_tuplehash_to_ctrack(found);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
149

d9ec4f1ee   Florian Westphal   netfilter: connli...
150
  		if (nf_ct_tuple_equal(&conn->tuple, tuple)) {
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
151
152
153
154
155
  			/*
  			 * Just to be sure we have it only once in the list.
  			 * We should not see tuples twice unless someone hooks
  			 * this into a table without "-p tcp --syn".
  			 */
3bcc5fdf1   Florian Westphal   netfilter: connli...
156
  			*addit = false;
d9ec4f1ee   Florian Westphal   netfilter: connli...
157
  		} else if (already_closed(found_ct)) {
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
158
159
160
161
  			/*
  			 * we do not care about connections which are
  			 * closed already -> ditch it
  			 */
ea781f197   Eric Dumazet   netfilter: nf_con...
162
  			nf_ct_put(found_ct);
3e0d5149e   Changli Gao   netfilter: xt_con...
163
  			hlist_del(&conn->node);
14e1a9777   Florian Westphal   netfilter: connli...
164
  			kmem_cache_free(connlimit_conn_cachep, conn);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
165
166
  			continue;
  		}
ea781f197   Eric Dumazet   netfilter: nf_con...
167
  		nf_ct_put(found_ct);
7d0848777   Florian Westphal   netfilter: connli...
168
  		length++;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
169
  	}
76507f69c   Patrick McHardy   [NETFILTER]: nf_c...
170
  	rcu_read_unlock();
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
171

7d0848777   Florian Westphal   netfilter: connli...
172
  	return length;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
173
  }
7d0848777   Florian Westphal   netfilter: connli...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  static void tree_nodes_free(struct rb_root *root,
  			    struct xt_connlimit_rb *gc_nodes[],
  			    unsigned int gc_count)
  {
  	struct xt_connlimit_rb *rbconn;
  
  	while (gc_count) {
  		rbconn = gc_nodes[--gc_count];
  		rb_erase(&rbconn->node, root);
  		kmem_cache_free(connlimit_rb_cachep, rbconn);
  	}
  }
  
  static unsigned int
  count_tree(struct net *net, struct rb_root *root,
  	   const struct nf_conntrack_tuple *tuple,
  	   const union nf_inet_addr *addr, const union nf_inet_addr *mask,
308ac9143   Daniel Borkmann   netfilter: nf_con...
191
  	   u8 family, const struct nf_conntrack_zone *zone)
3bcc5fdf1   Florian Westphal   netfilter: connli...
192
  {
7d0848777   Florian Westphal   netfilter: connli...
193
194
195
  	struct xt_connlimit_rb *gc_nodes[CONNLIMIT_GC_MAX_NODES];
  	struct rb_node **rbnode, *parent;
  	struct xt_connlimit_rb *rbconn;
14e1a9777   Florian Westphal   netfilter: connli...
196
  	struct xt_connlimit_conn *conn;
7d0848777   Florian Westphal   netfilter: connli...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  	unsigned int gc_count;
  	bool no_gc = false;
  
   restart:
  	gc_count = 0;
  	parent = NULL;
  	rbnode = &(root->rb_node);
  	while (*rbnode) {
  		int diff;
  		bool addit;
  
  		rbconn = container_of(*rbnode, struct xt_connlimit_rb, node);
  
  		parent = *rbnode;
  		diff = same_source_net(addr, mask, &rbconn->addr, family);
  		if (diff < 0) {
  			rbnode = &((*rbnode)->rb_left);
  		} else if (diff > 0) {
  			rbnode = &((*rbnode)->rb_right);
  		} else {
  			/* same source network -> be counted! */
  			unsigned int count;
e59ea3df3   Florian Westphal   netfilter: xt_con...
219
  			count = check_hlist(net, &rbconn->hhead, tuple, zone, &addit);
7d0848777   Florian Westphal   netfilter: connli...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  
  			tree_nodes_free(root, gc_nodes, gc_count);
  			if (!addit)
  				return count;
  
  			if (!add_hlist(&rbconn->hhead, tuple, addr))
  				return 0; /* hotdrop */
  
  			return count + 1;
  		}
  
  		if (no_gc || gc_count >= ARRAY_SIZE(gc_nodes))
  			continue;
  
  		/* only used for GC on hhead, retval and 'addit' ignored */
e59ea3df3   Florian Westphal   netfilter: xt_con...
235
  		check_hlist(net, &rbconn->hhead, tuple, zone, &addit);
7d0848777   Florian Westphal   netfilter: connli...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
  		if (hlist_empty(&rbconn->hhead))
  			gc_nodes[gc_count++] = rbconn;
  	}
  
  	if (gc_count) {
  		no_gc = true;
  		tree_nodes_free(root, gc_nodes, gc_count);
  		/* tree_node_free before new allocation permits
  		 * allocator to re-use newly free'd object.
  		 *
  		 * This is a rare event; in most cases we will find
  		 * existing node to re-use. (or gc_count is 0).
  		 */
  		goto restart;
  	}
  
  	/* no match, need to insert new node */
  	rbconn = kmem_cache_alloc(connlimit_rb_cachep, GFP_ATOMIC);
  	if (rbconn == NULL)
  		return 0;
14e1a9777   Florian Westphal   netfilter: connli...
256
257
  
  	conn = kmem_cache_alloc(connlimit_conn_cachep, GFP_ATOMIC);
7d0848777   Florian Westphal   netfilter: connli...
258
259
260
261
  	if (conn == NULL) {
  		kmem_cache_free(connlimit_rb_cachep, rbconn);
  		return 0;
  	}
3bcc5fdf1   Florian Westphal   netfilter: connli...
262
263
  	conn->tuple = *tuple;
  	conn->addr = *addr;
7d0848777   Florian Westphal   netfilter: connli...
264
265
266
267
268
269
270
271
  	rbconn->addr = *addr;
  
  	INIT_HLIST_HEAD(&rbconn->hhead);
  	hlist_add_head(&conn->node, &rbconn->hhead);
  
  	rb_link_node(&rbconn->node, parent, rbnode);
  	rb_insert_color(&rbconn->node, root);
  	return 1;
3bcc5fdf1   Florian Westphal   netfilter: connli...
272
  }
15cfd5289   Florian Westphal   netfilter: connli...
273
274
275
276
277
  static int count_them(struct net *net,
  		      struct xt_connlimit_data *data,
  		      const struct nf_conntrack_tuple *tuple,
  		      const union nf_inet_addr *addr,
  		      const union nf_inet_addr *mask,
308ac9143   Daniel Borkmann   netfilter: nf_con...
278
279
  		      u_int8_t family,
  		      const struct nf_conntrack_zone *zone)
15cfd5289   Florian Westphal   netfilter: connli...
280
  {
7d0848777   Florian Westphal   netfilter: connli...
281
  	struct rb_root *root;
15cfd5289   Florian Westphal   netfilter: connli...
282
283
  	int count;
  	u32 hash;
7d0848777   Florian Westphal   netfilter: connli...
284
  	if (family == NFPROTO_IPV6) {
15cfd5289   Florian Westphal   netfilter: connli...
285
  		hash = connlimit_iphash6(addr, mask);
7d0848777   Florian Westphal   netfilter: connli...
286
287
  		root = &data->climit_root6[hash];
  	} else {
15cfd5289   Florian Westphal   netfilter: connli...
288
  		hash = connlimit_iphash(addr->ip & mask->ip);
7d0848777   Florian Westphal   netfilter: connli...
289
290
  		root = &data->climit_root4[hash];
  	}
15cfd5289   Florian Westphal   netfilter: connli...
291

e00b437b3   Florian Westphal   netfilter: connli...
292
  	spin_lock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]);
7d0848777   Florian Westphal   netfilter: connli...
293

e59ea3df3   Florian Westphal   netfilter: xt_con...
294
  	count = count_tree(net, root, tuple, addr, mask, family, zone);
7d0848777   Florian Westphal   netfilter: connli...
295

e00b437b3   Florian Westphal   netfilter: connli...
296
  	spin_unlock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]);
15cfd5289   Florian Westphal   netfilter: connli...
297
298
299
  
  	return count;
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
300
  static bool
62fc80510   Jan Engelhardt   netfilter: xtable...
301
  connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
302
  {
686c9b508   Eric W. Biederman   netfilter: x_tabl...
303
  	struct net *net = par->net;
f7108a20d   Jan Engelhardt   netfilter: xtable...
304
  	const struct xt_connlimit_info *info = par->matchinfo;
22c2d8bca   Jan Engelhardt   [NETFILTER]: xt_c...
305
  	union nf_inet_addr addr;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
306
307
  	struct nf_conntrack_tuple tuple;
  	const struct nf_conntrack_tuple *tuple_ptr = &tuple;
308ac9143   Daniel Borkmann   netfilter: nf_con...
308
  	const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
309
310
  	enum ip_conntrack_info ctinfo;
  	const struct nf_conn *ct;
7d0848777   Florian Westphal   netfilter: connli...
311
  	unsigned int connections;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
312
313
  
  	ct = nf_ct_get(skb, &ctinfo);
e59ea3df3   Florian Westphal   netfilter: xt_con...
314
  	if (ct != NULL) {
8183e3a88   Changli Gao   netfilter: xt_con...
315
  		tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
e59ea3df3   Florian Westphal   netfilter: xt_con...
316
317
  		zone = nf_ct_zone(ct);
  	} else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
a31f1adc0   Eric W. Biederman   netfilter: nf_con...
318
  				      par->family, net, &tuple)) {
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
319
  		goto hotdrop;
e59ea3df3   Florian Westphal   netfilter: xt_con...
320
  	}
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
321

92f3b2b1b   Jan Engelhardt   netfilter: xtable...
322
  	if (par->family == NFPROTO_IPV6) {
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
323
  		const struct ipv6hdr *iph = ipv6_hdr(skb);
cc4fc0225   Jan Engelhardt   netfilter: xtable...
324
325
  		memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ?
  		       &iph->daddr : &iph->saddr, sizeof(addr.ip6));
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
326
327
  	} else {
  		const struct iphdr *iph = ip_hdr(skb);
cc4fc0225   Jan Engelhardt   netfilter: xtable...
328
329
  		addr.ip = (info->flags & XT_CONNLIMIT_DADDR) ?
  			  iph->daddr : iph->saddr;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
330
  	}
83fc81024   Alexey Dobriyan   netfilter: xt_con...
331
  	connections = count_them(net, info->data, tuple_ptr, &addr,
e59ea3df3   Florian Westphal   netfilter: xt_con...
332
  	                         &info->mask, par->family, zone);
7d0848777   Florian Westphal   netfilter: connli...
333
  	if (connections == 0)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
334
  		/* kmalloc failed, drop it entirely */
1cc34c30b   Richard Weinberger   netfilter: xt_con...
335
  		goto hotdrop;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
336

cc4fc0225   Jan Engelhardt   netfilter: xtable...
337
338
  	return (connections > info->limit) ^
  	       !!(info->flags & XT_CONNLIMIT_INVERT);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
339
340
  
   hotdrop:
b4ba26119   Jan Engelhardt   netfilter: xtable...
341
  	par->hotdrop = true;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
342
343
  	return false;
  }
b0f38452f   Jan Engelhardt   netfilter: xtable...
344
  static int connlimit_mt_check(const struct xt_mtchk_param *par)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
345
  {
9b4fce7a3   Jan Engelhardt   netfilter: xtable...
346
  	struct xt_connlimit_info *info = par->matchinfo;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
347
  	unsigned int i;
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
348
  	int ret;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
349

4656c4d61   Changli Gao   netfilter: xt_con...
350
351
352
353
354
355
356
  	if (unlikely(!connlimit_rnd)) {
  		u_int32_t rand;
  
  		do {
  			get_random_bytes(&rand, sizeof(rand));
  		} while (!rand);
  		cmpxchg(&connlimit_rnd, 0, rand);
294188ae3   Jan Engelhardt   netfilter: xtable...
357
  	}
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
358
359
  	ret = nf_ct_l3proto_try_module_get(par->family);
  	if (ret < 0) {
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
360
361
362
  		pr_info("cannot load conntrack support for "
  			"address family %u
  ", par->family);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
363
  		return ret;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
364
365
366
367
368
  	}
  
  	/* init private data */
  	info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
  	if (info->data == NULL) {
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
369
  		nf_ct_l3proto_module_put(par->family);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
370
  		return -ENOMEM;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
371
  	}
7d0848777   Florian Westphal   netfilter: connli...
372
373
374
375
  	for (i = 0; i < ARRAY_SIZE(info->data->climit_root4); ++i)
  		info->data->climit_root4[i] = RB_ROOT;
  	for (i = 0; i < ARRAY_SIZE(info->data->climit_root6); ++i)
  		info->data->climit_root6[i] = RB_ROOT;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
376

bd414ee60   Jan Engelhardt   netfilter: xtable...
377
  	return 0;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
378
  }
7d0848777   Florian Westphal   netfilter: connli...
379
  static void destroy_tree(struct rb_root *r)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
380
  {
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
381
  	struct xt_connlimit_conn *conn;
7d0848777   Florian Westphal   netfilter: connli...
382
  	struct xt_connlimit_rb *rbconn;
b67bfe0d4   Sasha Levin   hlist: drop the n...
383
  	struct hlist_node *n;
7d0848777   Florian Westphal   netfilter: connli...
384
  	struct rb_node *node;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
385

7d0848777   Florian Westphal   netfilter: connli...
386
387
  	while ((node = rb_first(r)) != NULL) {
  		rbconn = container_of(node, struct xt_connlimit_rb, node);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
388

7d0848777   Florian Westphal   netfilter: connli...
389
390
391
  		rb_erase(node, r);
  
  		hlist_for_each_entry_safe(conn, n, &rbconn->hhead, node)
14e1a9777   Florian Westphal   netfilter: connli...
392
  			kmem_cache_free(connlimit_conn_cachep, conn);
7d0848777   Florian Westphal   netfilter: connli...
393
394
  
  		kmem_cache_free(connlimit_rb_cachep, rbconn);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
395
  	}
7d0848777   Florian Westphal   netfilter: connli...
396
397
398
399
400
401
402
403
404
405
406
407
408
  }
  
  static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
  {
  	const struct xt_connlimit_info *info = par->matchinfo;
  	unsigned int i;
  
  	nf_ct_l3proto_module_put(par->family);
  
  	for (i = 0; i < ARRAY_SIZE(info->data->climit_root4); ++i)
  		destroy_tree(&info->data->climit_root4[i]);
  	for (i = 0; i < ARRAY_SIZE(info->data->climit_root6); ++i)
  		destroy_tree(&info->data->climit_root6[i]);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
409
410
411
  
  	kfree(info->data);
  }
68c07cb6d   Cong Wang   netfilter: xt_con...
412
413
414
415
416
417
418
419
420
  static struct xt_match connlimit_mt_reg __read_mostly = {
  	.name       = "connlimit",
  	.revision   = 1,
  	.family     = NFPROTO_UNSPEC,
  	.checkentry = connlimit_mt_check,
  	.match      = connlimit_mt,
  	.matchsize  = sizeof(struct xt_connlimit_info),
  	.destroy    = connlimit_mt_destroy,
  	.me         = THIS_MODULE,
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
421
  };
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
422
  static int __init connlimit_mt_init(void)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
423
  {
e00b437b3   Florian Westphal   netfilter: connli...
424
  	int ret, i;
1442e7507   Florian Westphal   netfilter: connli...
425
426
427
  
  	BUILD_BUG_ON(CONNLIMIT_LOCK_SLOTS > CONNLIMIT_SLOTS);
  	BUILD_BUG_ON((CONNLIMIT_SLOTS % CONNLIMIT_LOCK_SLOTS) != 0);
e00b437b3   Florian Westphal   netfilter: connli...
428
429
  	for (i = 0; i < CONNLIMIT_LOCK_SLOTS; ++i)
  		spin_lock_init(&xt_connlimit_locks[i]);
14e1a9777   Florian Westphal   netfilter: connli...
430
431
432
433
434
  	connlimit_conn_cachep = kmem_cache_create("xt_connlimit_conn",
  					   sizeof(struct xt_connlimit_conn),
  					   0, 0, NULL);
  	if (!connlimit_conn_cachep)
  		return -ENOMEM;
7d0848777   Florian Westphal   netfilter: connli...
435
436
437
438
439
440
441
  	connlimit_rb_cachep = kmem_cache_create("xt_connlimit_rb",
  					   sizeof(struct xt_connlimit_rb),
  					   0, 0, NULL);
  	if (!connlimit_rb_cachep) {
  		kmem_cache_destroy(connlimit_conn_cachep);
  		return -ENOMEM;
  	}
14e1a9777   Florian Westphal   netfilter: connli...
442
  	ret = xt_register_match(&connlimit_mt_reg);
7d0848777   Florian Westphal   netfilter: connli...
443
  	if (ret != 0) {
14e1a9777   Florian Westphal   netfilter: connli...
444
  		kmem_cache_destroy(connlimit_conn_cachep);
7d0848777   Florian Westphal   netfilter: connli...
445
446
  		kmem_cache_destroy(connlimit_rb_cachep);
  	}
14e1a9777   Florian Westphal   netfilter: connli...
447
  	return ret;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
448
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
449
  static void __exit connlimit_mt_exit(void)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
450
  {
68c07cb6d   Cong Wang   netfilter: xt_con...
451
  	xt_unregister_match(&connlimit_mt_reg);
14e1a9777   Florian Westphal   netfilter: connli...
452
  	kmem_cache_destroy(connlimit_conn_cachep);
7d0848777   Florian Westphal   netfilter: connli...
453
  	kmem_cache_destroy(connlimit_rb_cachep);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
454
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
455
456
  module_init(connlimit_mt_init);
  module_exit(connlimit_mt_exit);
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
457
  MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
2ae15b64e   Jan Engelhardt   [NETFILTER]: Upda...
458
  MODULE_DESCRIPTION("Xtables: Number of connections matching");
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
459
460
461
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("ipt_connlimit");
  MODULE_ALIAS("ip6t_connlimit");