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. */
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>
17 #include <linux/vmalloc.h>
18 #include <linux/moduleparam.h>
20 #include <linux/netfilter_ipv4/ip_tables.h>
21 #include <linux/netfilter_ipv4/ipt_recent.h>
26 /* Defaults, these can be overridden on the module command-line. */
27 static int ip_list_tot
= 100;
28 static int ip_pkt_list_tot
= 20;
29 static int ip_list_hash_size
= 0;
30 static int ip_list_perms
= 0644;
35 static char version
[] =
36 KERN_INFO RECENT_NAME
" " RECENT_VER
": Stephen Frost <sfrost@snowman.net>. http://snowman.net/projects/ipt_recent/\n";
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
, int, 0400);
42 module_param(ip_pkt_list_tot
, int, 0400);
43 module_param(ip_list_hash_size
, int, 0400);
44 module_param(ip_list_perms
, int, 0400);
46 module_param(debug
, int, 0600);
47 MODULE_PARM_DESC(debug
,"debugging level, defaults to 1");
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");
54 /* Structure of our list of recently seen addresses. */
55 struct recent_ip_list
{
58 unsigned long last_seen
;
59 unsigned long *last_pkts
;
65 struct time_info_list
{
70 /* Structure of our linked list of tables of recent lists. */
71 struct recent_ip_tables
{
72 char name
[IPT_RECENT_NAME_LEN
];
75 struct recent_ip_list
*table
;
76 struct recent_ip_tables
*next
;
79 struct time_info_list
*time_info
;
81 struct proc_dir_entry
*status_proc
;
82 #endif /* CONFIG_PROC_FS */
85 /* Our current list of addresses we have recently seen.
86 * Only added to on a --set, and only updated on --set || --update
88 static struct recent_ip_tables
*r_tables
= NULL
;
90 /* We protect r_list with this spinlock so two processors are not modifying
91 * the list at the same time.
93 static DEFINE_SPINLOCK(recent_lock
);
96 /* Our /proc/net/ipt_recent entry */
97 static struct proc_dir_entry
*proc_net_ipt_recent
= NULL
;
100 /* Function declaration for later. */
102 match(const struct sk_buff
*skb
,
103 const struct net_device
*in
,
104 const struct net_device
*out
,
105 const void *matchinfo
,
109 /* Function to hash a given address into the hash table of table_size size */
110 static int hash_func(unsigned int addr
, int table_size
)
113 unsigned int value
= addr
;
114 do { result
^= value
; } while((value
>>= HASH_LOG
));
117 if(debug
) printk(KERN_INFO RECENT_NAME
": %d = hash_func(%u,%d)\n",
118 result
& (table_size
- 1),
123 return(result
& (table_size
- 1));
126 #ifdef CONFIG_PROC_FS
127 /* This is the function which produces the output for our /proc output
128 * interface which lists each IP address, the last seen time and the
129 * other recent times the address was seen.
132 static int ip_recent_get_info(char *buffer
, char **start
, off_t offset
, int length
, int *eof
, void *data
)
134 int len
= 0, count
, last_len
= 0, pkt_count
;
137 struct recent_ip_tables
*curr_table
;
139 curr_table
= (struct recent_ip_tables
*) data
;
141 spin_lock_bh(&curr_table
->list_lock
);
142 for(count
= 0; count
< ip_list_tot
; count
++) {
143 if(!curr_table
->table
[count
].addr
) continue;
145 len
+= sprintf(buffer
+len
,"src=%u.%u.%u.%u ",NIPQUAD(curr_table
->table
[count
].addr
));
146 len
+= sprintf(buffer
+len
,"ttl: %u ",curr_table
->table
[count
].ttl
);
147 len
+= sprintf(buffer
+len
,"last_seen: %lu ",curr_table
->table
[count
].last_seen
);
148 len
+= sprintf(buffer
+len
,"oldest_pkt: %u ",curr_table
->table
[count
].oldest_pkt
);
149 len
+= sprintf(buffer
+len
,"last_pkts: %lu",curr_table
->table
[count
].last_pkts
[0]);
150 for(pkt_count
= 1; pkt_count
< ip_pkt_list_tot
; pkt_count
++) {
151 if(!curr_table
->table
[count
].last_pkts
[pkt_count
]) break;
152 len
+= sprintf(buffer
+len
,", %lu",curr_table
->table
[count
].last_pkts
[pkt_count
]);
154 len
+= sprintf(buffer
+len
,"\n");
156 if(pos
< offset
) { len
= 0; begin
= pos
; }
157 if(pos
> offset
+ length
) { len
= last_len
; break; }
160 *start
= buffer
+ (offset
- begin
);
161 len
-= (offset
- begin
);
162 if(len
> length
) len
= length
;
164 spin_unlock_bh(&curr_table
->list_lock
);
168 /* ip_recent_ctrl provides an interface for users to modify the table
169 * directly. This allows adding entries, removing entries, and
170 * flushing the entire table.
171 * This is done by opening up the appropriate table for writing and
173 * xx.xx.xx.xx -- Add entry to table with current time
174 * +xx.xx.xx.xx -- Add entry to table with current time
175 * -xx.xx.xx.xx -- Remove entry from table
176 * clear -- Flush table, remove all entries
179 static int ip_recent_ctrl(struct file
*file
, const char __user
*input
, unsigned long size
, void *data
)
181 static const u_int32_t max
[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
189 uint8_t *pp
= res
.bytes
;
193 int len
, check_set
= 0, count
;
196 struct ipt_recent_info
*info
;
197 struct recent_ip_tables
*curr_table
;
199 curr_table
= (struct recent_ip_tables
*) data
;
201 if(size
> 20) len
= 20; else len
= size
;
203 if(copy_from_user(buffer
,input
,len
)) return -EFAULT
;
205 if(len
< 20) buffer
[len
] = '\0';
208 if(debug
) printk(KERN_INFO RECENT_NAME
": ip_recent_ctrl len: %d, input: `%.20s'\n",len
,buffer
);
212 while(isspace(*cp
)) { cp
++; used
++; if(used
>= len
-5) return used
; }
214 /* Check if we are asked to flush the entire table */
215 if(!memcmp(cp
,"clear",5)) {
217 spin_lock_bh(&curr_table
->list_lock
);
218 curr_table
->time_pos
= 0;
219 for(count
= 0; count
< ip_list_hash_size
; count
++) {
220 curr_table
->hash_table
[count
] = -1;
222 for(count
= 0; count
< ip_list_tot
; count
++) {
223 curr_table
->table
[count
].last_seen
= 0;
224 curr_table
->table
[count
].addr
= 0;
225 curr_table
->table
[count
].ttl
= 0;
226 memset(curr_table
->table
[count
].last_pkts
,0,ip_pkt_list_tot
*sizeof(u_int32_t
));
227 curr_table
->table
[count
].oldest_pkt
= 0;
228 curr_table
->table
[count
].time_pos
= 0;
229 curr_table
->time_info
[count
].position
= count
;
230 curr_table
->time_info
[count
].time
= 0;
232 spin_unlock_bh(&curr_table
->list_lock
);
236 check_set
= IPT_RECENT_SET
;
238 case '+': check_set
= IPT_RECENT_SET
; cp
++; used
++; break;
239 case '-': check_set
= IPT_RECENT_REMOVE
; cp
++; used
++; break;
240 default: if(!isdigit(*cp
)) return (used
+1); break;
244 if(debug
) printk(KERN_INFO RECENT_NAME
": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp
,check_set
);
246 /* Get addr (effectively inet_aton()) */
247 /* Shamelessly stolen from libc, a function in the kernel for doing
248 * this would, of course, be greatly preferred, but our options appear
249 * to be rather limited, so we will just do it ourselves here.
255 if(!isdigit(c
)) return used
;
256 val
= 0; base
= 10; digit
= 0;
259 if(c
== 'x' || c
== 'X') base
= 16, c
= *++cp
;
260 else { base
= 8; digit
= 1; }
263 if(isascii(c
) && isdigit(c
)) {
264 if(base
== 8 && (c
== '8' || c
== '0')) return used
;
265 val
= (val
* base
) + (c
- '0');
268 } else if(base
== 16 && isascii(c
) && isxdigit(c
)) {
269 val
= (val
<< 4) | (c
+ 10 - (islower(c
) ? 'a' : 'A'));
275 if(pp
> res
.bytes
+ 2 || val
> 0xff) return used
;
281 if(c
!= '\0' && (!isascii(c
) || !isspace(c
))) return used
;
282 if(c
== '\n') used
++;
283 if(!digit
) return used
;
285 if(val
> max
[pp
- res
.bytes
]) return used
;
286 addr
= res
.word
| htonl(val
);
288 if(!addr
&& check_set
== IPT_RECENT_SET
) return used
;
291 if(debug
) printk(KERN_INFO RECENT_NAME
": ip_recent_ctrl c: %c, addr: %u used: %d\n",c
,addr
,used
);
294 /* Set up and just call match */
295 info
= kmalloc(sizeof(struct ipt_recent_info
),GFP_KERNEL
);
296 if(!info
) { return -ENOMEM
; }
299 info
->check_set
= check_set
;
301 info
->side
= IPT_RECENT_SOURCE
;
302 strncpy(info
->name
,curr_table
->name
,IPT_RECENT_NAME_LEN
);
303 info
->name
[IPT_RECENT_NAME_LEN
-1] = '\0';
305 skb
= kmalloc(sizeof(struct sk_buff
),GFP_KERNEL
);
310 skb
->nh
.iph
= kmalloc(sizeof(struct iphdr
),GFP_KERNEL
);
316 skb
->nh
.iph
->saddr
= addr
;
317 skb
->nh
.iph
->daddr
= 0;
318 /* Clear ttl since we have no way of knowing it */
319 skb
->nh
.iph
->ttl
= 0;
320 match(skb
,NULL
,NULL
,info
,0,NULL
);
329 if(debug
) printk(KERN_INFO RECENT_NAME
": Leaving ip_recent_ctrl addr: %u used: %d\n",addr
,used
);
334 #endif /* CONFIG_PROC_FS */
336 /* 'match' is our primary function, called by the kernel whenever a rule is
337 * hit with our module as an option to it.
338 * What this function does depends on what was specifically asked of it by
340 * --set -- Add or update last seen time of the source address of the packet
341 * -- matchinfo->check_set == IPT_RECENT_SET
342 * --rcheck -- Just check if the source address is in the list
343 * -- matchinfo->check_set == IPT_RECENT_CHECK
344 * --update -- If the source address is in the list, update last_seen
345 * -- matchinfo->check_set == IPT_RECENT_UPDATE
346 * --remove -- If the source address is in the list, remove it
347 * -- matchinfo->check_set == IPT_RECENT_REMOVE
348 * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
349 * -- matchinfo->seconds
350 * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
351 * -- matchinfo->hit_count
352 * --seconds and --hitcount can be combined
355 match(const struct sk_buff
*skb
,
356 const struct net_device
*in
,
357 const struct net_device
*out
,
358 const void *matchinfo
,
362 int pkt_count
, hits_found
, ans
;
364 const struct ipt_recent_info
*info
= matchinfo
;
365 u_int32_t addr
= 0, time_temp
;
366 u_int8_t ttl
= skb
->nh
.iph
->ttl
;
368 int orig_hash_result
, hash_result
, temp
, location
= 0, time_loc
, end_collision_chain
= -1;
369 struct time_info_list
*time_info
;
370 struct recent_ip_tables
*curr_table
;
371 struct recent_ip_tables
*last_table
;
372 struct recent_ip_list
*r_list
;
375 if(debug
) printk(KERN_INFO RECENT_NAME
": match() called\n");
378 /* Default is false ^ info->invert */
382 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): name = '%s'\n",info
->name
);
385 /* if out != NULL then routing has been done and TTL changed.
386 * We change it back here internally for match what came in before routing. */
389 /* Find the right table */
390 spin_lock_bh(&recent_lock
);
391 curr_table
= r_tables
;
392 while( (last_table
= curr_table
) && strncmp(info
->name
,curr_table
->name
,IPT_RECENT_NAME_LEN
) && (curr_table
= curr_table
->next
) );
395 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): table found('%s')\n",info
->name
);
398 spin_unlock_bh(&recent_lock
);
400 /* Table with this name not found, match impossible */
401 if(!curr_table
) { return ans
; }
403 /* Make sure no one is changing the list while we work with it */
404 spin_lock_bh(&curr_table
->list_lock
);
406 r_list
= curr_table
->table
;
407 if(info
->side
== IPT_RECENT_DEST
) addr
= skb
->nh
.iph
->daddr
; else addr
= skb
->nh
.iph
->saddr
;
411 if(debug
) printk(KERN_INFO RECENT_NAME
": match() address (%u) invalid, leaving.\n",addr
);
413 spin_unlock_bh(&curr_table
->list_lock
);
418 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 /* Get jiffies now in case they changed while we were waiting for a lock */
423 hash_table
= curr_table
->hash_table
;
424 time_info
= curr_table
->time_info
;
426 orig_hash_result
= hash_result
= hash_func(addr
,ip_list_hash_size
);
427 /* Hash entry at this result used */
428 /* Check for TTL match if requested. If TTL is zero then a match would never
429 * happen, so match regardless of existing TTL in that case. Zero means the
430 * entry was added via the /proc interface anyway, so we will just use the
431 * first TTL we get for that IP address. */
432 if(info
->check_set
& IPT_RECENT_TTL
) {
433 while(hash_table
[hash_result
] != -1 && !(r_list
[hash_table
[hash_result
]].addr
== addr
&&
434 (!r_list
[hash_table
[hash_result
]].ttl
|| r_list
[hash_table
[hash_result
]].ttl
== ttl
))) {
435 /* Collision in hash table */
436 hash_result
= (hash_result
+ 1) % ip_list_hash_size
;
439 while(hash_table
[hash_result
] != -1 && r_list
[hash_table
[hash_result
]].addr
!= addr
) {
440 /* Collision in hash table */
441 hash_result
= (hash_result
+ 1) % ip_list_hash_size
;
445 if(hash_table
[hash_result
] == -1 && !(info
->check_set
& IPT_RECENT_SET
)) {
446 /* IP not in list and not asked to SET */
447 spin_unlock_bh(&curr_table
->list_lock
);
451 /* Check if we need to handle the collision, do not need to on REMOVE */
452 if(orig_hash_result
!= hash_result
&& !(info
->check_set
& IPT_RECENT_REMOVE
)) {
454 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
457 r_list
[hash_table
[orig_hash_result
]].addr
,
461 /* We had a collision.
462 * orig_hash_result is where we started, hash_result is where we ended up.
463 * So, swap them because we are likely to see the same guy again sooner */
466 printk(KERN_INFO RECENT_NAME
": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table
[orig_hash_result
]);
467 printk(KERN_INFO RECENT_NAME
": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
468 r_list
[hash_table
[orig_hash_result
]].hash_entry
);
472 r_list
[hash_table
[orig_hash_result
]].hash_entry
= hash_result
;
475 temp
= hash_table
[orig_hash_result
];
477 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): Collision; hash_table[hash_result] = %d\n",hash_table
[hash_result
]);
479 hash_table
[orig_hash_result
] = hash_table
[hash_result
];
480 hash_table
[hash_result
] = temp
;
482 hash_result
= orig_hash_result
;
483 orig_hash_result
= temp
;
484 time_info
[r_list
[hash_table
[orig_hash_result
]].time_pos
].position
= hash_table
[orig_hash_result
];
485 if(hash_table
[hash_result
] != -1) {
486 r_list
[hash_table
[hash_result
]].hash_entry
= hash_result
;
487 time_info
[r_list
[hash_table
[hash_result
]].time_pos
].position
= hash_table
[hash_result
];
491 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): Collision handled.\n");
495 if(hash_table
[hash_result
] == -1) {
497 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): New table entry. (hr: %d,ha: %u)\n",
501 /* New item found and IPT_RECENT_SET, so we need to add it */
502 location
= time_info
[curr_table
->time_pos
].position
;
503 hash_table
[r_list
[location
].hash_entry
] = -1;
504 hash_table
[hash_result
] = location
;
505 memset(r_list
[location
].last_pkts
,0,ip_pkt_list_tot
*sizeof(u_int32_t
));
506 r_list
[location
].time_pos
= curr_table
->time_pos
;
507 r_list
[location
].addr
= addr
;
508 r_list
[location
].ttl
= ttl
;
509 r_list
[location
].last_seen
= now
;
510 r_list
[location
].oldest_pkt
= 1;
511 r_list
[location
].last_pkts
[0] = now
;
512 r_list
[location
].hash_entry
= hash_result
;
513 time_info
[curr_table
->time_pos
].time
= r_list
[location
].last_seen
;
514 curr_table
->time_pos
= (curr_table
->time_pos
+ 1) % ip_list_tot
;
519 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): Existing table entry. (hr: %d,ha: %u)\n",
524 /* Existing item found */
525 location
= hash_table
[hash_result
];
526 /* We have a match on address, now to make sure it meets all requirements for a
528 if(info
->check_set
& IPT_RECENT_CHECK
|| info
->check_set
& IPT_RECENT_UPDATE
) {
529 if(!info
->seconds
&& !info
->hit_count
) ans
= !info
->invert
; else ans
= info
->invert
;
530 if(info
->seconds
&& !info
->hit_count
) {
531 if(time_before_eq(now
,r_list
[location
].last_seen
+info
->seconds
*HZ
)) ans
= !info
->invert
; else ans
= info
->invert
;
533 if(info
->seconds
&& info
->hit_count
) {
534 for(pkt_count
= 0, hits_found
= 0; pkt_count
< ip_pkt_list_tot
; pkt_count
++) {
535 if(time_before_eq(now
,r_list
[location
].last_pkts
[pkt_count
]+info
->seconds
*HZ
)) hits_found
++;
537 if(hits_found
>= info
->hit_count
) ans
= !info
->invert
; else ans
= info
->invert
;
539 if(info
->hit_count
&& !info
->seconds
) {
540 for(pkt_count
= 0, hits_found
= 0; pkt_count
< ip_pkt_list_tot
; pkt_count
++) {
541 if(r_list
[location
].last_pkts
[pkt_count
] == 0) break;
544 if(hits_found
>= info
->hit_count
) ans
= !info
->invert
; else ans
= info
->invert
;
550 printk(KERN_INFO RECENT_NAME
": match(): match addr: %u\n",addr
);
552 printk(KERN_INFO RECENT_NAME
": match(): no match addr: %u\n",addr
);
556 /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
557 * current timestamp to the last_seen. */
558 if((info
->check_set
& IPT_RECENT_SET
&& (ans
= !info
->invert
)) || (info
->check_set
& IPT_RECENT_UPDATE
&& ans
)) {
560 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): SET or UPDATE; updating time info.\n");
562 /* Have to update our time info */
563 time_loc
= r_list
[location
].time_pos
;
564 time_info
[time_loc
].time
= now
;
565 time_info
[time_loc
].position
= location
;
566 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
) {
567 time_temp
= time_info
[time_loc
].time
;
568 time_info
[time_loc
].time
= time_info
[(time_loc
+1)%ip_list_tot
].time
;
569 time_info
[(time_loc
+1)%ip_list_tot
].time
= time_temp
;
570 time_temp
= time_info
[time_loc
].position
;
571 time_info
[time_loc
].position
= time_info
[(time_loc
+1)%ip_list_tot
].position
;
572 time_info
[(time_loc
+1)%ip_list_tot
].position
= time_temp
;
573 r_list
[time_info
[time_loc
].position
].time_pos
= time_loc
;
574 r_list
[time_info
[(time_loc
+1)%ip_list_tot
].position
].time_pos
= (time_loc
+1)%ip_list_tot
;
575 time_loc
= (time_loc
+1) % ip_list_tot
;
577 r_list
[location
].time_pos
= time_loc
;
578 r_list
[location
].ttl
= ttl
;
579 r_list
[location
].last_pkts
[r_list
[location
].oldest_pkt
] = now
;
580 r_list
[location
].oldest_pkt
= ++r_list
[location
].oldest_pkt
% ip_pkt_list_tot
;
581 r_list
[location
].last_seen
= now
;
583 /* If we have been asked to remove the entry from the list, just set it to 0 */
584 if(info
->check_set
& IPT_RECENT_REMOVE
) {
586 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result
,hash_result
);
588 /* Check if this is part of a collision chain */
589 while(hash_table
[(orig_hash_result
+1) % ip_list_hash_size
] != -1) {
591 if(hash_func(r_list
[hash_table
[orig_hash_result
]].addr
,ip_list_hash_size
) == hash_result
) {
592 /* Found collision chain, how deep does this rabbit hole go? */
594 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): REMOVE; found collision chain.\n");
596 end_collision_chain
= orig_hash_result
;
599 if(end_collision_chain
!= -1) {
601 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): REMOVE; part of collision chain, moving to end.\n");
603 /* Part of a collision chain, swap it with the end of the chain
604 * before removing. */
605 r_list
[hash_table
[end_collision_chain
]].hash_entry
= hash_result
;
606 temp
= hash_table
[end_collision_chain
];
607 hash_table
[end_collision_chain
] = hash_table
[hash_result
];
608 hash_table
[hash_result
] = temp
;
609 time_info
[r_list
[hash_table
[hash_result
]].time_pos
].position
= hash_table
[hash_result
];
610 hash_result
= end_collision_chain
;
611 r_list
[hash_table
[hash_result
]].hash_entry
= hash_result
;
612 time_info
[r_list
[hash_table
[hash_result
]].time_pos
].position
= hash_table
[hash_result
];
614 location
= hash_table
[hash_result
];
615 hash_table
[r_list
[location
].hash_entry
] = -1;
616 time_loc
= r_list
[location
].time_pos
;
617 time_info
[time_loc
].time
= 0;
618 time_info
[time_loc
].position
= location
;
619 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
) {
620 time_temp
= time_info
[time_loc
].time
;
621 time_info
[time_loc
].time
= time_info
[(time_loc
+1)%ip_list_tot
].time
;
622 time_info
[(time_loc
+1)%ip_list_tot
].time
= time_temp
;
623 time_temp
= time_info
[time_loc
].position
;
624 time_info
[time_loc
].position
= time_info
[(time_loc
+1)%ip_list_tot
].position
;
625 time_info
[(time_loc
+1)%ip_list_tot
].position
= time_temp
;
626 r_list
[time_info
[time_loc
].position
].time_pos
= time_loc
;
627 r_list
[time_info
[(time_loc
+1)%ip_list_tot
].position
].time_pos
= (time_loc
+1)%ip_list_tot
;
628 time_loc
= (time_loc
+1) % ip_list_tot
;
630 r_list
[location
].time_pos
= time_loc
;
631 r_list
[location
].last_seen
= 0;
632 r_list
[location
].addr
= 0;
633 r_list
[location
].ttl
= 0;
634 memset(r_list
[location
].last_pkts
,0,ip_pkt_list_tot
*sizeof(u_int32_t
));
635 r_list
[location
].oldest_pkt
= 0;
638 spin_unlock_bh(&curr_table
->list_lock
);
642 spin_unlock_bh(&curr_table
->list_lock
);
644 if(debug
) printk(KERN_INFO RECENT_NAME
": match() left.\n");
649 /* This function is to verify that the rule given during the userspace iptables
650 * command is correct.
651 * If the command is valid then we check if the table name referred to by the
652 * rule exists, if not it is created.
655 checkentry(const char *tablename
,
656 const struct ipt_ip
*ip
,
658 unsigned int matchsize
,
659 unsigned int hook_mask
)
663 const struct ipt_recent_info
*info
= matchinfo
;
664 struct recent_ip_tables
*curr_table
, *find_table
, *last_table
;
667 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry() entered.\n");
670 if (matchsize
!= IPT_ALIGN(sizeof(struct ipt_recent_info
))) return 0;
672 /* seconds and hit_count only valid for CHECK/UPDATE */
673 if(info
->check_set
& IPT_RECENT_SET
) { flag
++; if(info
->seconds
|| info
->hit_count
) return 0; }
674 if(info
->check_set
& IPT_RECENT_REMOVE
) { flag
++; if(info
->seconds
|| info
->hit_count
) return 0; }
675 if(info
->check_set
& IPT_RECENT_CHECK
) flag
++;
676 if(info
->check_set
& IPT_RECENT_UPDATE
) flag
++;
678 /* One and only one of these should ever be set */
679 if(flag
!= 1) return 0;
681 /* Name must be set to something */
682 if(!info
->name
|| !info
->name
[0]) return 0;
684 /* Things look good, create a list for this if it does not exist */
685 /* Lock the linked list while we play with it */
686 spin_lock_bh(&recent_lock
);
688 /* Look for an entry with this name already created */
689 /* Finds the end of the list and the entry before the end if current name does not exist */
690 find_table
= r_tables
;
691 while( (last_table
= find_table
) && strncmp(info
->name
,find_table
->name
,IPT_RECENT_NAME_LEN
) && (find_table
= find_table
->next
) );
693 /* If a table already exists just increment the count on that table and return */
696 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: table found (%s), incrementing count.\n",info
->name
);
699 spin_unlock_bh(&recent_lock
);
703 spin_unlock_bh(&recent_lock
);
705 /* Table with this name not found */
706 /* Allocate memory for new linked list item */
710 printk(KERN_INFO RECENT_NAME
": checkentry: no table found (%s)\n",info
->name
);
711 printk(KERN_INFO RECENT_NAME
": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables
));
715 curr_table
= vmalloc(sizeof(struct recent_ip_tables
));
716 if(curr_table
== NULL
) return 0;
718 spin_lock_init(&curr_table
->list_lock
);
719 curr_table
->next
= NULL
;
720 curr_table
->count
= 1;
721 curr_table
->time_pos
= 0;
722 strncpy(curr_table
->name
,info
->name
,IPT_RECENT_NAME_LEN
);
723 curr_table
->name
[IPT_RECENT_NAME_LEN
-1] = '\0';
725 /* Allocate memory for this table and the list of packets in each entry. */
727 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: Allocating %d for table (%s).\n",
728 sizeof(struct recent_ip_list
)*ip_list_tot
,
732 curr_table
->table
= vmalloc(sizeof(struct recent_ip_list
)*ip_list_tot
);
733 if(curr_table
->table
== NULL
) { vfree(curr_table
); return 0; }
734 memset(curr_table
->table
,0,sizeof(struct recent_ip_list
)*ip_list_tot
);
736 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: Allocating %d for pkt_list.\n",
737 sizeof(u_int32_t
)*ip_pkt_list_tot
*ip_list_tot
);
740 hold
= vmalloc(sizeof(u_int32_t
)*ip_pkt_list_tot
*ip_list_tot
);
742 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: After pkt_list allocation.\n");
745 printk(KERN_INFO RECENT_NAME
": checkentry: unable to allocate for pkt_list.\n");
746 vfree(curr_table
->table
);
750 for(c
= 0; c
< ip_list_tot
; c
++) {
751 curr_table
->table
[c
].last_pkts
= hold
+ c
*ip_pkt_list_tot
;
754 /* Allocate memory for the hash table */
756 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: Allocating %d for hash_table.\n",
757 sizeof(int)*ip_list_hash_size
);
760 curr_table
->hash_table
= vmalloc(sizeof(int)*ip_list_hash_size
);
761 if(!curr_table
->hash_table
) {
762 printk(KERN_INFO RECENT_NAME
": checkentry: unable to allocate for hash_table.\n");
764 vfree(curr_table
->table
);
769 for(c
= 0; c
< ip_list_hash_size
; c
++) {
770 curr_table
->hash_table
[c
] = -1;
773 /* Allocate memory for the time info */
775 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: Allocating %d for time_info.\n",
776 sizeof(struct time_info_list
)*ip_list_tot
);
779 curr_table
->time_info
= vmalloc(sizeof(struct time_info_list
)*ip_list_tot
);
780 if(!curr_table
->time_info
) {
781 printk(KERN_INFO RECENT_NAME
": checkentry: unable to allocate for time_info.\n");
782 vfree(curr_table
->hash_table
);
784 vfree(curr_table
->table
);
788 for(c
= 0; c
< ip_list_tot
; c
++) {
789 curr_table
->time_info
[c
].position
= c
;
790 curr_table
->time_info
[c
].time
= 0;
793 /* Put the new table in place */
794 spin_lock_bh(&recent_lock
);
795 find_table
= r_tables
;
796 while( (last_table
= find_table
) && strncmp(info
->name
,find_table
->name
,IPT_RECENT_NAME_LEN
) && (find_table
= find_table
->next
) );
798 /* If a table already exists just increment the count on that table and return */
801 spin_unlock_bh(&recent_lock
);
803 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: table found (%s), created by other process.\n",info
->name
);
805 vfree(curr_table
->time_info
);
806 vfree(curr_table
->hash_table
);
808 vfree(curr_table
->table
);
812 if(!last_table
) r_tables
= curr_table
; else last_table
->next
= curr_table
;
814 spin_unlock_bh(&recent_lock
);
816 #ifdef CONFIG_PROC_FS
817 /* Create our proc 'status' entry. */
818 curr_table
->status_proc
= create_proc_entry(curr_table
->name
, ip_list_perms
, proc_net_ipt_recent
);
819 if (!curr_table
->status_proc
) {
820 printk(KERN_INFO RECENT_NAME
": checkentry: unable to allocate for /proc entry.\n");
821 /* Destroy the created table */
822 spin_lock_bh(&recent_lock
);
824 curr_table
= r_tables
;
827 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry() create_proc failed, no tables.\n");
829 spin_unlock_bh(&recent_lock
);
832 while( strncmp(info
->name
,curr_table
->name
,IPT_RECENT_NAME_LEN
) && (last_table
= curr_table
) && (curr_table
= curr_table
->next
) );
835 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry() create_proc failed, table already destroyed.\n");
837 spin_unlock_bh(&recent_lock
);
840 if(last_table
) last_table
->next
= curr_table
->next
; else r_tables
= curr_table
->next
;
841 spin_unlock_bh(&recent_lock
);
842 vfree(curr_table
->time_info
);
843 vfree(curr_table
->hash_table
);
845 vfree(curr_table
->table
);
850 curr_table
->status_proc
->owner
= THIS_MODULE
;
851 curr_table
->status_proc
->data
= curr_table
;
853 curr_table
->status_proc
->read_proc
= ip_recent_get_info
;
854 curr_table
->status_proc
->write_proc
= ip_recent_ctrl
;
855 #endif /* CONFIG_PROC_FS */
858 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry() left.\n");
864 /* This function is called in the event that a rule matching this module is
866 * When this happens we need to check if there are no other rules matching
867 * the table given. If that is the case then we remove the table and clean
871 destroy(void *matchinfo
, unsigned int matchsize
)
873 const struct ipt_recent_info
*info
= matchinfo
;
874 struct recent_ip_tables
*curr_table
, *last_table
;
877 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() entered.\n");
880 if(matchsize
!= IPT_ALIGN(sizeof(struct ipt_recent_info
))) return;
882 /* Lock the linked list while we play with it */
883 spin_lock_bh(&recent_lock
);
885 /* Look for an entry with this name already created */
886 /* Finds the end of the list and the entry before the end if current name does not exist */
888 curr_table
= r_tables
;
891 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() No tables found, leaving.\n");
893 spin_unlock_bh(&recent_lock
);
896 while( strncmp(info
->name
,curr_table
->name
,IPT_RECENT_NAME_LEN
) && (last_table
= curr_table
) && (curr_table
= curr_table
->next
) );
898 /* If a table does not exist then do nothing and return */
901 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() table not found, leaving.\n");
903 spin_unlock_bh(&recent_lock
);
909 /* If count is still non-zero then there are still rules referenceing it so we do nothing */
910 if(curr_table
->count
) {
912 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() table found, non-zero count, leaving.\n");
914 spin_unlock_bh(&recent_lock
);
919 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() table found, zero count, removing.\n");
922 /* Count must be zero so we remove this table from the list */
923 if(last_table
) last_table
->next
= curr_table
->next
; else r_tables
= curr_table
->next
;
925 spin_unlock_bh(&recent_lock
);
927 /* lock to make sure any late-runners still using this after we removed it from
928 * the list finish up then remove everything */
929 spin_lock_bh(&curr_table
->list_lock
);
930 spin_unlock_bh(&curr_table
->list_lock
);
932 #ifdef CONFIG_PROC_FS
933 if(curr_table
->status_proc
) remove_proc_entry(curr_table
->name
,proc_net_ipt_recent
);
934 #endif /* CONFIG_PROC_FS */
935 vfree(curr_table
->table
[0].last_pkts
);
936 vfree(curr_table
->table
);
937 vfree(curr_table
->hash_table
);
938 vfree(curr_table
->time_info
);
942 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() left.\n");
948 /* This is the structure we pass to ipt_register to register our
949 * module with iptables.
951 static struct ipt_match recent_match
= {
954 .checkentry
= &checkentry
,
959 /* Kernel module initialization. */
960 static int __init
init(void)
965 #ifdef CONFIG_PROC_FS
966 proc_net_ipt_recent
= proc_mkdir("ipt_recent",proc_net
);
967 if(!proc_net_ipt_recent
) return -ENOMEM
;
970 if(ip_list_hash_size
&& ip_list_hash_size
<= ip_list_tot
) {
971 printk(KERN_WARNING RECENT_NAME
": ip_list_hash_size too small, resetting to default.\n");
972 ip_list_hash_size
= 0;
975 if(!ip_list_hash_size
) {
976 ip_list_hash_size
= ip_list_tot
*3;
978 while(ip_list_hash_size
> count
) count
= count
*2;
979 ip_list_hash_size
= count
;
983 if(debug
) printk(KERN_INFO RECENT_NAME
": ip_list_hash_size: %d\n",ip_list_hash_size
);
986 err
= ipt_register_match(&recent_match
);
988 remove_proc_entry("ipt_recent", proc_net
);
992 /* Kernel module destruction. */
993 static void __exit
fini(void)
995 ipt_unregister_match(&recent_match
);
997 remove_proc_entry("ipt_recent",proc_net
);
1000 /* Register our module with the kernel. */