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