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 void *matchinfo,
106 int offset,
107 unsigned int protoff,
108 int *hotdrop);
109
110 /* Function to hash a given address into the hash table of table_size size */
111 static int hash_func(unsigned int addr, int table_size)
112 {
113 int result = 0;
114 unsigned int value = addr;
115 do { result ^= value; } while((value >>= HASH_LOG));
116
117 #ifdef DEBUG
118 if(debug) printk(KERN_INFO RECENT_NAME ": %d = hash_func(%u,%d)\n",
119 result & (table_size - 1),
120 addr,
121 table_size);
122 #endif
123
124 return(result & (table_size - 1));
125 }
126
127 #ifdef CONFIG_PROC_FS
128 /* This is the function which produces the output for our /proc output
129 * interface which lists each IP address, the last seen time and the
130 * other recent times the address was seen.
131 */
132
133 static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
134 {
135 int len = 0, count, last_len = 0, pkt_count;
136 off_t pos = 0;
137 off_t begin = 0;
138 struct recent_ip_tables *curr_table;
139
140 curr_table = (struct recent_ip_tables*) data;
141
142 spin_lock_bh(&curr_table->list_lock);
143 for(count = 0; count < ip_list_tot; count++) {
144 if(!curr_table->table[count].addr) continue;
145 last_len = len;
146 len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].addr));
147 len += sprintf(buffer+len,"ttl: %u ",curr_table->table[count].ttl);
148 len += sprintf(buffer+len,"last_seen: %lu ",curr_table->table[count].last_seen);
149 len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[count].oldest_pkt);
150 len += sprintf(buffer+len,"last_pkts: %lu",curr_table->table[count].last_pkts[0]);
151 for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) {
152 if(!curr_table->table[count].last_pkts[pkt_count]) break;
153 len += sprintf(buffer+len,", %lu",curr_table->table[count].last_pkts[pkt_count]);
154 }
155 len += sprintf(buffer+len,"\n");
156 pos = begin + len;
157 if(pos < offset) { len = 0; begin = pos; }
158 if(pos > offset + length) { len = last_len; break; }
159 }
160
161 *start = buffer + (offset - begin);
162 len -= (offset - begin);
163 if(len > length) len = length;
164
165 spin_unlock_bh(&curr_table->list_lock);
166 return len;
167 }
168
169 /* ip_recent_ctrl provides an interface for users to modify the table
170 * directly. This allows adding entries, removing entries, and
171 * flushing the entire table.
172 * This is done by opening up the appropriate table for writing and
173 * sending one of:
174 * xx.xx.xx.xx -- Add entry to table with current time
175 * +xx.xx.xx.xx -- Add entry to table with current time
176 * -xx.xx.xx.xx -- Remove entry from table
177 * clear -- Flush table, remove all entries
178 */
179
180 static int ip_recent_ctrl(struct file *file, const char __user *input, unsigned long size, void *data)
181 {
182 static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
183 u_int32_t val;
184 int base, used = 0;
185 char c, *cp;
186 union iaddr {
187 uint8_t bytes[4];
188 uint32_t word;
189 } res;
190 uint8_t *pp = res.bytes;
191 int digit;
192
193 char buffer[20];
194 int len, check_set = 0, count;
195 u_int32_t addr = 0;
196 struct sk_buff *skb;
197 struct ipt_recent_info *info;
198 struct recent_ip_tables *curr_table;
199
200 curr_table = (struct recent_ip_tables*) data;
201
202 if(size > 20) len = 20; else len = size;
203
204 if(copy_from_user(buffer,input,len)) return -EFAULT;
205
206 if(len < 20) buffer[len] = '\0';
207
208 #ifdef DEBUG
209 if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl len: %d, input: `%.20s'\n",len,buffer);
210 #endif
211
212 cp = buffer;
213 while(isspace(*cp)) { cp++; used++; if(used >= len-5) return used; }
214
215 /* Check if we are asked to flush the entire table */
216 if(!memcmp(cp,"clear",5)) {
217 used += 5;
218 spin_lock_bh(&curr_table->list_lock);
219 curr_table->time_pos = 0;
220 for(count = 0; count < ip_list_hash_size; count++) {
221 curr_table->hash_table[count] = -1;
222 }
223 for(count = 0; count < ip_list_tot; count++) {
224 curr_table->table[count].last_seen = 0;
225 curr_table->table[count].addr = 0;
226 curr_table->table[count].ttl = 0;
227 memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
228 curr_table->table[count].oldest_pkt = 0;
229 curr_table->table[count].time_pos = 0;
230 curr_table->time_info[count].position = count;
231 curr_table->time_info[count].time = 0;
232 }
233 spin_unlock_bh(&curr_table->list_lock);
234 return used;
235 }
236
237 check_set = IPT_RECENT_SET;
238 switch(*cp) {
239 case '+': check_set = IPT_RECENT_SET; cp++; used++; break;
240 case '-': check_set = IPT_RECENT_REMOVE; cp++; used++; break;
241 default: if(!isdigit(*cp)) return (used+1); break;
242 }
243
244 #ifdef DEBUG
245 if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp,check_set);
246 #endif
247 /* Get addr (effectively inet_aton()) */
248 /* Shamelessly stolen from libc, a function in the kernel for doing
249 * this would, of course, be greatly preferred, but our options appear
250 * to be rather limited, so we will just do it ourselves here.
251 */
252 res.word = 0;
253
254 c = *cp;
255 for(;;) {
256 if(!isdigit(c)) return used;
257 val = 0; base = 10; digit = 0;
258 if(c == '') {
259 c = *++cp;
260 if(c == 'x' || c == 'X') base = 16, c = *++cp;
261 else { base = 8; digit = 1; }
262 }
263 for(;;) {
264 if(isascii(c) && isdigit(c)) {
265 if(base == 8 && (c == '8' || c == '')) return used;
266 val = (val * base) + (c - '');
267 c = *++cp;
268 digit = 1;
269 } else if(base == 16 && isascii(c) && isxdigit(c)) {
270 val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A'));
271 c = *++cp;
272 digit = 1;
273 } else break;
274 }
275 if(c == '.') {
276 if(pp > res.bytes + 2 || val > 0xff) return used;
277 *pp++ = val;
278 c = *++cp;
279 } else break;
280 }
281 used = cp - buffer;
282 if(c != '\0' && (!isascii(c) || !isspace(c))) return used;
283 if(c == '\n') used++;
284 if(!digit) return used;
285
286 if(val > max[pp - res.bytes]) return used;
287 addr = res.word | htonl(val);
288
289 if(!addr && check_set == IPT_RECENT_SET) return used;
290
291 #ifdef DEBUG
292 if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used);
293 #endif
294
295 /* Set up and just call match */
296 info = kmalloc(sizeof(struct ipt_recent_info),GFP_KERNEL);
297 if(!info) { return -ENOMEM; }
298 info->seconds = 0;
299 info->hit_count = 0;
300 info->check_set = check_set;
301 info->invert = 0;
302 info->side = IPT_RECENT_SOURCE;
303 strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN);
304 info->name[IPT_RECENT_NAME_LEN-1] = '\0';
305
306 skb = kmalloc(sizeof(struct sk_buff),GFP_KERNEL);
307 if (!skb) {
308 used = -ENOMEM;
309 goto out_free_info;
310 }
311 skb->nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL);
312 if (!skb->nh.iph) {
313 used = -ENOMEM;
314 goto out_free_skb;
315 }
316
317 skb->nh.iph->saddr = addr;
318 skb->nh.iph->daddr = 0;
319 /* Clear ttl since we have no way of knowing it */
320 skb->nh.iph->ttl = 0;
321 match(skb,NULL,NULL,info,0,0,NULL);
322
323 kfree(skb->nh.iph);
324 out_free_skb:
325 kfree(skb);
326 out_free_info:
327 kfree(info);
328
329 #ifdef DEBUG
330 if(debug) printk(KERN_INFO RECENT_NAME ": Leaving ip_recent_ctrl addr: %u used: %d\n",addr,used);
331 #endif
332 return used;
333 }
334
335 #endif /* CONFIG_PROC_FS */
336
337 /* 'match' is our primary function, called by the kernel whenever a rule is
338 * hit with our module as an option to it.
339 * What this function does depends on what was specifically asked of it by
340 * the user:
341 * --set -- Add or update last seen time of the source address of the packet
342 * -- matchinfo->check_set == IPT_RECENT_SET
343 * --rcheck -- Just check if the source address is in the list
344 * -- matchinfo->check_set == IPT_RECENT_CHECK
345 * --update -- If the source address is in the list, update last_seen
346 * -- matchinfo->check_set == IPT_RECENT_UPDATE
347 * --remove -- If the source address is in the list, remove it
348 * -- matchinfo->check_set == IPT_RECENT_REMOVE
349 * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
350 * -- matchinfo->seconds
351 * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
352 * -- matchinfo->hit_count
353 * --seconds and --hitcount can be combined
354 */
355 static int
356 match(const struct sk_buff *skb,
357 const struct net_device *in,
358 const struct net_device *out,
359 const void *matchinfo,
360 int offset,
361 unsigned int protoff,
362 int *hotdrop)
363 {
364 int pkt_count, hits_found, ans;
365 unsigned long now;
366 const struct ipt_recent_info *info = matchinfo;
367 u_int32_t addr = 0, time_temp;
368 u_int8_t ttl = skb->nh.iph->ttl;
369 int *hash_table;
370 int orig_hash_result, hash_result, temp, location = 0, time_loc, end_collision_chain = -1;
371 struct time_info_list *time_info;
372 struct recent_ip_tables *curr_table;
373 struct recent_ip_tables *last_table;
374 struct recent_ip_list *r_list;
375
376 #ifdef DEBUG
377 if(debug) printk(KERN_INFO RECENT_NAME ": match() called\n");
378 #endif
379
380 /* Default is false ^ info->invert */
381 ans = info->invert;
382
383 #ifdef DEBUG
384 if(debug) printk(KERN_INFO RECENT_NAME ": match(): name = '%s'\n",info->name);
385 #endif
386
387 /* if out != NULL then routing has been done and TTL changed.
388 * We change it back here internally for match what came in before routing. */
389 if(out) ttl++;
390
391 /* Find the right table */
392 spin_lock_bh(&recent_lock);
393 curr_table = r_tables;
394 while( (last_table = curr_table) && strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (curr_table = curr_table->next) );
395
396 #ifdef DEBUG
397 if(debug) printk(KERN_INFO RECENT_NAME ": match(): table found('%s')\n",info->name);
398 #endif
399
400 spin_unlock_bh(&recent_lock);
401
402 /* Table with this name not found, match impossible */
403 if(!curr_table) { return ans; }
404
405 /* Make sure no one is changing the list while we work with it */
406 spin_lock_bh(&curr_table->list_lock);
407
408 r_list = curr_table->table;
409 if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr;
410
411 if(!addr) {
412 #ifdef DEBUG
413 if(debug) printk(KERN_INFO RECENT_NAME ": match() address (%u) invalid, leaving.\n",addr);
414 #endif
415 spin_unlock_bh(&curr_table->list_lock);
416 return ans;
417 }
418
419 #ifdef DEBUG
420 if(debug) printk(KERN_INFO RECENT_NAME ": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr,ttl,skb->nh.iph->ttl);
421 #endif
422
423 /* Get jiffies now in case they changed while we were waiting for a lock */
424 now = jiffies;
425 hash_table = curr_table->hash_table;
426 time_info = curr_table->time_info;
427
428 orig_hash_result = hash_result = hash_func(addr,ip_list_hash_size);
429 /* Hash entry at this result used */
430 /* Check for TTL match if requested. If TTL is zero then a match would never
431 * happen, so match regardless of existing TTL in that case. Zero means the
432 * entry was added via the /proc interface anyway, so we will just use the
433 * first TTL we get for that IP address. */
434 if(info->check_set & IPT_RECENT_TTL) {
435 while(hash_table[hash_result] != -1 && !(r_list[hash_table[hash_result]].addr == addr &&
436 (!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) {
437 /* Collision in hash table */
438 hash_result = (hash_result + 1) % ip_list_hash_size;
439 }
440 } else {
441 while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) {
442 /* Collision in hash table */
443 hash_result = (hash_result + 1) % ip_list_hash_size;
444 }
445 }
446
447 if(hash_table[hash_result] == -1 && !(info->check_set & IPT_RECENT_SET)) {
448 /* IP not in list and not asked to SET */
449 spin_unlock_bh(&curr_table->list_lock);
450 return ans;
451 }
452
453 /* Check if we need to handle the collision, do not need to on REMOVE */
454 if(orig_hash_result != hash_result && !(info->check_set & IPT_RECENT_REMOVE)) {
455 #ifdef DEBUG
456 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
457 orig_hash_result,
458 hash_result,
459 r_list[hash_table[orig_hash_result]].addr,
460 addr);
461 #endif
462
463 /* We had a collision.
464 * orig_hash_result is where we started, hash_result is where we ended up.
465 * So, swap them because we are likely to see the same guy again sooner */
466 #ifdef DEBUG
467 if(debug) {
468 printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table[orig_hash_result]);
469 printk(KERN_INFO RECENT_NAME ": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
470 r_list[hash_table[orig_hash_result]].hash_entry);
471 }
472 #endif
473
474 r_list[hash_table[orig_hash_result]].hash_entry = hash_result;
475
476
477 temp = hash_table[orig_hash_result];
478 #ifdef DEBUG
479 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[hash_result] = %d\n",hash_table[hash_result]);
480 #endif
481 hash_table[orig_hash_result] = hash_table[hash_result];
482 hash_table[hash_result] = temp;
483 temp = hash_result;
484 hash_result = orig_hash_result;
485 orig_hash_result = temp;
486 time_info[r_list[hash_table[orig_hash_result]].time_pos].position = hash_table[orig_hash_result];
487 if(hash_table[hash_result] != -1) {
488 r_list[hash_table[hash_result]].hash_entry = hash_result;
489 time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
490 }
491
492 #ifdef DEBUG
493 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision handled.\n");
494 #endif
495 }
496
497 if(hash_table[hash_result] == -1) {
498 #ifdef DEBUG
499 if(debug) printk(KERN_INFO RECENT_NAME ": match(): New table entry. (hr: %d,ha: %u)\n",
500 hash_result, addr);
501 #endif
502
503 /* New item found and IPT_RECENT_SET, so we need to add it */
504 location = time_info[curr_table->time_pos].position;
505 hash_table[r_list[location].hash_entry] = -1;
506 hash_table[hash_result] = location;
507 memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
508 r_list[location].time_pos = curr_table->time_pos;
509 r_list[location].addr = addr;
510 r_list[location].ttl = ttl;
511 r_list[location].last_seen = now;
512 r_list[location].oldest_pkt = 1;
513 r_list[location].last_pkts[0] = now;
514 r_list[location].hash_entry = hash_result;
515 time_info[curr_table->time_pos].time = r_list[location].last_seen;
516 curr_table->time_pos = (curr_table->time_pos + 1) % ip_list_tot;
517
518 ans = !info->invert;
519 } else {
520 #ifdef DEBUG
521 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Existing table entry. (hr: %d,ha: %u)\n",
522 hash_result,
523 addr);
524 #endif
525
526 /* Existing item found */
527 location = hash_table[hash_result];
528 /* We have a match on address, now to make sure it meets all requirements for a
529 * full match. */
530 if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) {
531 if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert;
532 if(info->seconds && !info->hit_count) {
533 if(time_before_eq(now,r_list[location].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert;
534 }
535 if(info->seconds && info->hit_count) {
536 for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
537 if(r_list[location].last_pkts[pkt_count] == 0) break;
538 if(time_before_eq(now,r_list[location].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++;
539 }
540 if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
541 }
542 if(info->hit_count && !info->seconds) {
543 for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
544 if(r_list[location].last_pkts[pkt_count] == 0) break;
545 hits_found++;
546 }
547 if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
548 }
549 }
550 #ifdef DEBUG
551 if(debug) {
552 if(ans)
553 printk(KERN_INFO RECENT_NAME ": match(): match addr: %u\n",addr);
554 else
555 printk(KERN_INFO RECENT_NAME ": match(): no match addr: %u\n",addr);
556 }
557 #endif
558
559 /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
560 * current timestamp to the last_seen. */
561 if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) {
562 #ifdef DEBUG
563 if(debug) printk(KERN_INFO RECENT_NAME ": match(): SET or UPDATE; updating time info.\n");
564 #endif
565 /* Have to update our time info */
566 time_loc = r_list[location].time_pos;
567 time_info[time_loc].time = now;
568 time_info[time_loc].position = location;
569 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) {
570 time_temp = time_info[time_loc].time;
571 time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
572 time_info[(time_loc+1)%ip_list_tot].time = time_temp;
573 time_temp = time_info[time_loc].position;
574 time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
575 time_info[(time_loc+1)%ip_list_tot].position = time_temp;
576 r_list[time_info[time_loc].position].time_pos = time_loc;
577 r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
578 time_loc = (time_loc+1) % ip_list_tot;
579 }
580 r_list[location].time_pos = time_loc;
581 r_list[location].ttl = ttl;
582 r_list[location].last_pkts[r_list[location].oldest_pkt] = now;
583 r_list[location].oldest_pkt = ++r_list[location].oldest_pkt % ip_pkt_list_tot;
584 r_list[location].last_seen = now;
585 }
586 /* If we have been asked to remove the entry from the list, just set it to 0 */
587 if(info->check_set & IPT_RECENT_REMOVE) {
588 #ifdef DEBUG
589 if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result,hash_result);
590 #endif
591 /* Check if this is part of a collision chain */
592 while(hash_table[(orig_hash_result+1) % ip_list_hash_size] != -1) {
593 orig_hash_result++;
594 if(hash_func(r_list[hash_table[orig_hash_result]].addr,ip_list_hash_size) == hash_result) {
595 /* Found collision chain, how deep does this rabbit hole go? */
596 #ifdef DEBUG
597 if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; found collision chain.\n");
598 #endif
599 end_collision_chain = orig_hash_result;
600 }
601 }
602 if(end_collision_chain != -1) {
603 #ifdef DEBUG
604 if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; part of collision chain, moving to end.\n");
605 #endif
606 /* Part of a collision chain, swap it with the end of the chain
607 * before removing. */
608 r_list[hash_table[end_collision_chain]].hash_entry = hash_result;
609 temp = hash_table[end_collision_chain];
610 hash_table[end_collision_chain] = hash_table[hash_result];
611 hash_table[hash_result] = temp;
612 time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
613 hash_result = end_collision_chain;
614 r_list[hash_table[hash_result]].hash_entry = hash_result;
615 time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
616 }
617 location = hash_table[hash_result];
618 hash_table[r_list[location].hash_entry] = -1;
619 time_loc = r_list[location].time_pos;
620 time_info[time_loc].time = 0;
621 time_info[time_loc].position = location;
622 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) {
623 time_temp = time_info[time_loc].time;
624 time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
625 time_info[(time_loc+1)%ip_list_tot].time = time_temp;
626 time_temp = time_info[time_loc].position;
627 time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
628 time_info[(time_loc+1)%ip_list_tot].position = time_temp;
629 r_list[time_info[time_loc].position].time_pos = time_loc;
630 r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
631 time_loc = (time_loc+1) % ip_list_tot;
632 }
633 r_list[location].time_pos = time_loc;
634 r_list[location].last_seen = 0;
635 r_list[location].addr = 0;
636 r_list[location].ttl = 0;
637 memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
638 r_list[location].oldest_pkt = 0;
639 ans = !info->invert;
640 }
641 spin_unlock_bh(&curr_table->list_lock);
642 return ans;
643 }
644
645 spin_unlock_bh(&curr_table->list_lock);
646 #ifdef DEBUG
647 if(debug) printk(KERN_INFO RECENT_NAME ": match() left.\n");
648 #endif
649 return ans;
650 }
651
652 /* This function is to verify that the rule given during the userspace iptables
653 * command is correct.
654 * If the command is valid then we check if the table name referred to by the
655 * rule exists, if not it is created.
656 */
657 static int
658 checkentry(const char *tablename,
659 const void *ip,
660 void *matchinfo,
661 unsigned int matchsize,
662 unsigned int hook_mask)
663 {
664 int flag = 0, c;
665 unsigned long *hold;
666 const struct ipt_recent_info *info = matchinfo;
667 struct recent_ip_tables *curr_table, *find_table, *last_table;
668
669 #ifdef DEBUG
670 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() entered.\n");
671 #endif
672
673 if (matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return 0;
674
675 /* seconds and hit_count only valid for CHECK/UPDATE */
676 if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; }
677 if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; }
678 if(info->check_set & IPT_RECENT_CHECK) flag++;
679 if(info->check_set & IPT_RECENT_UPDATE) flag++;
680
681 /* One and only one of these should ever be set */
682 if(flag != 1) return 0;
683
684 /* Name must be set to something */
685 if(!info->name || !info->name[0]) return 0;
686
687 /* Things look good, create a list for this if it does not exist */
688 /* Lock the linked list while we play with it */
689 spin_lock_bh(&recent_lock);
690
691 /* Look for an entry with this name already created */
692 /* Finds the end of the list and the entry before the end if current name does not exist */
693 find_table = r_tables;
694 while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
695
696 /* If a table already exists just increment the count on that table and return */
697 if(find_table) {
698 #ifdef DEBUG
699 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), incrementing count.\n",info->name);
700 #endif
701 find_table->count++;
702 spin_unlock_bh(&recent_lock);
703 return 1;
704 }
705
706 spin_unlock_bh(&recent_lock);
707
708 /* Table with this name not found */
709 /* Allocate memory for new linked list item */
710
711 #ifdef DEBUG
712 if(debug) {
713 printk(KERN_INFO RECENT_NAME ": checkentry: no table found (%s)\n",info->name);
714 printk(KERN_INFO RECENT_NAME ": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables));
715 }
716 #endif
717
718 curr_table = vmalloc(sizeof(struct recent_ip_tables));
719 if(curr_table == NULL) return 0;
720
721 spin_lock_init(&curr_table->list_lock);
722 curr_table->next = NULL;
723 curr_table->count = 1;
724 curr_table->time_pos = 0;
725 strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN);
726 curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0';
727
728 /* Allocate memory for this table and the list of packets in each entry. */
729 #ifdef DEBUG
730 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for table (%s).\n",
731 sizeof(struct recent_ip_list)*ip_list_tot,
732 info->name);
733 #endif
734
735 curr_table->table = vmalloc(sizeof(struct recent_ip_list)*ip_list_tot);
736 if(curr_table->table == NULL) { vfree(curr_table); return 0; }
737 memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot);
738 #ifdef DEBUG
739 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for pkt_list.\n",
740 sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
741 #endif
742
743 hold = vmalloc(sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
744 #ifdef DEBUG
745 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: After pkt_list allocation.\n");
746 #endif
747 if(hold == NULL) {
748 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for pkt_list.\n");
749 vfree(curr_table->table);
750 vfree(curr_table);
751 return 0;
752 }
753 for(c = 0; c < ip_list_tot; c++) {
754 curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot;
755 }
756
757 /* Allocate memory for the hash table */
758 #ifdef DEBUG
759 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for hash_table.\n",
760 sizeof(int)*ip_list_hash_size);
761 #endif
762
763 curr_table->hash_table = vmalloc(sizeof(int)*ip_list_hash_size);
764 if(!curr_table->hash_table) {
765 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for hash_table.\n");
766 vfree(hold);
767 vfree(curr_table->table);
768 vfree(curr_table);
769 return 0;
770 }
771
772 for(c = 0; c < ip_list_hash_size; c++) {
773 curr_table->hash_table[c] = -1;
774 }
775
776 /* Allocate memory for the time info */
777 #ifdef DEBUG
778 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for time_info.\n",
779 sizeof(struct time_info_list)*ip_list_tot);
780 #endif
781
782 curr_table->time_info = vmalloc(sizeof(struct time_info_list)*ip_list_tot);
783 if(!curr_table->time_info) {
784 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for time_info.\n");
785 vfree(curr_table->hash_table);
786 vfree(hold);
787 vfree(curr_table->table);
788 vfree(curr_table);
789 return 0;
790 }
791 for(c = 0; c < ip_list_tot; c++) {
792 curr_table->time_info[c].position = c;
793 curr_table->time_info[c].time = 0;
794 }
795
796 /* Put the new table in place */
797 spin_lock_bh(&recent_lock);
798 find_table = r_tables;
799 while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
800
801 /* If a table already exists just increment the count on that table and return */
802 if(find_table) {
803 find_table->count++;
804 spin_unlock_bh(&recent_lock);
805 #ifdef DEBUG
806 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), created by other process.\n",info->name);
807 #endif
808 vfree(curr_table->time_info);
809 vfree(curr_table->hash_table);
810 vfree(hold);
811 vfree(curr_table->table);
812 vfree(curr_table);
813 return 1;
814 }
815 if(!last_table) r_tables = curr_table; else last_table->next = curr_table;
816
817 spin_unlock_bh(&recent_lock);
818
819 #ifdef CONFIG_PROC_FS
820 /* Create our proc 'status' entry. */
821 curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
822 if (!curr_table->status_proc) {
823 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
824 /* Destroy the created table */
825 spin_lock_bh(&recent_lock);
826 last_table = NULL;
827 curr_table = r_tables;
828 if(!curr_table) {
829 #ifdef DEBUG
830 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, no tables.\n");
831 #endif
832 spin_unlock_bh(&recent_lock);
833 return 0;
834 }
835 while( strncmp(