Commit 4656c4d61adb8dc3ee04c08f57a5cc7598814420

Authored by Changli Gao
Committed by Patrick McHardy
1 parent 3e0d5149e6

netfilter: xt_connlimit: remove connlimit_rnd_inited

A potential race condition when generating connlimit_rnd is also fixed.

Signed-off-by: Changli Gao <xiaosuo@gmail.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>

Showing 1 changed file with 7 additions and 4 deletions Inline Diff

net/netfilter/xt_connlimit.c
1 /* 1 /*
2 * netfilter module to limit the number of parallel tcp 2 * netfilter module to limit the number of parallel tcp
3 * connections per IP address. 3 * connections per IP address.
4 * (c) 2000 Gerd Knorr <kraxel@bytesex.org> 4 * (c) 2000 Gerd Knorr <kraxel@bytesex.org>
5 * Nov 2002: Martin Bene <martin.bene@icomedias.com>: 5 * Nov 2002: Martin Bene <martin.bene@icomedias.com>:
6 * only ignore TIME_WAIT or gone connections 6 * only ignore TIME_WAIT or gone connections
7 * (C) CC Computer Consultants GmbH, 2007 7 * (C) CC Computer Consultants GmbH, 2007
8 * 8 *
9 * based on ... 9 * based on ...
10 * 10 *
11 * Kernel module to match connection tracking information. 11 * Kernel module to match connection tracking information.
12 * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au). 12 * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au).
13 */ 13 */
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15 #include <linux/in.h> 15 #include <linux/in.h>
16 #include <linux/in6.h> 16 #include <linux/in6.h>
17 #include <linux/ip.h> 17 #include <linux/ip.h>
18 #include <linux/ipv6.h> 18 #include <linux/ipv6.h>
19 #include <linux/jhash.h> 19 #include <linux/jhash.h>
20 #include <linux/slab.h> 20 #include <linux/slab.h>
21 #include <linux/list.h> 21 #include <linux/list.h>
22 #include <linux/module.h> 22 #include <linux/module.h>
23 #include <linux/random.h> 23 #include <linux/random.h>
24 #include <linux/skbuff.h> 24 #include <linux/skbuff.h>
25 #include <linux/spinlock.h> 25 #include <linux/spinlock.h>
26 #include <linux/netfilter/nf_conntrack_tcp.h> 26 #include <linux/netfilter/nf_conntrack_tcp.h>
27 #include <linux/netfilter/x_tables.h> 27 #include <linux/netfilter/x_tables.h>
28 #include <linux/netfilter/xt_connlimit.h> 28 #include <linux/netfilter/xt_connlimit.h>
29 #include <net/netfilter/nf_conntrack.h> 29 #include <net/netfilter/nf_conntrack.h>
30 #include <net/netfilter/nf_conntrack_core.h> 30 #include <net/netfilter/nf_conntrack_core.h>
31 #include <net/netfilter/nf_conntrack_tuple.h> 31 #include <net/netfilter/nf_conntrack_tuple.h>
32 #include <net/netfilter/nf_conntrack_zones.h> 32 #include <net/netfilter/nf_conntrack_zones.h>
33 33
34 /* we will save the tuples of all connections we care about */ 34 /* we will save the tuples of all connections we care about */
35 struct xt_connlimit_conn { 35 struct xt_connlimit_conn {
36 struct hlist_node node; 36 struct hlist_node node;
37 struct nf_conntrack_tuple tuple; 37 struct nf_conntrack_tuple tuple;
38 union nf_inet_addr addr; 38 union nf_inet_addr addr;
39 }; 39 };
40 40
41 struct xt_connlimit_data { 41 struct xt_connlimit_data {
42 struct hlist_head iphash[256]; 42 struct hlist_head iphash[256];
43 spinlock_t lock; 43 spinlock_t lock;
44 }; 44 };
45 45
46 static u_int32_t connlimit_rnd __read_mostly; 46 static u_int32_t connlimit_rnd __read_mostly;
47 static bool connlimit_rnd_inited __read_mostly;
48 47
49 static inline unsigned int connlimit_iphash(__be32 addr) 48 static inline unsigned int connlimit_iphash(__be32 addr)
50 { 49 {
51 return jhash_1word((__force __u32)addr, connlimit_rnd) & 0xFF; 50 return jhash_1word((__force __u32)addr, connlimit_rnd) & 0xFF;
52 } 51 }
53 52
54 static inline unsigned int 53 static inline unsigned int
55 connlimit_iphash6(const union nf_inet_addr *addr, 54 connlimit_iphash6(const union nf_inet_addr *addr,
56 const union nf_inet_addr *mask) 55 const union nf_inet_addr *mask)
57 { 56 {
58 union nf_inet_addr res; 57 union nf_inet_addr res;
59 unsigned int i; 58 unsigned int i;
60 59
61 for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) 60 for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
62 res.ip6[i] = addr->ip6[i] & mask->ip6[i]; 61 res.ip6[i] = addr->ip6[i] & mask->ip6[i];
63 62
64 return jhash2((u32 *)res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF; 63 return jhash2((u32 *)res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
65 } 64 }
66 65
67 static inline bool already_closed(const struct nf_conn *conn) 66 static inline bool already_closed(const struct nf_conn *conn)
68 { 67 {
69 if (nf_ct_protonum(conn) == IPPROTO_TCP) 68 if (nf_ct_protonum(conn) == IPPROTO_TCP)
70 return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT || 69 return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT ||
71 conn->proto.tcp.state == TCP_CONNTRACK_CLOSE; 70 conn->proto.tcp.state == TCP_CONNTRACK_CLOSE;
72 else 71 else
73 return 0; 72 return 0;
74 } 73 }
75 74
76 static inline unsigned int 75 static inline unsigned int
77 same_source_net(const union nf_inet_addr *addr, 76 same_source_net(const union nf_inet_addr *addr,
78 const union nf_inet_addr *mask, 77 const union nf_inet_addr *mask,
79 const union nf_inet_addr *u3, u_int8_t family) 78 const union nf_inet_addr *u3, u_int8_t family)
80 { 79 {
81 if (family == NFPROTO_IPV4) { 80 if (family == NFPROTO_IPV4) {
82 return (addr->ip & mask->ip) == (u3->ip & mask->ip); 81 return (addr->ip & mask->ip) == (u3->ip & mask->ip);
83 } else { 82 } else {
84 union nf_inet_addr lh, rh; 83 union nf_inet_addr lh, rh;
85 unsigned int i; 84 unsigned int i;
86 85
87 for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) { 86 for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) {
88 lh.ip6[i] = addr->ip6[i] & mask->ip6[i]; 87 lh.ip6[i] = addr->ip6[i] & mask->ip6[i];
89 rh.ip6[i] = u3->ip6[i] & mask->ip6[i]; 88 rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
90 } 89 }
91 90
92 return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0; 91 return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
93 } 92 }
94 } 93 }
95 94
96 static int count_them(struct net *net, 95 static int count_them(struct net *net,
97 struct xt_connlimit_data *data, 96 struct xt_connlimit_data *data,
98 const struct nf_conntrack_tuple *tuple, 97 const struct nf_conntrack_tuple *tuple,
99 const union nf_inet_addr *addr, 98 const union nf_inet_addr *addr,
100 const union nf_inet_addr *mask, 99 const union nf_inet_addr *mask,
101 u_int8_t family) 100 u_int8_t family)
102 { 101 {
103 const struct nf_conntrack_tuple_hash *found; 102 const struct nf_conntrack_tuple_hash *found;
104 struct xt_connlimit_conn *conn; 103 struct xt_connlimit_conn *conn;
105 struct hlist_node *pos, *n; 104 struct hlist_node *pos, *n;
106 struct nf_conn *found_ct; 105 struct nf_conn *found_ct;
107 struct hlist_head *hash; 106 struct hlist_head *hash;
108 bool addit = true; 107 bool addit = true;
109 int matches = 0; 108 int matches = 0;
110 109
111 if (family == NFPROTO_IPV6) 110 if (family == NFPROTO_IPV6)
112 hash = &data->iphash[connlimit_iphash6(addr, mask)]; 111 hash = &data->iphash[connlimit_iphash6(addr, mask)];
113 else 112 else
114 hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)]; 113 hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
115 114
116 rcu_read_lock(); 115 rcu_read_lock();
117 116
118 /* check the saved connections */ 117 /* check the saved connections */
119 hlist_for_each_entry_safe(conn, pos, n, hash, node) { 118 hlist_for_each_entry_safe(conn, pos, n, hash, node) {
120 found = nf_conntrack_find_get(net, NF_CT_DEFAULT_ZONE, 119 found = nf_conntrack_find_get(net, NF_CT_DEFAULT_ZONE,
121 &conn->tuple); 120 &conn->tuple);
122 found_ct = NULL; 121 found_ct = NULL;
123 122
124 if (found != NULL) 123 if (found != NULL)
125 found_ct = nf_ct_tuplehash_to_ctrack(found); 124 found_ct = nf_ct_tuplehash_to_ctrack(found);
126 125
127 if (found_ct != NULL && 126 if (found_ct != NULL &&
128 nf_ct_tuple_equal(&conn->tuple, tuple) && 127 nf_ct_tuple_equal(&conn->tuple, tuple) &&
129 !already_closed(found_ct)) 128 !already_closed(found_ct))
130 /* 129 /*
131 * Just to be sure we have it only once in the list. 130 * Just to be sure we have it only once in the list.
132 * We should not see tuples twice unless someone hooks 131 * We should not see tuples twice unless someone hooks
133 * this into a table without "-p tcp --syn". 132 * this into a table without "-p tcp --syn".
134 */ 133 */
135 addit = false; 134 addit = false;
136 135
137 if (found == NULL) { 136 if (found == NULL) {
138 /* this one is gone */ 137 /* this one is gone */
139 hlist_del(&conn->node); 138 hlist_del(&conn->node);
140 kfree(conn); 139 kfree(conn);
141 continue; 140 continue;
142 } 141 }
143 142
144 if (already_closed(found_ct)) { 143 if (already_closed(found_ct)) {
145 /* 144 /*
146 * we do not care about connections which are 145 * we do not care about connections which are
147 * closed already -> ditch it 146 * closed already -> ditch it
148 */ 147 */
149 nf_ct_put(found_ct); 148 nf_ct_put(found_ct);
150 hlist_del(&conn->node); 149 hlist_del(&conn->node);
151 kfree(conn); 150 kfree(conn);
152 continue; 151 continue;
153 } 152 }
154 153
155 if (same_source_net(addr, mask, &conn->addr, family)) 154 if (same_source_net(addr, mask, &conn->addr, family))
156 /* same source network -> be counted! */ 155 /* same source network -> be counted! */
157 ++matches; 156 ++matches;
158 nf_ct_put(found_ct); 157 nf_ct_put(found_ct);
159 } 158 }
160 159
161 rcu_read_unlock(); 160 rcu_read_unlock();
162 161
163 if (addit) { 162 if (addit) {
164 /* save the new connection in our list */ 163 /* save the new connection in our list */
165 conn = kmalloc(sizeof(*conn), GFP_ATOMIC); 164 conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
166 if (conn == NULL) 165 if (conn == NULL)
167 return -ENOMEM; 166 return -ENOMEM;
168 conn->tuple = *tuple; 167 conn->tuple = *tuple;
169 conn->addr = *addr; 168 conn->addr = *addr;
170 hlist_add_head(&conn->node, hash); 169 hlist_add_head(&conn->node, hash);
171 ++matches; 170 ++matches;
172 } 171 }
173 172
174 return matches; 173 return matches;
175 } 174 }
176 175
177 static bool 176 static bool
178 connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) 177 connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
179 { 178 {
180 struct net *net = dev_net(par->in ? par->in : par->out); 179 struct net *net = dev_net(par->in ? par->in : par->out);
181 const struct xt_connlimit_info *info = par->matchinfo; 180 const struct xt_connlimit_info *info = par->matchinfo;
182 union nf_inet_addr addr; 181 union nf_inet_addr addr;
183 struct nf_conntrack_tuple tuple; 182 struct nf_conntrack_tuple tuple;
184 const struct nf_conntrack_tuple *tuple_ptr = &tuple; 183 const struct nf_conntrack_tuple *tuple_ptr = &tuple;
185 enum ip_conntrack_info ctinfo; 184 enum ip_conntrack_info ctinfo;
186 const struct nf_conn *ct; 185 const struct nf_conn *ct;
187 int connections; 186 int connections;
188 187
189 ct = nf_ct_get(skb, &ctinfo); 188 ct = nf_ct_get(skb, &ctinfo);
190 if (ct != NULL) 189 if (ct != NULL)
191 tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; 190 tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
192 else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), 191 else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
193 par->family, &tuple)) 192 par->family, &tuple))
194 goto hotdrop; 193 goto hotdrop;
195 194
196 if (par->family == NFPROTO_IPV6) { 195 if (par->family == NFPROTO_IPV6) {
197 const struct ipv6hdr *iph = ipv6_hdr(skb); 196 const struct ipv6hdr *iph = ipv6_hdr(skb);
198 memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ? 197 memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ?
199 &iph->daddr : &iph->saddr, sizeof(addr.ip6)); 198 &iph->daddr : &iph->saddr, sizeof(addr.ip6));
200 } else { 199 } else {
201 const struct iphdr *iph = ip_hdr(skb); 200 const struct iphdr *iph = ip_hdr(skb);
202 addr.ip = (info->flags & XT_CONNLIMIT_DADDR) ? 201 addr.ip = (info->flags & XT_CONNLIMIT_DADDR) ?
203 iph->daddr : iph->saddr; 202 iph->daddr : iph->saddr;
204 } 203 }
205 204
206 spin_lock_bh(&info->data->lock); 205 spin_lock_bh(&info->data->lock);
207 connections = count_them(net, info->data, tuple_ptr, &addr, 206 connections = count_them(net, info->data, tuple_ptr, &addr,
208 &info->mask, par->family); 207 &info->mask, par->family);
209 spin_unlock_bh(&info->data->lock); 208 spin_unlock_bh(&info->data->lock);
210 209
211 if (connections < 0) 210 if (connections < 0)
212 /* kmalloc failed, drop it entirely */ 211 /* kmalloc failed, drop it entirely */
213 goto hotdrop; 212 goto hotdrop;
214 213
215 return (connections > info->limit) ^ 214 return (connections > info->limit) ^
216 !!(info->flags & XT_CONNLIMIT_INVERT); 215 !!(info->flags & XT_CONNLIMIT_INVERT);
217 216
218 hotdrop: 217 hotdrop:
219 par->hotdrop = true; 218 par->hotdrop = true;
220 return false; 219 return false;
221 } 220 }
222 221
223 static int connlimit_mt_check(const struct xt_mtchk_param *par) 222 static int connlimit_mt_check(const struct xt_mtchk_param *par)
224 { 223 {
225 struct xt_connlimit_info *info = par->matchinfo; 224 struct xt_connlimit_info *info = par->matchinfo;
226 unsigned int i; 225 unsigned int i;
227 int ret; 226 int ret;
228 227
229 if (unlikely(!connlimit_rnd_inited)) { 228 if (unlikely(!connlimit_rnd)) {
230 get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd)); 229 u_int32_t rand;
231 connlimit_rnd_inited = true; 230
231 do {
232 get_random_bytes(&rand, sizeof(rand));
233 } while (!rand);
234 cmpxchg(&connlimit_rnd, 0, rand);
232 } 235 }
233 ret = nf_ct_l3proto_try_module_get(par->family); 236 ret = nf_ct_l3proto_try_module_get(par->family);
234 if (ret < 0) { 237 if (ret < 0) {
235 pr_info("cannot load conntrack support for " 238 pr_info("cannot load conntrack support for "
236 "address family %u\n", par->family); 239 "address family %u\n", par->family);
237 return ret; 240 return ret;
238 } 241 }
239 242
240 /* init private data */ 243 /* init private data */
241 info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL); 244 info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
242 if (info->data == NULL) { 245 if (info->data == NULL) {
243 nf_ct_l3proto_module_put(par->family); 246 nf_ct_l3proto_module_put(par->family);
244 return -ENOMEM; 247 return -ENOMEM;
245 } 248 }
246 249
247 spin_lock_init(&info->data->lock); 250 spin_lock_init(&info->data->lock);
248 for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) 251 for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
249 INIT_HLIST_HEAD(&info->data->iphash[i]); 252 INIT_HLIST_HEAD(&info->data->iphash[i]);
250 253
251 return 0; 254 return 0;
252 } 255 }
253 256
254 static void connlimit_mt_destroy(const struct xt_mtdtor_param *par) 257 static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
255 { 258 {
256 const struct xt_connlimit_info *info = par->matchinfo; 259 const struct xt_connlimit_info *info = par->matchinfo;
257 struct xt_connlimit_conn *conn; 260 struct xt_connlimit_conn *conn;
258 struct hlist_node *pos, *n; 261 struct hlist_node *pos, *n;
259 struct hlist_head *hash = info->data->iphash; 262 struct hlist_head *hash = info->data->iphash;
260 unsigned int i; 263 unsigned int i;
261 264
262 nf_ct_l3proto_module_put(par->family); 265 nf_ct_l3proto_module_put(par->family);
263 266
264 for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) { 267 for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
265 hlist_for_each_entry_safe(conn, pos, n, &hash[i], node) { 268 hlist_for_each_entry_safe(conn, pos, n, &hash[i], node) {
266 hlist_del(&conn->node); 269 hlist_del(&conn->node);
267 kfree(conn); 270 kfree(conn);
268 } 271 }
269 } 272 }
270 273
271 kfree(info->data); 274 kfree(info->data);
272 } 275 }
273 276
274 static struct xt_match connlimit_mt_reg[] __read_mostly = { 277 static struct xt_match connlimit_mt_reg[] __read_mostly = {
275 { 278 {
276 .name = "connlimit", 279 .name = "connlimit",
277 .revision = 0, 280 .revision = 0,
278 .family = NFPROTO_UNSPEC, 281 .family = NFPROTO_UNSPEC,
279 .checkentry = connlimit_mt_check, 282 .checkentry = connlimit_mt_check,
280 .match = connlimit_mt, 283 .match = connlimit_mt,
281 .matchsize = sizeof(struct xt_connlimit_info), 284 .matchsize = sizeof(struct xt_connlimit_info),
282 .destroy = connlimit_mt_destroy, 285 .destroy = connlimit_mt_destroy,
283 .me = THIS_MODULE, 286 .me = THIS_MODULE,
284 }, 287 },
285 { 288 {
286 .name = "connlimit", 289 .name = "connlimit",
287 .revision = 1, 290 .revision = 1,
288 .family = NFPROTO_UNSPEC, 291 .family = NFPROTO_UNSPEC,
289 .checkentry = connlimit_mt_check, 292 .checkentry = connlimit_mt_check,
290 .match = connlimit_mt, 293 .match = connlimit_mt,
291 .matchsize = sizeof(struct xt_connlimit_info), 294 .matchsize = sizeof(struct xt_connlimit_info),
292 .destroy = connlimit_mt_destroy, 295 .destroy = connlimit_mt_destroy,
293 .me = THIS_MODULE, 296 .me = THIS_MODULE,
294 }, 297 },
295 }; 298 };
296 299
297 static int __init connlimit_mt_init(void) 300 static int __init connlimit_mt_init(void)
298 { 301 {
299 return xt_register_matches(connlimit_mt_reg, 302 return xt_register_matches(connlimit_mt_reg,
300 ARRAY_SIZE(connlimit_mt_reg)); 303 ARRAY_SIZE(connlimit_mt_reg));
301 } 304 }
302 305
303 static void __exit connlimit_mt_exit(void) 306 static void __exit connlimit_mt_exit(void)
304 { 307 {
305 xt_unregister_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg)); 308 xt_unregister_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg));
306 } 309 }
307 310
308 module_init(connlimit_mt_init); 311 module_init(connlimit_mt_init);
309 module_exit(connlimit_mt_exit); 312 module_exit(connlimit_mt_exit);
310 MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); 313 MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
311 MODULE_DESCRIPTION("Xtables: Number of connections matching"); 314 MODULE_DESCRIPTION("Xtables: Number of connections matching");
312 MODULE_LICENSE("GPL"); 315 MODULE_LICENSE("GPL");
313 MODULE_ALIAS("ipt_connlimit"); 316 MODULE_ALIAS("ipt_connlimit");
314 MODULE_ALIAS("ip6t_connlimit"); 317 MODULE_ALIAS("ip6t_connlimit");