Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / ipv4 / netfilter / ipt_recent.c
1 /* Kernel module to check if the source address has been seen recently. */
2 /* Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org */
3 /* Author: Stephen Frost <sfrost@snowman.net> */
4 /* Project Page: http://snowman.net/projects/ipt_recent/ */
5 /* This software is distributed under the terms of the GPL, Version 2 */
6 /* This copyright does not cover user programs that use kernel services
7  * by normal system calls. */
8
9 #include <linux/module.h>
10 #include <linux/skbuff.h>
11 #include <linux/proc_fs.h>
12 #include <linux/spinlock.h>
13 #include <linux/interrupt.h>
14 #include <asm/uaccess.h>
15 #include <linux/ctype.h>
16 #include <linux/ip.h>
17 #include <linux/vmalloc.h>
18 #include <linux/moduleparam.h>
19
20 #include <linux/netfilter_ipv4/ip_tables.h>
21 #include <linux/netfilter_ipv4/ipt_recent.h>
22
23 #undef DEBUG
24 #define HASH_LOG 9
25
26 /* Defaults, these can be overridden on the module command-line. */
27 static unsigned int ip_list_tot = 100;
28 static unsigned int ip_pkt_list_tot = 20;
29 static unsigned int ip_list_hash_size = 0;
30 static unsigned int ip_list_perms = 0644;
31 #ifdef DEBUG
32 static int debug = 1;
33 #endif
34
35 static char version[] =
36 KERN_INFO RECENT_NAME " " RECENT_VER ": Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n";
37
38 MODULE_AUTHOR("Stephen Frost <sfrost@snowman.net>");
39 MODULE_DESCRIPTION("IP tables recently seen matching module " RECENT_VER);
40 MODULE_LICENSE("GPL");
41 module_param(ip_list_tot, uint, 0400);
42 module_param(ip_pkt_list_tot, uint, 0400);
43 module_param(ip_list_hash_size, uint, 0400);
44 module_param(ip_list_perms, uint, 0400);
45 #ifdef DEBUG
46 module_param(debug, bool, 0600);
47 MODULE_PARM_DESC(debug,"enable debugging output");
48 #endif
49 MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list");
50 MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember");
51 MODULE_PARM_DESC(ip_list_hash_size,"size of hash table used to look up IPs");
52 MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_recent/* files");
53
54 /* Structure of our list of recently seen addresses. */
55 struct recent_ip_list {
56         u_int32_t addr;
57         u_int8_t  ttl;
58         unsigned long last_seen;
59         unsigned long *last_pkts;
60         u_int32_t oldest_pkt;
61         u_int32_t hash_entry;
62         u_int32_t time_pos;
63 };
64
65 struct time_info_list {
66         u_int32_t position;
67         u_int32_t time;
68 };
69
70 /* Structure of our linked list of tables of recent lists. */
71 struct recent_ip_tables {
72         char name[IPT_RECENT_NAME_LEN];
73         int count;
74         int time_pos;
75         struct recent_ip_list *table;
76         struct recent_ip_tables *next;
77         spinlock_t list_lock;
78         int *hash_table;
79         struct time_info_list *time_info;
80 #ifdef CONFIG_PROC_FS
81         struct proc_dir_entry *status_proc;
82 #endif /* CONFIG_PROC_FS */
83 };
84
85 /* Our current list of addresses we have recently seen.
86  * Only added to on a --set, and only updated on --set || --update 
87  */
88 static struct recent_ip_tables *r_tables = NULL;
89
90 /* We protect r_list with this spinlock so two processors are not modifying
91  * the list at the same time. 
92  */
93 static DEFINE_SPINLOCK(recent_lock);
94
95 #ifdef CONFIG_PROC_FS
96 /* Our /proc/net/ipt_recent entry */
97 static struct proc_dir_entry *proc_net_ipt_recent = NULL;
98 #endif
99
100 /* Function declaration for later. */
101 static int
102 match(const struct sk_buff *skb,
103       const struct net_device *in,
104       const struct net_device *out,
105       const struct xt_match *match,
106       const void *matchinfo,
107       int offset,
108       unsigned int protoff,
109       int *hotdrop);
110
111 /* Function to hash a given address into the hash table of table_size size */
112 static int hash_func(unsigned int addr, int table_size)
113 {
114         int result = 0;
115         unsigned int value = addr;
116         do { result ^= value; } while((value >>= HASH_LOG));
117
118 #ifdef DEBUG
119         if(debug) printk(KERN_INFO RECENT_NAME ": %d = hash_func(%u,%d)\n",
120                          result & (table_size - 1),
121                          addr,
122                          table_size);
123 #endif
124
125         return(result & (table_size - 1));
126 }
127
128 #ifdef CONFIG_PROC_FS
129 /* This is the function which produces the output for our /proc output
130  * interface which lists each IP address, the last seen time and the 
131  * other recent times the address was seen.
132  */
133
134 static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
135 {
136         int len = 0, count, last_len = 0, pkt_count;
137         off_t pos = 0;
138         off_t begin = 0;
139         struct recent_ip_tables *curr_table;
140
141         curr_table = (struct recent_ip_tables*) data;
142
143         spin_lock_bh(&curr_table->list_lock);
144         for(count = 0; count < ip_list_tot; count++) {
145                 if(!curr_table->table[count].addr) continue;
146                 last_len = len;
147                 len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].addr));
148                 len += sprintf(buffer+len,"ttl: %u ",curr_table->table[count].ttl);
149                 len += sprintf(buffer+len,"last_seen: %lu ",curr_table->table[count].last_seen);
150                 len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[count].oldest_pkt);
151                 len += sprintf(buffer+len,"last_pkts: %lu",curr_table->table[count].last_pkts[0]);
152                 for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) {
153                         if(!curr_table->table[count].last_pkts[pkt_count]) break;
154                         len += sprintf(buffer+len,", %lu",curr_table->table[count].last_pkts[pkt_count]);
155                 }
156                 len += sprintf(buffer+len,"\n");
157                 pos = begin + len;
158                 if(pos < offset) { len = 0; begin = pos; }
159                 if(pos > offset + length) { len = last_len; break; }
160         }
161
162         *start = buffer + (offset - begin);
163         len -= (offset - begin);
164         if(len > length) len = length;
165
166         spin_unlock_bh(&curr_table->list_lock);
167         return len;
168 }
169
170 /* ip_recent_ctrl provides an interface for users to modify the table
171  * directly.  This allows adding entries, removing entries, and
172  * flushing the entire table.
173  * This is done by opening up the appropriate table for writing and
174  * sending one of:
175  * xx.xx.xx.xx   -- Add entry to table with current time
176  * +xx.xx.xx.xx  -- Add entry to table with current time
177  * -xx.xx.xx.xx  -- Remove entry from table
178  * clear         -- Flush table, remove all entries
179  */
180
181 static int ip_recent_ctrl(struct file *file, const char __user *input, unsigned long size, void *data)
182 {
183         static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
184         u_int32_t val;
185         int base, used = 0;
186         char c, *cp;
187         union iaddr {
188                 uint8_t bytes[4];
189                 uint32_t word;
190         } res;
191         uint8_t *pp = res.bytes;
192         int digit;
193
194         char buffer[20];
195         int len, check_set = 0, count;
196         u_int32_t addr = 0;
197         struct sk_buff *skb;
198         struct ipt_recent_info *info;
199         struct recent_ip_tables *curr_table;
200
201         curr_table = (struct recent_ip_tables*) data;
202
203         if(size > 20) len = 20; else len = size;
204
205         if(copy_from_user(buffer,input,len)) return -EFAULT;
206
207         if(len < 20) buffer[len] = '\0';
208
209 #ifdef DEBUG
210         if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl len: %d, input: `%.20s'\n",len,buffer);
211 #endif
212
213         cp = buffer;
214         while(isspace(*cp)) { cp++; used++; if(used >= len-5) return used; }
215
216         /* Check if we are asked to flush the entire table */
217         if(!memcmp(cp,"clear",5)) {
218                 used += 5;
219                 spin_lock_bh(&curr_table->list_lock);
220                 curr_table->time_pos = 0;
221                 for(count = 0; count < ip_list_hash_size; count++) {
222                         curr_table->hash_table[count] = -1;
223                 }
224                 for(count = 0; count < ip_list_tot; count++) {
225                         curr_table->table[count].last_seen = 0;
226                         curr_table->table[count].addr = 0;
227                         curr_table->table[count].ttl = 0;
228                         memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
229                         curr_table->table[count].oldest_pkt = 0;
230                         curr_table->table[count].time_pos = 0;
231                         curr_table->time_info[count].position = count;
232                         curr_table->time_info[count].time = 0;
233                 }
234                 spin_unlock_bh(&curr_table->list_lock);
235                 return used;
236         }
237
238         check_set = IPT_RECENT_SET;
239         switch(*cp) {
240                 case '+': check_set = IPT_RECENT_SET; cp++; used++; break;
241                 case '-': check_set = IPT_RECENT_REMOVE; cp++; used++; break;
242                 default: if(!isdigit(*cp)) return (used+1); break;
243         }
244
245 #ifdef DEBUG
246         if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp,check_set);
247 #endif
248         /* Get addr (effectively inet_aton()) */
249         /* Shamelessly stolen from libc, a function in the kernel for doing
250          * this would, of course, be greatly preferred, but our options appear
251          * to be rather limited, so we will just do it ourselves here.
252          */
253         res.word = 0;
254
255         c = *cp;
256         for(;;) {
257                 if(!isdigit(c)) return used;
258                 val = 0; base = 10; digit = 0;
259                 if(c == '0') {
260                         c = *++cp;
261                         if(c == 'x' || c == 'X') base = 16, c = *++cp;
262                         else { base = 8; digit = 1; }
263                 }
264                 for(;;) {
265                         if(isascii(c) && isdigit(c)) {
266                                 if(base == 8 && (c == '8' || c == '0')) return used;
267                                 val = (val * base) + (c - '0');
268                                 c = *++cp;
269                                 digit = 1;
270                         } else if(base == 16 && isascii(c) && isxdigit(c)) {
271                                 val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A'));
272                                 c = *++cp;
273                                 digit = 1;
274                         } else break;
275                 }
276                 if(c == '.') {
277                         if(pp > res.bytes + 2 || val > 0xff) return used;
278                         *pp++ = val;
279                         c = *++cp;
280                 } else break;
281         }
282         used = cp - buffer;
283         if(c != '\0' && (!isascii(c) || !isspace(c))) return used;
284         if(c == '\n') used++;
285         if(!digit) return used;
286
287         if(val > max[pp - res.bytes]) return used;
288         addr = res.word | htonl(val);
289
290         if(!addr && check_set == IPT_RECENT_SET) return used;
291
292 #ifdef DEBUG
293         if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used);
294 #endif
295
296         /* Set up and just call match */
297         info = kmalloc(sizeof(struct ipt_recent_info),GFP_KERNEL);
298         if(!info) { return -ENOMEM; }
299         info->seconds = 0;
300         info->hit_count = 0;
301         info->check_set = check_set;
302         info->invert = 0;
303         info->side = IPT_RECENT_SOURCE;
304         strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN);
305         info->name[IPT_RECENT_NAME_LEN-1] = '\0';
306
307         skb = kmalloc(sizeof(struct sk_buff),GFP_KERNEL);
308         if (!skb) {
309                 used = -ENOMEM;
310                 goto out_free_info;
311         }
312         skb->nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL);
313         if (!skb->nh.iph) {
314                 used = -ENOMEM;
315                 goto out_free_skb;
316         }
317
318         skb->nh.iph->saddr = addr;
319         skb->nh.iph->daddr = 0;
320         /* Clear ttl since we have no way of knowing it */
321         skb->nh.iph->ttl = 0;
322         match(skb,NULL,NULL,NULL,info,0,0,NULL);
323
324         kfree(skb->nh.iph);
325 out_free_skb:
326         kfree(skb);
327 out_free_info:
328         kfree(info);
329
330 #ifdef DEBUG
331         if(debug) printk(KERN_INFO RECENT_NAME ": Leaving ip_recent_ctrl addr: %u used: %d\n",addr,used);
332 #endif
333         return used;
334 }
335
336 #endif /* CONFIG_PROC_FS */
337
338 /* 'match' is our primary function, called by the kernel whenever a rule is
339  * hit with our module as an option to it.
340  * What this function does depends on what was specifically asked of it by
341  * the user:
342  * --set -- Add or update last seen time of the source address of the packet
343  *   -- matchinfo->check_set == IPT_RECENT_SET
344  * --rcheck -- Just check if the source address is in the list
345  *   -- matchinfo->check_set == IPT_RECENT_CHECK
346  * --update -- If the source address is in the list, update last_seen
347  *   -- matchinfo->check_set == IPT_RECENT_UPDATE
348  * --remove -- If the source address is in the list, remove it
349  *   -- matchinfo->check_set == IPT_RECENT_REMOVE
350  * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
351  *   -- matchinfo->seconds
352  * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
353  *   -- matchinfo->hit_count
354  * --seconds and --hitcount can be combined
355  */
356 static int
357 match(const struct sk_buff *skb,
358       const struct net_device *in,
359       const struct net_device *out,
360       const struct xt_match *match,
361       const void *matchinfo,
362       int offset,
363       unsigned int protoff,
364       int *hotdrop)
365 {
366         int pkt_count, hits_found, ans;
367         unsigned long now;
368         const struct ipt_recent_info *info = matchinfo;
369         u_int32_t addr = 0, time_temp;
370         u_int8_t ttl = skb->nh.iph->ttl;
371         int *hash_table;
372         int orig_hash_result, hash_result, temp, location = 0, time_loc, end_collision_chain = -1;
373         struct time_info_list *time_info;
374         struct recent_ip_tables *curr_table;
375         struct recent_ip_tables *last_table;
376         struct recent_ip_list *r_list;
377
378 #ifdef DEBUG
379         if(debug) printk(KERN_INFO RECENT_NAME ": match() called\n");
380 #endif
381
382         /* Default is false ^ info->invert */
383         ans = info->invert;
384
385 #ifdef DEBUG
386         if(debug) printk(KERN_INFO RECENT_NAME ": match(): name = '%s'\n",info->name);
387 #endif
388
389         /* if out != NULL then routing has been done and TTL changed.
390          * We change it back here internally for match what came in before routing. */
391         if(out) ttl++;
392
393         /* Find the right table */
394         spin_lock_bh(&recent_lock);
395         curr_table = r_tables;
396         while( (last_table = curr_table) && strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (curr_table = curr_table->next) );
397
398 #ifdef DEBUG
399         if(debug) printk(KERN_INFO RECENT_NAME ": match(): table found('%s')\n",info->name);
400 #endif
401
402         spin_unlock_bh(&recent_lock);
403
404         /* Table with this name not found, match impossible */
405         if(!curr_table) { return ans; }
406
407         /* Make sure no one is changing the list while we work with it */
408         spin_lock_bh(&curr_table->list_lock);
409
410         r_list = curr_table->table;
411         if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr;
412
413         if(!addr) { 
414 #ifdef DEBUG
415                 if(debug) printk(KERN_INFO RECENT_NAME ": match() address (%u) invalid, leaving.\n",addr);
416 #endif
417                 spin_unlock_bh(&curr_table->list_lock);
418                 return ans;
419         }
420
421 #ifdef DEBUG
422         if(debug) printk(KERN_INFO RECENT_NAME ": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr,ttl,skb->nh.iph->ttl);
423 #endif
424
425         /* Get jiffies now in case they changed while we were waiting for a lock */
426         now = jiffies;
427         hash_table = curr_table->hash_table;
428         time_info = curr_table->time_info;
429
430         orig_hash_result = hash_result = hash_func(addr,ip_list_hash_size);
431         /* Hash entry at this result used */
432         /* Check for TTL match if requested.  If TTL is zero then a match would never
433          * happen, so match regardless of existing TTL in that case.  Zero means the
434          * entry was added via the /proc interface anyway, so we will just use the
435          * first TTL we get for that IP address. */
436         if(info->check_set & IPT_RECENT_TTL) {
437                 while(hash_table[hash_result] != -1 && !(r_list[hash_table[hash_result]].addr == addr &&
438                         (!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) {
439                         /* Collision in hash table */
440                         hash_result = (hash_result + 1) % ip_list_hash_size;
441                 }
442         } else {
443                 while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) {
444                         /* Collision in hash table */
445                         hash_result = (hash_result + 1) % ip_list_hash_size;
446                 }
447         }
448
449         if(hash_table[hash_result] == -1 && !(info->check_set & IPT_RECENT_SET)) {
450                 /* IP not in list and not asked to SET */
451                 spin_unlock_bh(&curr_table->list_lock);
452                 return ans;
453         }
454
455         /* Check if we need to handle the collision, do not need to on REMOVE */
456         if(orig_hash_result != hash_result && !(info->check_set & IPT_RECENT_REMOVE)) {
457 #ifdef DEBUG
458                 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
459                                  orig_hash_result,
460                                  hash_result,
461                                  r_list[hash_table[orig_hash_result]].addr,
462                                  addr);
463 #endif
464
465                 /* We had a collision.
466                  * orig_hash_result is where we started, hash_result is where we ended up.
467                  * So, swap them because we are likely to see the same guy again sooner */
468 #ifdef DEBUG
469                 if(debug) {
470                   printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table[orig_hash_result]);
471                   printk(KERN_INFO RECENT_NAME ": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
472                                 r_list[hash_table[orig_hash_result]].hash_entry);
473                 }
474 #endif
475
476                 r_list[hash_table[orig_hash_result]].hash_entry = hash_result;
477
478
479                 temp = hash_table[orig_hash_result];
480 #ifdef DEBUG
481                 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[hash_result] = %d\n",hash_table[hash_result]);
482 #endif
483                 hash_table[orig_hash_result] = hash_table[hash_result];
484                 hash_table[hash_result] = temp;
485                 temp = hash_result;
486                 hash_result = orig_hash_result;
487                 orig_hash_result = temp;
488                 time_info[r_list[hash_table[orig_hash_result]].time_pos].position = hash_table[orig_hash_result];
489                 if(hash_table[hash_result] != -1) {
490                         r_list[hash_table[hash_result]].hash_entry = hash_result;
491                         time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
492                 }
493
494 #ifdef DEBUG
495                 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision handled.\n");
496 #endif
497         }
498
499         if(hash_table[hash_result] == -1) {
500 #ifdef DEBUG
501                 if(debug) printk(KERN_INFO RECENT_NAME ": match(): New table entry. (hr: %d,ha: %u)\n",
502                                  hash_result, addr);
503 #endif
504
505                 /* New item found and IPT_RECENT_SET, so we need to add it */
506                 location = time_info[curr_table->time_pos].position;
507                 hash_table[r_list[location].hash_entry] = -1;
508                 hash_table[hash_result] = location;
509                 memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
510                 r_list[location].time_pos = curr_table->time_pos;
511                 r_list[location].addr = addr;
512                 r_list[location].ttl = ttl;
513                 r_list[location].last_seen = now;
514                 r_list[location].oldest_pkt = 1;
515                 r_list[location].last_pkts[0] = now;
516                 r_list[location].hash_entry = hash_result;
517                 time_info[curr_table->time_pos].time = r_list[location].last_seen;
518                 curr_table->time_pos = (curr_table->time_pos + 1) % ip_list_tot;
519
520                 ans = !info->invert;
521         } else {
522 #ifdef DEBUG
523                 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Existing table entry. (hr: %d,ha: %u)\n",
524                                  hash_result,
525                                  addr);
526 #endif
527
528                 /* Existing item found */
529                 location = hash_table[hash_result];
530                 /* We have a match on address, now to make sure it meets all requirements for a
531                  * full match. */
532                 if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) {
533                         if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert;
534                         if(info->seconds && !info->hit_count) {
535                                 if(time_before_eq(now,r_list[location].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert;
536                         }
537                         if(info->seconds && info->hit_count) {
538                                 for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
539                                         if(r_list[location].last_pkts[pkt_count] == 0) break;
540                                         if(time_before_eq(now,r_list[location].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++;
541                                 }
542                                 if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
543                         }
544                         if(info->hit_count && !info->seconds) {
545                                 for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
546                                         if(r_list[location].last_pkts[pkt_count] == 0) break;
547                                         hits_found++;
548                                 }
549                                 if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
550                         }
551                 }
552 #ifdef DEBUG
553                 if(debug) {
554                         if(ans)
555                                 printk(KERN_INFO RECENT_NAME ": match(): match addr: %u\n",addr);
556                         else
557                                 printk(KERN_INFO RECENT_NAME ": match(): no match addr: %u\n",addr);
558                 }
559 #endif
560
561                 /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
562                  * current timestamp to the last_seen. */
563                 if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) {
564 #ifdef DEBUG
565                         if(debug) printk(KERN_INFO RECENT_NAME ": match(): SET or UPDATE; updating time info.\n");
566 #endif
567                         /* Have to update our time info */
568                         time_loc = r_list[location].time_pos;
569                         time_info[time_loc].time = now;
570                         time_info[time_loc].position = location;
571                         while((time_info[(time_loc+1) % ip_list_tot].time < time_info[time_loc].time) && ((time_loc+1) % ip_list_tot) != curr_table->time_pos) {
572                                 time_temp = time_info[time_loc].time;
573                                 time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
574                                 time_info[(time_loc+1)%ip_list_tot].time = time_temp;
575                                 time_temp = time_info[time_loc].position;
576                                 time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
577                                 time_info[(time_loc+1)%ip_list_tot].position = time_temp;
578                                 r_list[time_info[time_loc].position].time_pos = time_loc;
579                                 r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
580                                 time_loc = (time_loc+1) % ip_list_tot;
581                         }
582                         r_list[location].time_pos = time_loc;
583                         r_list[location].ttl = ttl;
584                         r_list[location].last_pkts[r_list[location].oldest_pkt] = now;
585                         r_list[location].oldest_pkt = ++r_list[location].oldest_pkt % ip_pkt_list_tot;
586                         r_list[location].last_seen = now;
587                 }
588                 /* If we have been asked to remove the entry from the list, just set it to 0 */
589                 if(info->check_set & IPT_RECENT_REMOVE) {
590 #ifdef DEBUG
591                         if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result,hash_result);
592 #endif
593                         /* Check if this is part of a collision chain */
594                         while(hash_table[(orig_hash_result+1) % ip_list_hash_size] != -1) {
595                                 orig_hash_result++;
596                                 if(hash_func(r_list[hash_table[orig_hash_result]].addr,ip_list_hash_size) == hash_result) {
597                                         /* Found collision chain, how deep does this rabbit hole go? */
598 #ifdef DEBUG
599                                         if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; found collision chain.\n");
600 #endif
601                                         end_collision_chain = orig_hash_result;
602                                 }
603                         }
604                         if(end_collision_chain != -1) {
605 #ifdef DEBUG
606                                 if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; part of collision chain, moving to end.\n");
607 #endif
608                                 /* Part of a collision chain, swap it with the end of the chain
609                                  * before removing. */
610                                 r_list[hash_table[end_collision_chain]].hash_entry = hash_result;
611                                 temp = hash_table[end_collision_chain];
612                                 hash_table[end_collision_chain] = hash_table[hash_result];
613                                 hash_table[hash_result] = temp;
614                                 time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
615                                 hash_result = end_collision_chain;
616                                 r_list[hash_table[hash_result]].hash_entry = hash_result;
617                                 time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
618                         }
619                         location = hash_table[hash_result];
620                         hash_table[r_list[location].hash_entry] = -1;
621                         time_loc = r_list[location].time_pos;
622                         time_info[time_loc].time = 0;
623                         time_info[time_loc].position = location;
624                         while((time_info[(time_loc+1) % ip_list_tot].time < time_info[time_loc].time) && ((time_loc+1) % ip_list_tot) != curr_table->time_pos) {
625                                 time_temp = time_info[time_loc].time;
626                                 time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
627                                 time_info[(time_loc+1)%ip_list_tot].time = time_temp;
628                                 time_temp = time_info[time_loc].position;
629                                 time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
630                                 time_info[(time_loc+1)%ip_list_tot].position = time_temp;
631                                 r_list[time_info[time_loc].position].time_pos = time_loc;
632                                 r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
633                                 time_loc = (time_loc+1) % ip_list_tot;
634                         }
635                         r_list[location].time_pos = time_loc;
636                         r_list[location].last_seen = 0;
637                         r_list[location].addr = 0;
638                         r_list[location].ttl = 0;
639                         memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
640                         r_list[location].oldest_pkt = 0;
641                         ans = !info->invert;
642                 }
643                 spin_unlock_bh(&curr_table->list_lock);
644                 return ans;
645         }
646
647         spin_unlock_bh(&curr_table->list_lock);
648 #ifdef DEBUG
649         if(debug) printk(KERN_INFO RECENT_NAME ": match() left.\n");
650 #endif
651         return ans;
652 }
653
654 /* This function is to verify that the rule given during the userspace iptables
655  * command is correct.
656  * If the command is valid then we check if the table name referred to by the
657  * rule exists, if not it is created.
658  */
659 static int
660 checkentry(const char *tablename,
661            const void *ip,
662            const struct xt_match *match,
663            void *matchinfo,
664            unsigned int matchsize,
665            unsigned int hook_mask)
666 {
667         int flag = 0, c;
668         unsigned long *hold;
669         const struct ipt_recent_info *info = matchinfo;
670         struct recent_ip_tables *curr_table, *find_table, *last_table;
671
672 #ifdef DEBUG
673         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() entered.\n");
674 #endif
675
676         /* seconds and hit_count only valid for CHECK/UPDATE */
677         if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; }
678         if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; }
679         if(info->check_set & IPT_RECENT_CHECK) flag++;
680         if(info->check_set & IPT_RECENT_UPDATE) flag++;
681
682         /* One and only one of these should ever be set */
683         if(flag != 1) return 0;
684
685         /* Name must be set to something */
686         if(!info->name || !info->name[0]) return 0;
687
688         /* Things look good, create a list for this if it does not exist */
689         /* Lock the linked list while we play with it */
690         spin_lock_bh(&recent_lock);
691
692         /* Look for an entry with this name already created */
693         /* Finds the end of the list and the entry before the end if current name does not exist */
694         find_table = r_tables;
695         while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
696
697         /* If a table already exists just increment the count on that table and return */
698         if(find_table) { 
699 #ifdef DEBUG
700                 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), incrementing count.\n",info->name);
701 #endif
702                 find_table->count++;
703                 spin_unlock_bh(&recent_lock);
704                 return 1;
705         }
706
707         spin_unlock_bh(&recent_lock);
708
709         /* Table with this name not found */
710         /* Allocate memory for new linked list item */
711
712 #ifdef DEBUG
713         if(debug) {
714                 printk(KERN_INFO RECENT_NAME ": checkentry: no table found (%s)\n",info->name);
715                 printk(KERN_INFO RECENT_NAME ": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables));
716         }
717 #endif
718
719         curr_table = vmalloc(sizeof(struct recent_ip_tables));
720         if(curr_table == NULL) return 0;
721
722         spin_lock_init(&curr_table->list_lock);
723         curr_table->next = NULL;
724         curr_table->count = 1;
725         curr_table->time_pos = 0;
726         strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN);
727         curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0';
728
729         /* Allocate memory for this table and the list of packets in each entry. */
730 #ifdef DEBUG
731         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for table (%s).\n",
732                         sizeof(struct recent_ip_list)*ip_list_tot,
733                         info->name);
734 #endif
735
736         curr_table->table = vmalloc(sizeof(struct recent_ip_list)*ip_list_tot);
737         if(curr_table->table == NULL) { vfree(curr_table); return 0; }
738         memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot);
739 #ifdef DEBUG
740         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for pkt_list.\n",
741                         sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
742 #endif
743
744         hold = vmalloc(sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
745 #ifdef DEBUG
746         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: After pkt_list allocation.\n");
747 #endif
748         if(hold == NULL) { 
749                 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for pkt_list.\n");
750                 vfree(curr_table->table); 
751                 vfree(curr_table);
752                 return 0;
753         }
754         for(c = 0; c < ip_list_tot; c++) {
755                 curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot;
756         }
757
758         /* Allocate memory for the hash table */
759 #ifdef DEBUG
760         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for hash_table.\n",
761                         sizeof(int)*ip_list_hash_size);
762 #endif
763
764         curr_table->hash_table = vmalloc(sizeof(int)*ip_list_hash_size);
765         if(!curr_table->hash_table) {
766                 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for hash_table.\n");
767                 vfree(hold);
768                 vfree(curr_table->table); 
769                 vfree(curr_table);
770                 return 0;
771         }
772
773         for(c = 0; c < ip_list_hash_size; c++) {
774                 curr_table->hash_table[c] = -1;
775         }
776
777         /* Allocate memory for the time info */
778 #ifdef DEBUG
779         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for time_info.\n",
780                         sizeof(struct time_info_list)*ip_list_tot);
781 #endif
782
783         curr_table->time_info = vmalloc(sizeof(struct time_info_list)*ip_list_tot);
784         if(!curr_table->time_info) {
785                 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for time_info.\n");
786                 vfree(curr_table->hash_table);
787                 vfree(hold);
788                 vfree(curr_table->table); 
789                 vfree(curr_table);
790                 return 0;
791         }
792         for(c = 0; c < ip_list_tot; c++) {
793                 curr_table->time_info[c].position = c;
794                 curr_table->time_info[c].time = 0;
795         }
796
797         /* Put the new table in place */
798         spin_lock_bh(&recent_lock);
799         find_table = r_tables;
800         while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
801
802         /* If a table already exists just increment the count on that table and return */
803         if(find_table) { 
804                 find_table->count++;    
805                 spin_unlock_bh(&recent_lock);
806 #ifdef DEBUG
807                 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), created by other process.\n",info->name);
808 #endif
809                 vfree(curr_table->time_info);
810                 vfree(curr_table->hash_table);
811                 vfree(hold);
812                 vfree(curr_table->table);
813                 vfree(curr_table);
814                 return 1;
815         }
816         if(!last_table) r_tables = curr_table; else last_table->next = curr_table;
817
818         spin_unlock_bh(&recent_lock);
819
820 #ifdef CONFIG_PROC_FS
821         /* Create our proc 'status' entry. */
822         curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
823         if (!curr_table->status_proc) {
824                 vfree(hold);
825                 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
826                 /* Destroy the created table */
827                 spin_lock_bh(&recent_lock);
828                 last_table = NULL;
829                 curr_table = r_tables;
830                 if(!curr_table) {
831 #ifdef DEBUG
832                         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, no tables.\n");
833 #endif
834                         spin_unlock_bh(&recent_lock);
835                         return 0;
836                 }
837                 while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
838                 if(!curr_table) {
839 #ifdef DEBUG
840                         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, table already destroyed.\n");
841 #endif
842                         spin_unlock_bh(&recent_lock);
843                         return 0;
844                 }
845                 if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
846                 spin_unlock_bh(&recent_lock);
847                 vfree(curr_table->time_info);
848                 vfree(curr_table->hash_table);
849                 vfree(curr_table->table);
850                 vfree(curr_table);
851                 return 0;
852         }
853         
854         curr_table->status_proc->owner = THIS_MODULE;
855         curr_table->status_proc->data = curr_table;
856         wmb();
857         curr_table->status_proc->read_proc = ip_recent_get_info;
858         curr_table->status_proc->write_proc = ip_recent_ctrl;
859 #endif /* CONFIG_PROC_FS */
860
861 #ifdef DEBUG
862         if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() left.\n");
863 #endif
864
865         return 1;
866 }
867
868 /* This function is called in the event that a rule matching this module is
869  * removed.
870  * When this happens we need to check if there are no other rules matching
871  * the table given.  If that is the case then we remove the table and clean
872  * up its memory.
873  */
874 static void
875 destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
876 {
877         const struct ipt_recent_info *info = matchinfo;
878         struct recent_ip_tables *curr_table, *last_table;
879
880 #ifdef DEBUG
881         if(debug) printk(KERN_INFO RECENT_NAME ": destroy() entered.\n");
882 #endif
883
884         if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return;
885
886         /* Lock the linked list while we play with it */
887         spin_lock_bh(&recent_lock);
888
889         /* Look for an entry with this name already created */
890         /* Finds the end of the list and the entry before the end if current name does not exist */
891         last_table = NULL;
892         curr_table = r_tables;
893         if(!curr_table) { 
894 #ifdef DEBUG
895                 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() No tables found, leaving.\n");
896 #endif
897                 spin_unlock_bh(&recent_lock);
898                 return;
899         }
900         while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
901
902         /* If a table does not exist then do nothing and return */
903         if(!curr_table) { 
904 #ifdef DEBUG
905                 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table not found, leaving.\n");
906 #endif
907                 spin_unlock_bh(&recent_lock);
908                 return;
909         }
910
911         curr_table->count--;
912
913         /* If count is still non-zero then there are still rules referenceing it so we do nothing */
914         if(curr_table->count) { 
915 #ifdef DEBUG
916                 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, non-zero count, leaving.\n");
917 #endif
918                 spin_unlock_bh(&recent_lock);
919                 return;
920         }
921
922 #ifdef DEBUG
923         if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, zero count, removing.\n");
924 #endif
925
926         /* Count must be zero so we remove this table from the list */
927         if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
928
929         spin_unlock_bh(&recent_lock);
930
931         /* lock to make sure any late-runners still using this after we removed it from
932          * the list finish up then remove everything */
933         spin_lock_bh(&curr_table->list_lock);
934         spin_unlock_bh(&curr_table->list_lock);
935
936 #ifdef CONFIG_PROC_FS
937         if(curr_table->status_proc) remove_proc_entry(curr_table->name,proc_net_ipt_recent);
938 #endif /* CONFIG_PROC_FS */
939         vfree(curr_table->table[0].last_pkts);
940         vfree(curr_table->table);
941         vfree(curr_table->hash_table);
942         vfree(curr_table->time_info);
943         vfree(curr_table);
944
945 #ifdef DEBUG
946         if(debug) printk(KERN_INFO RECENT_NAME ": destroy() left.\n");
947 #endif
948
949         return;
950 }
951
952 /* This is the structure we pass to ipt_register to register our
953  * module with iptables.
954  */
955 static struct ipt_match recent_match = {
956         .name           = "recent",
957         .match          = match,
958         .matchsize      = sizeof(struct ipt_recent_info),
959         .checkentry     = checkentry,
960         .destroy        = destroy,
961         .me             = THIS_MODULE
962 };
963
964 /* Kernel module initialization. */
965 static int __init ipt_recent_init(void)
966 {
967         int err, count;
968
969         printk(version);
970 #ifdef CONFIG_PROC_FS
971         proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net);
972         if(!proc_net_ipt_recent) return -ENOMEM;
973 #endif
974
975         if(ip_list_hash_size && ip_list_hash_size <= ip_list_tot) {
976           printk(KERN_WARNING RECENT_NAME ": ip_list_hash_size too small, resetting to default.\n");
977           ip_list_hash_size = 0;
978         }
979
980         if(!ip_list_hash_size) {
981                 ip_list_hash_size = ip_list_tot*3;
982                 count = 2*2;
983                 while(ip_list_hash_size > count) count = count*2;
984                 ip_list_hash_size = count;
985         }
986
987 #ifdef DEBUG
988         if(debug) printk(KERN_INFO RECENT_NAME ": ip_list_hash_size: %d\n",ip_list_hash_size);
989 #endif
990
991         err = ipt_register_match(&recent_match);
992         if (err)
993                 remove_proc_entry("ipt_recent", proc_net);
994         return err;
995 }
996
997 /* Kernel module destruction. */
998 static void __exit ipt_recent_fini(void)
999 {
1000         ipt_unregister_match(&recent_match);
1001
1002         remove_proc_entry("ipt_recent",proc_net);
1003 }
1004
1005 /* Register our module with the kernel. */
1006 module_init(ipt_recent_init);
1007 module_exit(ipt_recent_fini);