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
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/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>
24 MODULE_LICENSE("GPL");
26 /* we'll save the tuples of all connections we care about */
27 struct ipt_connlimit_conn
29 struct list_head list;
30 struct ip_conntrack_tuple tuple;
33 struct ipt_connlimit_data {
35 struct list_head iphash[256];
38 static int ipt_iphash(u_int32_t addr)
43 hash ^= (addr >> 8) & 0xff;
44 hash ^= (addr >> 16) & 0xff;
45 hash ^= (addr >> 24) & 0xff;
49 static int count_them(struct ipt_connlimit_data *data,
50 u_int32_t addr, u_int32_t mask,
51 struct ip_conntrack *ct)
54 const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv",
55 "fin_wait", "time_wait", "close", "close_wait",
56 "last_ack", "listen" };
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;
64 spin_lock(&data->lock);
65 tuple = ct->tuplehash[0].tuple;
66 hash = &data->iphash[ipt_iphash(addr & mask)];
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)) &&
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" */
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");
88 /* this one is gone */
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 */
100 nf_conntrack_put(&found->ctrack->infos[0]);
103 if ((addr & mask) == (conn->tuple.src.ip & mask)) {
104 /* same source IP address -> be counted! */
107 nf_conntrack_put(&found->ctrack->infos[0]);
110 /* save the new connection in our list */
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));
117 conn = kmalloc(sizeof(*conn),GFP_ATOMIC);
120 memset(conn,0,sizeof(*conn));
121 INIT_LIST_HEAD(&conn->list);
123 list_add(&conn->list,hash);
126 spin_unlock(&data->lock);
131 match(const struct sk_buff *skb,
132 const struct net_device *in,
133 const struct net_device *out,
134 const void *matchinfo,
138 const struct ipt_connlimit_info *info = matchinfo;
139 int connections, match;
140 struct ip_conntrack *ct;
141 enum ip_conntrack_info ctinfo;
143 ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
145 printk("ipt_connlimit: Oops: invalid ct state ?\n");
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 :-) */
155 match = (info->inverse) ? (connections <= info->limit) : (connections > info->limit);
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");
166 static int check(const char *tablename,
167 const struct ipt_ip *ip,
169 unsigned int matchsize,
170 unsigned int hook_mask)
172 struct ipt_connlimit_info *info = matchinfo;
176 if (matchsize != IPT_ALIGN(sizeof(struct ipt_connlimit_info)))
179 /* refuse anything but tcp */
180 if (ip->proto != IPPROTO_TCP)
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]));
192 static void destroy(void *matchinfo, unsigned int matchinfosize)
194 struct ipt_connlimit_info *info = matchinfo;
195 struct ipt_connlimit_conn *conn;
196 struct list_head *hash;
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);
211 static struct ipt_match connlimit_match = {
214 .checkentry = &check,
219 static int __init init(void)
221 return ipt_register_match(&connlimit_match);
224 static void __exit fini(void)
226 ipt_unregister_match(&connlimit_match);