NETFILTER: remove unnecessary goto statement for error recovery
[tomato.git] / release / src-rt / linux / linux-2.6 / net / ipv4 / netfilter / ipt_connlimit.c
blob151b3c443b39f6a770c6373f35b0db3ca83c099f
1 /*
2 * netfilter module to limit the number of parallel tcp
3 * connections per IP address.
4 * (c) 2000 Gerd Knorr <kraxel@bytesex.org>
5 * Nov 2002: Martin Bene <martin.bene@icomedias.com>:
6 * only ignore TIME_WAIT or gone connections
8 * based on ...
10 * Kernel module to match connection tracking information.
11 * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au).
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
15 #include <linux/version.h>
16 #include <linux/list.h>
18 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
19 #error Please use the xt_connlimit match
20 #endif
22 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
23 #define CONFIG_NF_CONNTRACK_SUPPORT
24 #endif
26 #ifdef CONFIG_NF_CONNTRACK_SUPPORT
27 #include <net/netfilter/nf_conntrack.h>
28 #include <net/netfilter/nf_conntrack_core.h>
29 #include <linux/netfilter/nf_conntrack_tcp.h>
30 #else
31 #include <linux/netfilter_ipv4/ip_conntrack.h>
32 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
33 #include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
34 #endif
36 #include <linux/netfilter_ipv4/ip_tables.h>
37 #include <linux/netfilter_ipv4/ipt_connlimit.h>
39 #define DEBUG 0
41 MODULE_LICENSE("GPL");
43 /* we'll save the tuples of all connections we care about */
44 struct ipt_connlimit_conn
46 struct list_head list;
47 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
48 struct ip_conntrack_tuple tuple;
49 #else
50 struct nf_conntrack_tuple tuple;
51 #endif
54 struct ipt_connlimit_data {
55 spinlock_t lock;
56 struct list_head iphash[256];
59 static inline unsigned ipt_iphash(const unsigned addr)
61 return ((addr ^ (addr >> 8) ^ (addr >> 16) ^ (addr >> 24)) & 0xff);
64 static int count_them(struct ipt_connlimit_data *data,
65 u_int32_t addr, u_int32_t mask,
66 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
67 struct ip_conntrack *ct)
68 #else
69 struct nf_conn *ct)
70 #endif
73 #if DEBUG
74 const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv",
75 "fin_wait", "time_wait", "close", "close_wait",
76 "last_ack", "listen" };
77 #endif
78 int addit = 1, matches = 0;
79 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
80 struct ip_conntrack_tuple tuple;
81 struct ip_conntrack_tuple_hash *found;
82 #else
83 struct nf_conntrack_tuple tuple;
84 struct nf_conntrack_tuple_hash *found;
85 #endif
86 struct ipt_connlimit_conn *conn;
87 struct list_head *hash,*lh;
89 spin_lock_bh(&data->lock);
90 tuple = ct->tuplehash[0].tuple;
91 hash = &data->iphash[ipt_iphash(addr & mask)];
93 /* check the saved connections */
94 for (lh = hash->next; lh != hash; lh = lh->next) {
95 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
96 struct ip_conntrack *found_ct = NULL;
97 conn = list_entry(lh, struct ipt_connlimit_conn, list);
98 found = ip_conntrack_find_get(&conn->tuple, ct);
99 #else
100 struct nf_conn *found_ct = NULL;
101 conn = list_entry(lh, struct ipt_connlimit_conn, list);
102 found = nf_conntrack_find_get(&conn->tuple, ct);
103 #endif
105 if (found != NULL
106 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
107 && (found_ct = tuplehash_to_ctrack(found)) != NULL
108 #else
109 && (found_ct = nf_ct_tuplehash_to_ctrack(found)) != NULL
110 #endif
111 && 0 == memcmp(&conn->tuple,&tuple,sizeof(tuple))
112 && found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT) {
113 /* Just to be sure we have it only once in the list.
114 We should'nt see tuples twice unless someone hooks this
115 into a table without "-p tcp --syn" */
116 addit = 0;
118 #if DEBUG
119 printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d %s\n",
120 ipt_iphash(addr & mask),
121 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
122 NIPQUAD(conn->tuple.src.ip), ntohs(conn->tuple.src.u.tcp.port),
123 NIPQUAD(conn->tuple.dst.ip), ntohs(conn->tuple.dst.u.tcp.port),
124 #else
125 NIPQUAD(conn->tuple.src.u3.ip), ntohs(conn->tuple.src.u.tcp.port),
126 NIPQUAD(conn->tuple.dst.u3.ip), ntohs(conn->tuple.dst.u.tcp.port),
127 #endif
128 (NULL != found) ? tcp[found_ct->proto.tcp.state] : "gone");
129 #endif
130 if (NULL == found) {
131 /* this one is gone */
132 lh = lh->prev;
133 list_del(lh->next);
134 kfree(conn);
135 continue;
137 if (found_ct->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) {
138 /* we don't care about connections which are
139 closed already -> ditch it */
140 lh = lh->prev;
141 list_del(lh->next);
142 kfree(conn);
143 nf_conntrack_put(&found_ct->ct_general);
144 continue;
146 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
147 if ((addr & mask) == (conn->tuple.src.ip & mask)) {
148 #else
149 if ((addr & mask) == (conn->tuple.src.u3.ip & mask)) {
150 #endif
151 /* same source IP address -> be counted! */
152 matches++;
154 nf_conntrack_put(&found_ct->ct_general);
156 if (addit) {
157 /* save the new connection in our list */
158 #if DEBUG
159 printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d new\n",
160 ipt_iphash(addr & mask),
161 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
162 NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port),
163 NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port));
164 #else
165 NIPQUAD(tuple.src.u3.ip), ntohs(tuple.src.u.tcp.port),
166 NIPQUAD(tuple.dst.u3.ip), ntohs(tuple.dst.u.tcp.port));
167 #endif
169 #endif
170 conn = kmalloc(sizeof(*conn),GFP_ATOMIC);
171 if (NULL == conn) {
172 spin_unlock_bh(&data->lock);
173 return -1;
175 memset(conn,0,sizeof(*conn));
176 INIT_LIST_HEAD(&conn->list);
177 conn->tuple = tuple;
178 list_add(&conn->list,hash);
179 matches++;
181 spin_unlock_bh(&data->lock);
182 return matches;
185 static int
186 match(const struct sk_buff *skb,
187 const struct net_device *in,
188 const struct net_device *out,
189 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
190 const struct xt_match *match,
191 #endif
192 const void *matchinfo,
193 int offset,
194 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
195 unsigned int protoff,
196 #endif
197 int *hotdrop)
199 const struct ipt_connlimit_info *info = matchinfo;
200 int connections, rv;
201 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
202 struct ip_conntrack *ct;
203 enum ip_conntrack_info ctinfo;
205 ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
206 #else
207 struct nf_conn *ct;
208 enum ip_conntrack_info ctinfo;
210 ct = nf_ct_get((struct sk_buff *)skb, &ctinfo);
211 #endif
212 if (NULL == ct) {
213 printk("ipt_connlimit: Oops: invalid ct state ?\n");
214 *hotdrop = 1;
215 return 0;
218 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
219 connections = count_them(info->data, ip_hdr(skb)->saddr, info->mask, ct);
220 #else
221 connections = count_them(info->data, skb->nh.iph->saddr, info->mask, ct);
222 #endif
223 if (-1 == connections) {
224 printk("ipt_connlimit: Hmm, kmalloc failed :-(\n");
225 *hotdrop = 1; /* let's free some memory :-) */
226 return 0;
228 rv = (info->inverse) ? (connections <= info->limit) : (connections > info->limit);
229 #if DEBUG
230 printk("ipt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u "
231 "connections=%d limit=%d match=%s\n",
232 NIPQUAD(skb->nh.iph->saddr), NIPQUAD(info->mask),
233 connections, info->limit, rv?"yes":"no");
234 #endif
236 return rv;
239 static int checkentry(const char *tablename,
240 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
241 const void *ip_void,
242 #else
243 const struct ipt_ip *ip,
244 #endif
245 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
246 const struct xt_match *match,
247 #endif
248 void *matchinfo,
249 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
250 unsigned int matchsize,
251 #endif
252 unsigned int hook_mask)
254 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
255 const struct ipt_ip *ip = ip_void;
256 #endif
258 struct ipt_connlimit_info *info = matchinfo;
259 int i;
261 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
262 /* verify size */
263 if (matchsize != IPT_ALIGN(sizeof(struct ipt_connlimit_info)))
264 return 0;
265 #endif
267 /* refuse anything but tcp */
268 if (ip->proto != IPPROTO_TCP)
269 return 0;
271 /* init private data */
272 info->data = kmalloc(sizeof(struct ipt_connlimit_data),GFP_KERNEL);
273 spin_lock_init(&(info->data->lock));
274 for (i = 0; i < 256; i++)
275 INIT_LIST_HEAD(&(info->data->iphash[i]));
277 return 1;
280 static void destroy(
281 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
282 const struct xt_match *match,
283 #endif
284 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
285 void *matchinfo, unsigned int matchsize)
286 #else
287 void *matchinfo)
288 #endif
290 struct ipt_connlimit_info *info = matchinfo;
291 struct ipt_connlimit_conn *conn;
292 struct list_head *hash;
293 int i;
295 /* cleanup */
296 for (i = 0; i < 256; i++) {
297 hash = &(info->data->iphash[i]);
298 while (hash != hash->next) {
299 conn = list_entry(hash->next,struct ipt_connlimit_conn,list);
300 list_del(hash->next);
301 kfree(conn);
304 kfree(info->data);
307 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
308 static struct xt_match connlimit_match = {
309 #else
310 static struct ipt_match connlimit_match = {
311 #endif
312 .name = "connlimit",
313 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
314 .family = AF_INET,
315 #endif
316 .match = &match,
317 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
318 .matchsize = sizeof(struct ipt_connlimit_info),
319 #endif
320 .checkentry = &checkentry,
321 .destroy = &destroy,
322 .me = THIS_MODULE
325 static int __init init(void)
327 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
328 return xt_register_match(&connlimit_match);
329 #else
330 return ipt_register_match(&connlimit_match);
331 #endif
334 static void __exit fini(void)
336 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
337 xt_unregister_match(&connlimit_match);
338 #else
339 ipt_unregister_match(&connlimit_match);
340 #endif
343 module_init(init);
344 module_exit(fini);