www.usr.com/support/gpl/USR9113_release1.0.tar.gz
[bcm963xx.git] / kernel / linux / net / ipv4 / netfilter / ipt_connlimit.c
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
7  *
8  * based on ...
9  *
10  * Kernel module to match connection tracking information.
11  * GPL (C) 1999  Rusty Russell (rusty@rustcorp.com.au).
12  */
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
15 #include <linux/list.h>
16 #include <linux/netfilter_ipv4/ip_conntrack.h>
17 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
18 #include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
19 #include <linux/netfilter_ipv4/ip_tables.h>
20 #include <linux/netfilter_ipv4/ipt_connlimit.h>
21
22 #define DEBUG 0
23
24 MODULE_LICENSE("GPL");
25
26 /* we'll save the tuples of all connections we care about */
27 struct ipt_connlimit_conn
28 {
29         struct list_head list;
30         struct ip_conntrack_tuple tuple;
31 };
32
33 struct ipt_connlimit_data {
34         spinlock_t lock;
35         struct list_head iphash[256];
36 };
37
38 static int ipt_iphash(u_int32_t addr)
39 {
40         int hash;
41
42         hash  =  addr        & 0xff;
43         hash ^= (addr >>  8) & 0xff;
44         hash ^= (addr >> 16) & 0xff;
45         hash ^= (addr >> 24) & 0xff;
46         return hash;
47 }
48
49 static int count_them(struct ipt_connlimit_data *data,
50                       u_int32_t addr, u_int32_t mask,
51                       struct ip_conntrack *ct)
52 {
53 #if DEBUG
54         const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv",
55                                      "fin_wait", "time_wait", "close", "close_wait",
56                                      "last_ack", "listen" };
57 #endif
58         int addit = 1, matches = 0;
59         struct ip_conntrack_tuple tuple;
60         struct ip_conntrack_tuple_hash *found;
61         struct ipt_connlimit_conn *conn;
62         struct list_head *hash,*lh;
63
64         spin_lock(&data->lock);
65         tuple = ct->tuplehash[0].tuple;
66         hash = &data->iphash[ipt_iphash(addr & mask)];
67
68         /* check the saved connections */
69         for (lh = hash->next; lh != hash; lh = lh->next) {
70                 conn = list_entry(lh,struct ipt_connlimit_conn,list);
71                 found = ip_conntrack_find_get(&conn->tuple,ct);
72                 if (0 == memcmp(&conn->tuple,&tuple,sizeof(tuple)) &&
73                     found != NULL &&
74                     found->ctrack->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT) {
75                         /* Just to be sure we have it only once in the list.
76                            We should'nt see tuples twice unless someone hooks this
77                            into a table without "-p tcp --syn" */
78                         addit = 0;
79                 }
80 #if DEBUG
81                 printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d %s\n",
82                        ipt_iphash(addr & mask),
83                        NIPQUAD(conn->tuple.src.ip), ntohs(conn->tuple.src.u.tcp.port),
84                        NIPQUAD(conn->tuple.dst.ip), ntohs(conn->tuple.dst.u.tcp.port),
85                        (NULL != found) ? tcp[found->ctrack->proto.tcp.state] : "gone");
86 #endif
87                 if (NULL == found) {
88                         /* this one is gone */
89                         lh = lh->prev;
90                         list_del(lh->next);
91                         kfree(conn);
92                         continue;
93                 }
94                 if (found->ctrack->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) {
95                         /* we don't care about connections which are
96                            closed already -> ditch it */
97                         lh = lh->prev;
98                         list_del(lh->next);
99                         kfree(conn);
100                         nf_conntrack_put(&found->ctrack->infos[0]);
101                         continue;
102                 }
103                 if ((addr & mask) == (conn->tuple.src.ip & mask)) {
104                         /* same source IP address -> be counted! */
105                         matches++;
106                 }
107                 nf_conntrack_put(&found->ctrack->infos[0]);
108         }
109         if (addit) {
110                 /* save the new connection in our list */
111 #if DEBUG
112                 printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d new\n",
113                        ipt_iphash(addr & mask),
114                        NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port),
115                        NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port));
116 #endif
117                 conn = kmalloc(sizeof(*conn),GFP_ATOMIC);
118                 if (NULL == conn)
119                         return -1;
120                 memset(conn,0,sizeof(*conn));
121                 INIT_LIST_HEAD(&conn->list);
122                 conn->tuple = tuple;
123                 list_add(&conn->list,hash);
124                 matches++;
125         }
126         spin_unlock(&data->lock);
127         return matches;
128 }
129
130 static int
131 match(const struct sk_buff *skb,
132       const struct net_device *in,
133       const struct net_device *out,
134       const void *matchinfo,
135       int offset,
136       int *hotdrop)
137 {
138         const struct ipt_connlimit_info *info = matchinfo;
139         int connections, match;
140         struct ip_conntrack *ct;
141         enum ip_conntrack_info ctinfo;
142
143         ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
144         if (NULL == ct) {
145                 printk("ipt_connlimit: Oops: invalid ct state ?\n");
146                 *hotdrop = 1;
147                 return 0;
148         }
149         connections = count_them(info->data,skb->nh.iph->saddr,info->mask,ct);
150         if (-1 == connections) {
151                 printk("ipt_connlimit: Hmm, kmalloc failed :-(\n");
152                 *hotdrop = 1; /* let's free some memory :-) */
153                 return 0;
154         }
155         match = (info->inverse) ? (connections <= info->limit) : (connections > info->limit);
156 #if DEBUG
157         printk("ipt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u "
158                "connections=%d limit=%d match=%s\n",
159                NIPQUAD(skb->nh.iph->saddr), NIPQUAD(info->mask),
160                connections, info->limit, match ? "yes" : "no");
161 #endif
162
163         return match;
164 }
165
166 static int check(const char *tablename,
167                  const struct ipt_ip *ip,
168                  void *matchinfo,
169                  unsigned int matchsize,
170                  unsigned int hook_mask)
171 {
172         struct ipt_connlimit_info *info = matchinfo;
173         int i;
174
175         /* verify size */
176         if (matchsize != IPT_ALIGN(sizeof(struct ipt_connlimit_info)))
177                 return 0;
178
179         /* refuse anything but tcp */
180         if (ip->proto != IPPROTO_TCP)
181                 return 0;
182
183         /* init private data */
184         info->data = kmalloc(sizeof(struct ipt_connlimit_data),GFP_KERNEL);
185         spin_lock_init(&(info->data->lock));
186         for (i = 0; i < 256; i++)
187                 INIT_LIST_HEAD(&(info->data->iphash[i]));
188         
189         return 1;
190 }
191
192 static void destroy(void *matchinfo, unsigned int matchinfosize)
193 {
194         struct ipt_connlimit_info *info = matchinfo;
195         struct ipt_connlimit_conn *conn;
196         struct list_head *hash;
197         int i;
198
199         /* cleanup */
200         for (i = 0; i < 256; i++) {
201                 hash = &(info->data->iphash[i]);
202                 while (hash != hash->next) {
203                         conn = list_entry(hash->next,struct ipt_connlimit_conn,list);
204                         list_del(hash->next);
205                         kfree(conn);
206                 }
207         }
208         kfree(info->data);
209 }
210
211 static struct ipt_match connlimit_match = { 
212         .name = "connlimit",
213         .match = &match,
214         .checkentry = &check,
215         .destroy = &destroy,
216         .me = THIS_MODULE
217 };
218
219 static int __init init(void)
220 {
221         return ipt_register_match(&connlimit_match);
222 }
223
224 static void __exit fini(void)
225 {
226         ipt_unregister_match(&connlimit_match);
227 }
228
229 module_init(init);
230 module_exit(fini);