Blame view

net/netfilter/xt_connlimit.c 7.51 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
36
37
38
39
40
41
42
43
  
  /* we will save the tuples of all connections we care about */
  struct xt_connlimit_conn {
  	struct list_head list;
  	struct nf_conntrack_tuple tuple;
  };
  
  struct xt_connlimit_data {
  	struct list_head iphash[256];
  	spinlock_t lock;
  };
294188ae3   Jan Engelhardt   netfilter: xtable...
44
45
  static u_int32_t connlimit_rnd __read_mostly;
  static bool connlimit_rnd_inited __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,
539054a8f   Jan Engelhardt   netfilter: xt_con...
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
100
  	struct xt_connlimit_conn *conn;
  	struct xt_connlimit_conn *tmp;
ea781f197   Eric Dumazet   netfilter: nf_con...
101
  	struct nf_conn *found_ct;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
102
103
104
  	struct list_head *hash;
  	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
112
  
  	/* check the saved connections */
  	list_for_each_entry_safe(conn, tmp, hash, list) {
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
132
133
134
135
136
137
138
139
140
141
  		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 */
  			list_del(&conn->list);
  			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);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
143
144
145
146
  			list_del(&conn->list);
  			kfree(conn);
  			continue;
  		}
539054a8f   Jan Engelhardt   netfilter: xt_con...
147
  		if (same_source_net(addr, mask, &conn->tuple.src.u3, 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
156
157
158
159
160
161
162
163
164
165
  
  	if (addit) {
  		/* save the new connection in our list */
  		conn = kzalloc(sizeof(*conn), GFP_ATOMIC);
  		if (conn == NULL)
  			return -ENOMEM;
  		conn->tuple = *tuple;
  		list_add(&conn->list, hash);
  		++matches;
  	}
  
  	return matches;
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
166
  static bool
62fc80510   Jan Engelhardt   netfilter: xtable...
167
  connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
168
  {
83fc81024   Alexey Dobriyan   netfilter: xt_con...
169
  	struct net *net = dev_net(par->in ? par->in : par->out);
f7108a20d   Jan Engelhardt   netfilter: xtable...
170
  	const struct xt_connlimit_info *info = par->matchinfo;
22c2d8bca   Jan Engelhardt   [NETFILTER]: xt_c...
171
  	union nf_inet_addr addr;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
172
173
174
175
176
177
178
179
180
181
  	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);
  	if (ct != NULL)
  		tuple_ptr = &ct->tuplehash[0].tuple;
  	else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
182
  				    par->family, &tuple))
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
183
  		goto hotdrop;
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
184
  	if (par->family == NFPROTO_IPV6) {
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
185
186
  		const struct ipv6hdr *iph = ipv6_hdr(skb);
  		memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr));
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
187
188
189
  	} else {
  		const struct iphdr *iph = ip_hdr(skb);
  		addr.ip = iph->saddr;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
190
191
192
  	}
  
  	spin_lock_bh(&info->data->lock);
83fc81024   Alexey Dobriyan   netfilter: xt_con...
193
  	connections = count_them(net, info->data, tuple_ptr, &addr,
539054a8f   Jan Engelhardt   netfilter: xt_con...
194
  	                         &info->mask, par->family);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
195
196
197
198
  	spin_unlock_bh(&info->data->lock);
  
  	if (connections < 0) {
  		/* kmalloc failed, drop it entirely */
b4ba26119   Jan Engelhardt   netfilter: xtable...
199
  		par->hotdrop = true;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
200
201
202
203
204
205
  		return false;
  	}
  
  	return (connections > info->limit) ^ info->inverse;
  
   hotdrop:
b4ba26119   Jan Engelhardt   netfilter: xtable...
206
  	par->hotdrop = true;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
207
208
  	return false;
  }
b0f38452f   Jan Engelhardt   netfilter: xtable...
209
  static int connlimit_mt_check(const struct xt_mtchk_param *par)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
210
  {
9b4fce7a3   Jan Engelhardt   netfilter: xtable...
211
  	struct xt_connlimit_info *info = par->matchinfo;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
212
  	unsigned int i;
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
213
  	int ret;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
214

294188ae3   Jan Engelhardt   netfilter: xtable...
215
216
217
218
  	if (unlikely(!connlimit_rnd_inited)) {
  		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
  		connlimit_rnd_inited = true;
  	}
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
219
220
  	ret = nf_ct_l3proto_try_module_get(par->family);
  	if (ret < 0) {
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
221
222
223
  		pr_info("cannot load conntrack support for "
  			"address family %u
  ", par->family);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
224
  		return ret;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
225
226
227
228
229
  	}
  
  	/* init private data */
  	info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
  	if (info->data == NULL) {
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
230
  		nf_ct_l3proto_module_put(par->family);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
231
  		return -ENOMEM;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
232
233
234
235
236
  	}
  
  	spin_lock_init(&info->data->lock);
  	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
  		INIT_LIST_HEAD(&info->data->iphash[i]);
bd414ee60   Jan Engelhardt   netfilter: xtable...
237
  	return 0;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
238
  }
6be3d8598   Jan Engelhardt   netfilter: xtable...
239
  static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
240
  {
6be3d8598   Jan Engelhardt   netfilter: xtable...
241
  	const struct xt_connlimit_info *info = par->matchinfo;
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
242
243
244
245
  	struct xt_connlimit_conn *conn;
  	struct xt_connlimit_conn *tmp;
  	struct list_head *hash = info->data->iphash;
  	unsigned int i;
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
246
  	nf_ct_l3proto_module_put(par->family);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
247
248
249
250
251
252
253
254
255
256
  
  	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
  		list_for_each_entry_safe(conn, tmp, &hash[i], list) {
  			list_del(&conn->list);
  			kfree(conn);
  		}
  	}
  
  	kfree(info->data);
  }
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
257
258
259
260
261
262
263
264
265
  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,
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
266
  };
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
267
  static int __init connlimit_mt_init(void)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
268
  {
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
269
  	return xt_register_match(&connlimit_mt_reg);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
270
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
271
  static void __exit connlimit_mt_exit(void)
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
272
  {
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
273
  	xt_unregister_match(&connlimit_mt_reg);
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
274
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
275
276
  module_init(connlimit_mt_init);
  module_exit(connlimit_mt_exit);
92f3b2b1b   Jan Engelhardt   netfilter: xtable...
277
  MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
2ae15b64e   Jan Engelhardt   [NETFILTER]: Upda...
278
  MODULE_DESCRIPTION("Xtables: Number of connections matching");
370786f9c   Jan Engelhardt   [NETFILTER]: x_ta...
279
280
281
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("ipt_connlimit");
  MODULE_ALIAS("ip6t_connlimit");