Blame view

net/netfilter/xt_connlimit.c 8.03 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
22
23
24
25
26
27
28
29
30
31
  #include <linux/list.h>
  #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...
32
  #include <net/netfilter/nf_conntrack_zones.h>
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
33
34
35
  
  /* we will save the tuples of all connections we care about */
  struct xt_connlimit_conn {
3e0d5149e   Changli Gao   netfilter: xt_con...
36
  	struct hlist_node		node;
8183e3a88   Changli Gao   netfilter: xt_con...
37
38
  	struct nf_conntrack_tuple	tuple;
  	union nf_inet_addr		addr;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
39
40
41
  };
  
  struct xt_connlimit_data {
3e0d5149e   Changli Gao   netfilter: xt_con...
42
43
  	struct hlist_head	iphash[256];
  	spinlock_t		lock;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
44
  };
294188ae3   Jan Engelhardt   netfilter: xtable...
45
  static u_int32_t connlimit_rnd __read_mostly;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
46

a34c45896   Al Viro   netfilter endian ...
47
  static inline unsigned int connlimit_iphash(__be32 addr)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
48
  {
a34c45896   Al Viro   netfilter endian ...
49
  	return jhash_1word((__force __u32)addr, connlimit_rnd) & 0xFF;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
50
51
52
  }
  
  static inline unsigned int
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
53
54
  connlimit_iphash6(const union nf_inet_addr *addr,
                    const union nf_inet_addr *mask)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
55
  {
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
56
  	union nf_inet_addr res;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
57
  	unsigned int i;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
58
59
  	for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
  		res.ip6[i] = addr->ip6[i] & mask->ip6[i];
a34c45896   Al Viro   netfilter endian ...
60
  	return jhash2((u32 *)res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
61
62
63
64
  }
  
  static inline bool already_closed(const struct nf_conn *conn)
  {
5e8fbe2ac   Patrick McHardy   [NETFILTER]: nf_c...
65
  	if (nf_ct_protonum(conn) == IPPROTO_TCP)
d2ee3f2c4   Dong Wei   netfilter: xt_con...
66
67
  		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT ||
  		       conn->proto.tcp.state == TCP_CONNTRACK_CLOSE;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
68
69
70
71
72
  	else
  		return 0;
  }
  
  static inline unsigned int
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
73
74
  same_source_net(const union nf_inet_addr *addr,
  		const union nf_inet_addr *mask,
76108cea0   Jan Engelhardt   netfilter: Use un...
75
  		const union nf_inet_addr *u3, u_int8_t family)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
76
  {
ee999d8b9   Jan Engelhardt   netfilter: x_tabl...
77
  	if (family == NFPROTO_IPV4) {
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
78
79
  		return (addr->ip & mask->ip) == (u3->ip & mask->ip);
  	} else {
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
80
  		union nf_inet_addr lh, rh;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
81
82
83
84
85
86
87
88
89
90
  		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];
  		}
  
  		return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
  	}
  }
83fc81024   Alexey Dobriyan   netfilter: xt_con...
91
92
  static int count_them(struct net *net,
  		      struct xt_connlimit_data *data,
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
93
  		      const struct nf_conntrack_tuple *tuple,
643a2c15a   Jan Engelhardt   [NETFILTER]: Intr...
94
95
  		      const union nf_inet_addr *addr,
  		      const union nf_inet_addr *mask,
20b7975e5   Stefan Berger   Revert "netfilter...
96
  		      u_int8_t family)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
97
  {
3cf93c96a   Jan Engelhardt   [NETFILTER]: anno...
98
  	const struct nf_conntrack_tuple_hash *found;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
99
  	struct xt_connlimit_conn *conn;
3e0d5149e   Changli Gao   netfilter: xt_con...
100
  	struct hlist_node *pos, *n;
ea781f197   Eric Dumazet   netfilter: nf_con...
101
  	struct nf_conn *found_ct;
3e0d5149e   Changli Gao   netfilter: xt_con...
102
  	struct hlist_head *hash;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
103
104
  	bool addit = true;
  	int matches = 0;
539054a8f   Jan Engelhardt   netfilter: xt_con...
105
  	if (family == NFPROTO_IPV6)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
106
107
108
  		hash = &data->iphash[connlimit_iphash6(addr, mask)];
  	else
  		hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
76507f69c   Patrick McHardy   [NETFILTER]: nf_c...
109
  	rcu_read_lock();
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
110
111
  
  	/* check the saved connections */
3e0d5149e   Changli Gao   netfilter: xt_con...
112
  	hlist_for_each_entry_safe(conn, pos, n, hash, node) {
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
113
114
  		found    = nf_conntrack_find_get(net, NF_CT_DEFAULT_ZONE,
  						 &conn->tuple);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  		found_ct = NULL;
  
  		if (found != NULL)
  			found_ct = nf_ct_tuplehash_to_ctrack(found);
  
  		if (found_ct != NULL &&
  		    nf_ct_tuple_equal(&conn->tuple, tuple) &&
  		    !already_closed(found_ct))
  			/*
  			 * 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".
  			 */
  			addit = false;
  
  		if (found == NULL) {
  			/* this one is gone */
3e0d5149e   Changli Gao   netfilter: xt_con...
132
  			hlist_del(&conn->node);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
133
134
135
136
137
138
139
140
141
  			kfree(conn);
  			continue;
  		}
  
  		if (already_closed(found_ct)) {
  			/*
  			 * we do not care about connections which are
  			 * closed already -> ditch it
  			 */
ea781f197   Eric Dumazet   netfilter: nf_con...
142
  			nf_ct_put(found_ct);
3e0d5149e   Changli Gao   netfilter: xt_con...
143
  			hlist_del(&conn->node);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
144
145
146
  			kfree(conn);
  			continue;
  		}
8183e3a88   Changli Gao   netfilter: xt_con...
147
  		if (same_source_net(addr, mask, &conn->addr, family))
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
148
149
  			/* same source network -> be counted! */
  			++matches;
ea781f197   Eric Dumazet   netfilter: nf_con...
150
  		nf_ct_put(found_ct);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
151
  	}
76507f69c   Patrick McHardy   [NETFILTER]: nf_c...
152
  	rcu_read_unlock();
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
153
154
155
  
  	if (addit) {
  		/* save the new connection in our list */
0e23ca14f   Changli Gao   netfilter: xt_con...
156
  		conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
157
158
159
  		if (conn == NULL)
  			return -ENOMEM;
  		conn->tuple = *tuple;
8183e3a88   Changli Gao   netfilter: xt_con...
160
  		conn->addr = *addr;
3e0d5149e   Changli Gao   netfilter: xt_con...
161
  		hlist_add_head(&conn->node, hash);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
162
163
164
165
166
  		++matches;
  	}
  
  	return matches;
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
167
  static bool
62fc80510   Jan Engelhardt   netfilter: xtable...
168
  connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
169
  {
83fc81024   Alexey Dobriyan   netfilter: xt_con...
170
  	struct net *net = dev_net(par->in ? par->in : par->out);
f7108a20d   Jan Engelhardt   netfilter: xtable...
171
  	const struct xt_connlimit_info *info = par->matchinfo;
22c2d8bca   Jan Engelhardt   [NETFILTER]: xt_c...
172
  	union nf_inet_addr addr;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
173
174
175
176
177
178
179
  	struct nf_conntrack_tuple tuple;
  	const struct nf_conntrack_tuple *tuple_ptr = &tuple;
  	enum ip_conntrack_info ctinfo;
  	const struct nf_conn *ct;
  	int connections;
  
  	ct = nf_ct_get(skb, &ctinfo);
8183e3a88   Changli Gao   netfilter: xt_con...
180
181
182
183
  	if (ct != NULL)
  		tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
  	else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
  				    par->family, &tuple))
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
184
  		goto hotdrop;
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
185
  	if (par->family == NFPROTO_IPV6) {
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
186
  		const struct ipv6hdr *iph = ipv6_hdr(skb);
cc4fc0225   Jan Engelhardt   netfilter: xtable...
187
188
  		memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ?
  		       &iph->daddr : &iph->saddr, sizeof(addr.ip6));
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
189
190
  	} else {
  		const struct iphdr *iph = ip_hdr(skb);
cc4fc0225   Jan Engelhardt   netfilter: xtable...
191
192
  		addr.ip = (info->flags & XT_CONNLIMIT_DADDR) ?
  			  iph->daddr : iph->saddr;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
193
194
195
  	}
  
  	spin_lock_bh(&info->data->lock);
83fc81024   Alexey Dobriyan   netfilter: xt_con...
196
  	connections = count_them(net, info->data, tuple_ptr, &addr,
20b7975e5   Stefan Berger   Revert "netfilter...
197
  	                         &info->mask, par->family);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
198
  	spin_unlock_bh(&info->data->lock);
1cc34c30b   Richard Weinberger   netfilter: xt_con...
199
  	if (connections < 0)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
200
  		/* kmalloc failed, drop it entirely */
1cc34c30b   Richard Weinberger   netfilter: xt_con...
201
  		goto hotdrop;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
202

cc4fc0225   Jan Engelhardt   netfilter: xtable...
203
204
  	return (connections > info->limit) ^
  	       !!(info->flags & XT_CONNLIMIT_INVERT);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
205
206
  
   hotdrop:
b4ba26119   Jan Engelhardt   netfilter: xtable...
207
  	par->hotdrop = true;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
208
209
  	return false;
  }
b0f38452f   Jan Engelhardt   netfilter: xtable...
210
  static int connlimit_mt_check(const struct xt_mtchk_param *par)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
211
  {
9b4fce7a3   Jan Engelhardt   netfilter: xtable...
212
  	struct xt_connlimit_info *info = par->matchinfo;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
213
  	unsigned int i;
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
214
  	int ret;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
215

4656c4d61   Changli Gao   netfilter: xt_con...
216
217
218
219
220
221
222
  	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...
223
  	}
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
224
225
  	ret = nf_ct_l3proto_try_module_get(par->family);
  	if (ret < 0) {
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
226
227
228
  		pr_info("cannot load conntrack support for "
  			"address family %u
  ", par->family);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
229
  		return ret;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
230
231
232
233
234
  	}
  
  	/* init private data */
  	info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
  	if (info->data == NULL) {
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
235
  		nf_ct_l3proto_module_put(par->family);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
236
  		return -ENOMEM;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
237
238
239
240
  	}
  
  	spin_lock_init(&info->data->lock);
  	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
3e0d5149e   Changli Gao   netfilter: xt_con...
241
  		INIT_HLIST_HEAD(&info->data->iphash[i]);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
242

bd414ee60   Jan Engelhardt   netfilter: xtable...
243
  	return 0;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
244
  }
6be3d8598   Jan Engelhardt   netfilter: xtable...
245
  static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
246
  {
6be3d8598   Jan Engelhardt   netfilter: xtable...
247
  	const struct xt_connlimit_info *info = par->matchinfo;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
248
  	struct xt_connlimit_conn *conn;
3e0d5149e   Changli Gao   netfilter: xt_con...
249
250
  	struct hlist_node *pos, *n;
  	struct hlist_head *hash = info->data->iphash;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
251
  	unsigned int i;
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
252
  	nf_ct_l3proto_module_put(par->family);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
253
254
  
  	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
3e0d5149e   Changli Gao   netfilter: xt_con...
255
256
  		hlist_for_each_entry_safe(conn, pos, n, &hash[i], node) {
  			hlist_del(&conn->node);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
257
258
259
260
261
262
  			kfree(conn);
  		}
  	}
  
  	kfree(info->data);
  }
cc4fc0225   Jan Engelhardt   netfilter: xtable...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
  static struct xt_match connlimit_mt_reg[] __read_mostly = {
  	{
  		.name       = "connlimit",
  		.revision   = 0,
  		.family     = NFPROTO_UNSPEC,
  		.checkentry = connlimit_mt_check,
  		.match      = connlimit_mt,
  		.matchsize  = sizeof(struct xt_connlimit_info),
  		.destroy    = connlimit_mt_destroy,
  		.me         = THIS_MODULE,
  	},
  	{
  		.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...
284
  };
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
285
  static int __init connlimit_mt_init(void)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
286
  {
cc4fc0225   Jan Engelhardt   netfilter: xtable...
287
288
  	return xt_register_matches(connlimit_mt_reg,
  	       ARRAY_SIZE(connlimit_mt_reg));
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
289
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
290
  static void __exit connlimit_mt_exit(void)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
291
  {
cc4fc0225   Jan Engelhardt   netfilter: xtable...
292
  	xt_unregister_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg));
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
293
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
294
295
  module_init(connlimit_mt_init);
  module_exit(connlimit_mt_exit);
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
296
  MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
2ae15b64e   Jan Engelhardt   [NETFILTER]: Upda...
297
  MODULE_DESCRIPTION("Xtables: Number of connections matching");
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
298
299
300
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("ipt_connlimit");
  MODULE_ALIAS("ip6t_connlimit");