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/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
22 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
23 #define CONFIG_NF_CONNTRACK_SUPPORT
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>
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>
36 #include <linux/netfilter_ipv4/ip_tables.h>
37 #include <linux/netfilter_ipv4/ipt_connlimit.h>
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
;
50 struct nf_conntrack_tuple tuple
;
54 struct ipt_connlimit_data
{
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
)
74 const static char *tcp
[] = { "none", "established", "syn_sent", "syn_recv",
75 "fin_wait", "time_wait", "close", "close_wait",
76 "last_ack", "listen" };
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
;
83 struct nf_conntrack_tuple tuple
;
84 struct nf_conntrack_tuple_hash
*found
;
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
);
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
);
106 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
107 && (found_ct
= tuplehash_to_ctrack(found
)) != NULL
109 && (found_ct
= nf_ct_tuplehash_to_ctrack(found
)) != NULL
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" */
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
),
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
),
128 (NULL
!= found
) ? tcp
[found_ct
->proto
.tcp
.state
] : "gone");
131 /* this one is gone */
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 */
143 nf_conntrack_put(&found_ct
->ct_general
);
146 #ifndef CONFIG_NF_CONNTRACK_SUPPORT
147 if ((addr
& mask
) == (conn
->tuple
.src
.ip
& mask
)) {
149 if ((addr
& mask
) == (conn
->tuple
.src
.u3
.ip
& mask
)) {
151 /* same source IP address -> be counted! */
154 nf_conntrack_put(&found_ct
->ct_general
);
157 /* save the new connection in our list */
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
));
165 NIPQUAD(tuple
.src
.u3
.ip
), ntohs(tuple
.src
.u
.tcp
.port
),
166 NIPQUAD(tuple
.dst
.u3
.ip
), ntohs(tuple
.dst
.u
.tcp
.port
));
170 conn
= kmalloc(sizeof(*conn
),GFP_ATOMIC
);
172 spin_unlock_bh(&data
->lock
);
175 memset(conn
,0,sizeof(*conn
));
176 INIT_LIST_HEAD(&conn
->list
);
178 list_add(&conn
->list
,hash
);
181 spin_unlock_bh(&data
->lock
);
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
,
192 const void *matchinfo
,
194 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
195 unsigned int protoff
,
199 const struct ipt_connlimit_info
*info
= matchinfo
;
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
);
208 enum ip_conntrack_info ctinfo
;
210 ct
= nf_ct_get((struct sk_buff
*)skb
, &ctinfo
);
213 printk("ipt_connlimit: Oops: invalid ct state ?\n");
218 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
219 connections
= count_them(info
->data
, ip_hdr(skb
)->saddr
, info
->mask
, ct
);
221 connections
= count_them(info
->data
, skb
->nh
.iph
->saddr
, info
->mask
, ct
);
223 if (-1 == connections
) {
224 printk("ipt_connlimit: Hmm, kmalloc failed :-(\n");
225 *hotdrop
= 1; /* let's free some memory :-) */
228 rv
= (info
->inverse
) ? (connections
<= info
->limit
) : (connections
> info
->limit
);
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");
239 static int checkentry(const char *tablename
,
240 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
243 const struct ipt_ip
*ip
,
245 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
246 const struct xt_match
*match
,
249 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
250 unsigned int matchsize
,
252 unsigned int hook_mask
)
254 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
255 const struct ipt_ip
*ip
= ip_void
;
258 struct ipt_connlimit_info
*info
= matchinfo
;
261 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
263 if (matchsize
!= IPT_ALIGN(sizeof(struct ipt_connlimit_info
)))
267 /* refuse anything but tcp */
268 if (ip
->proto
!= IPPROTO_TCP
)
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
]));
281 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
282 const struct xt_match
*match
,
284 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
285 void *matchinfo
, unsigned int matchsize
)
290 struct ipt_connlimit_info
*info
= matchinfo
;
291 struct ipt_connlimit_conn
*conn
;
292 struct list_head
*hash
;
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
);
307 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
308 static struct xt_match connlimit_match
= {
310 static struct ipt_match connlimit_match
= {
313 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
317 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
318 .matchsize
= sizeof(struct ipt_connlimit_info
),
320 .checkentry
= &checkentry
,
325 static int __init
init(void)
327 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
328 return xt_register_match(&connlimit_match
);
330 return ipt_register_match(&connlimit_match
);
334 static void __exit
fini(void)
336 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
337 xt_unregister_match(&connlimit_match
);
339 ipt_unregister_match(&connlimit_match
);