Tomato 1.25
[tomato.git] / release / src / linux / linux / net / ipv4 / netfilter / ipt_connlimit.c
blobabf8efff65591ab916bd48d6f3e443d50934e658
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/list.h>
16 #include <linux/version.h>
17 #include <linux/netfilter_ipv4/ip_conntrack.h>
18 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
19 #include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
20 #include <linux/netfilter_ipv4/ip_tables.h>
21 #include <linux/netfilter_ipv4/ipt_connlimit.h>
23 #define DEBUG 0
25 MODULE_LICENSE("GPL");
27 /* we'll save the tuples of all connections we care about */
28 struct ipt_connlimit_conn
30 struct list_head list;
31 struct ip_conntrack_tuple tuple;
34 struct ipt_connlimit_data {
35 spinlock_t lock;
36 struct list_head iphash[256];
39 static inline unsigned ipt_iphash(const unsigned addr)
41 return ((addr ^ (addr >> 8) ^ (addr >> 16) ^ (addr >> 24)) & 0xff);
44 static int count_them(struct ipt_connlimit_data *data,
45 u_int32_t addr, u_int32_t mask,
46 struct ip_conntrack *ct)
48 #if DEBUG
49 const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv",
50 "fin_wait", "time_wait", "close", "close_wait",
51 "last_ack", "listen" };
52 #endif
53 int addit = 1, matches = 0;
54 struct ip_conntrack_tuple tuple;
55 struct ip_conntrack_tuple_hash *found;
56 struct ipt_connlimit_conn *conn;
57 struct list_head *hash,*lh;
59 spin_lock_bh(&data->lock);
60 tuple = ct->tuplehash[0].tuple;
61 hash = &data->iphash[ipt_iphash(addr & mask)];
63 /* check the saved connections */
64 for (lh = hash->next; lh != hash; lh = lh->next) {
65 conn = list_entry(lh,struct ipt_connlimit_conn,list);
66 found = ip_conntrack_find_get(&conn->tuple,ct);
67 if (found != NULL &&
68 0 == memcmp(&conn->tuple,&tuple,sizeof(tuple)) &&
69 found->ctrack->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT) {
70 /* Just to be sure we have it only once in the list.
71 We should'nt see tuples twice unless someone hooks this
72 into a table without "-p tcp --syn" */
73 addit = 0;
75 #if DEBUG
76 printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d %s\n",
77 ipt_iphash(addr & mask),
78 NIPQUAD(conn->tuple.src.ip), ntohs(conn->tuple.src.u.tcp.port),
79 NIPQUAD(conn->tuple.dst.ip), ntohs(conn->tuple.dst.u.tcp.port),
80 (NULL != found) ? tcp[found->ctrack->proto.tcp.state] : "gone");
81 #endif
82 if (NULL == found) {
83 /* this one is gone */
84 lh = lh->prev;
85 list_del(lh->next);
86 kfree(conn);
87 continue;
89 if (found->ctrack->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) {
90 /* we don't care about connections which are
91 closed already -> ditch it */
92 lh = lh->prev;
93 list_del(lh->next);
94 kfree(conn);
95 nf_conntrack_put(&found->ctrack->infos[0]);
96 continue;
98 if ((addr & mask) == (conn->tuple.src.ip & mask)) {
99 /* same source IP address -> be counted! */
100 matches++;
102 nf_conntrack_put(&found->ctrack->infos[0]);
104 if (addit) {
105 /* save the new connection in our list */
106 #if DEBUG
107 printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d new\n",
108 ipt_iphash(addr & mask),
109 NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port),
110 NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port));
111 #endif
112 conn = kmalloc(sizeof(*conn),GFP_ATOMIC);
113 if (NULL == conn)
114 return -1;
115 memset(conn,0,sizeof(*conn));
116 INIT_LIST_HEAD(&conn->list);
117 conn->tuple = tuple;
118 list_add(&conn->list,hash);
119 matches++;
121 spin_unlock_bh(&data->lock);
122 return matches;
125 static int
126 match(const struct sk_buff *skb,
127 const struct net_device *in,
128 const struct net_device *out,
129 const void *matchinfo,
130 int offset,
131 const void *hdr,
132 u_int16_t datalen,
133 int *hotdrop)
135 const struct ipt_connlimit_info *info = matchinfo;
136 int connections, match;
137 struct ip_conntrack *ct;
138 enum ip_conntrack_info ctinfo;
140 ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
141 if (NULL == ct) {
142 printk("ipt_connlimit: Oops: invalid ct state ?\n");
143 *hotdrop = 1;
144 return 0;
146 connections = count_them(info->data,skb->nh.iph->saddr,info->mask,ct);
147 if (-1 == connections) {
148 printk("ipt_connlimit: Hmm, kmalloc failed :-(\n");
149 *hotdrop = 1; /* let's free some memory :-) */
150 return 0;
152 match = (info->inverse) ? (connections <= info->limit) : (connections > info->limit);
153 #if DEBUG
154 printk("ipt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u "
155 "connections=%d limit=%d match=%s\n",
156 NIPQUAD(skb->nh.iph->saddr), NIPQUAD(info->mask),
157 connections, info->limit, match ? "yes" : "no");
158 #endif
160 return match;
163 static int check(const char *tablename,
164 const struct ipt_ip *ip,
165 void *matchinfo,
166 unsigned int matchsize,
167 unsigned int hook_mask)
169 struct ipt_connlimit_info *info = matchinfo;
170 int i;
172 /* verify size */
173 if (matchsize != IPT_ALIGN(sizeof(struct ipt_connlimit_info)))
174 return 0;
176 /* refuse anything but tcp */
177 if (ip->proto != IPPROTO_TCP)
178 return 0;
180 /* init private data */
181 info->data = kmalloc(sizeof(struct ipt_connlimit_data),GFP_KERNEL);
182 spin_lock_init(&(info->data->lock));
183 for (i = 0; i < 256; i++)
184 INIT_LIST_HEAD(&(info->data->iphash[i]));
186 return 1;
189 static void destroy(void *matchinfo, unsigned int matchinfosize)
191 struct ipt_connlimit_info *info = matchinfo;
192 struct ipt_connlimit_conn *conn;
193 struct list_head *hash;
194 int i;
196 /* cleanup */
197 for (i = 0; i < 256; i++) {
198 hash = &(info->data->iphash[i]);
199 while (hash != hash->next) {
200 conn = list_entry(hash->next,struct ipt_connlimit_conn,list);
201 list_del(hash->next);
202 kfree(conn);
205 kfree(info->data);
208 static struct ipt_match connlimit_match
209 = { { NULL, NULL }, "connlimit", &match, &check, &destroy, THIS_MODULE };
211 static int __init init(void)
213 return ipt_register_match(&connlimit_match);
216 static void __exit fini(void)
218 ipt_unregister_match(&connlimit_match);
221 module_init(init);
222 module_exit(fini);