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 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;
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
, 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);
46 module_param(debug
, bool, 0600);
47 MODULE_PARM_DESC(debug
,"enable debugging output");
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 struct xt_match
*match
,
106 const void *matchinfo
,
108 unsigned int protoff
,
111 /* Function to hash a given address into the hash table of table_size size */
112 static int hash_func(unsigned int addr
, int table_size
)
115 unsigned int value
= addr
;
116 do { result
^= value
; } while((value
>>= HASH_LOG
));
119 if(debug
) printk(KERN_INFO RECENT_NAME
": %d = hash_func(%u,%d)\n",
120 result
& (table_size
- 1),
125 return(result
& (table_size
- 1));
128 #ifdef CONFIG_PROC_FS
129 /* This is the function which produces the output for our /proc output
130 * interface which lists each IP address, the last seen time and the
131 * other recent times the address was seen.
134 static int ip_recent_get_info(char *buffer
, char **start
, off_t offset
, int length
, int *eof
, void *data
)
136 int len
= 0, count
, last_len
= 0, pkt_count
;
139 struct recent_ip_tables
*curr_table
;
141 curr_table
= (struct recent_ip_tables
*) data
;
143 spin_lock_bh(&curr_table
->list_lock
);
144 for(count
= 0; count
< ip_list_tot
; count
++) {
145 if(!curr_table
->table
[count
].addr
) continue;
147 len
+= sprintf(buffer
+len
,"src=%u.%u.%u.%u ",NIPQUAD(curr_table
->table
[count
].addr
));
148 len
+= sprintf(buffer
+len
,"ttl: %u ",curr_table
->table
[count
].ttl
);
149 len
+= sprintf(buffer
+len
,"last_seen: %lu ",curr_table
->table
[count
].last_seen
);
150 len
+= sprintf(buffer
+len
,"oldest_pkt: %u ",curr_table
->table
[count
].oldest_pkt
);
151 len
+= sprintf(buffer
+len
,"last_pkts: %lu",curr_table
->table
[count
].last_pkts
[0]);
152 for(pkt_count
= 1; pkt_count
< ip_pkt_list_tot
; pkt_count
++) {
153 if(!curr_table
->table
[count
].last_pkts
[pkt_count
]) break;
154 len
+= sprintf(buffer
+len
,", %lu",curr_table
->table
[count
].last_pkts
[pkt_count
]);
156 len
+= sprintf(buffer
+len
,"\n");
158 if(pos
< offset
) { len
= 0; begin
= pos
; }
159 if(pos
> offset
+ length
) { len
= last_len
; break; }
162 *start
= buffer
+ (offset
- begin
);
163 len
-= (offset
- begin
);
164 if(len
> length
) len
= length
;
166 spin_unlock_bh(&curr_table
->list_lock
);
170 /* ip_recent_ctrl provides an interface for users to modify the table
171 * directly. This allows adding entries, removing entries, and
172 * flushing the entire table.
173 * This is done by opening up the appropriate table for writing and
175 * xx.xx.xx.xx -- Add entry to table with current time
176 * +xx.xx.xx.xx -- Add entry to table with current time
177 * -xx.xx.xx.xx -- Remove entry from table
178 * clear -- Flush table, remove all entries
181 static int ip_recent_ctrl(struct file
*file
, const char __user
*input
, unsigned long size
, void *data
)
183 static const u_int32_t max
[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
191 uint8_t *pp
= res
.bytes
;
195 int len
, check_set
= 0, count
;
198 struct ipt_recent_info
*info
;
199 struct recent_ip_tables
*curr_table
;
201 curr_table
= (struct recent_ip_tables
*) data
;
203 if(size
> 20) len
= 20; else len
= size
;
205 if(copy_from_user(buffer
,input
,len
)) return -EFAULT
;
207 if(len
< 20) buffer
[len
] = '\0';
210 if(debug
) printk(KERN_INFO RECENT_NAME
": ip_recent_ctrl len: %d, input: `%.20s'\n",len
,buffer
);
214 while(isspace(*cp
)) { cp
++; used
++; if(used
>= len
-5) return used
; }
216 /* Check if we are asked to flush the entire table */
217 if(!memcmp(cp
,"clear",5)) {
219 spin_lock_bh(&curr_table
->list_lock
);
220 curr_table
->time_pos
= 0;
221 for(count
= 0; count
< ip_list_hash_size
; count
++) {
222 curr_table
->hash_table
[count
] = -1;
224 for(count
= 0; count
< ip_list_tot
; count
++) {
225 curr_table
->table
[count
].last_seen
= 0;
226 curr_table
->table
[count
].addr
= 0;
227 curr_table
->table
[count
].ttl
= 0;
228 memset(curr_table
->table
[count
].last_pkts
,0,ip_pkt_list_tot
*sizeof(unsigned long));
229 curr_table
->table
[count
].oldest_pkt
= 0;
230 curr_table
->table
[count
].time_pos
= 0;
231 curr_table
->time_info
[count
].position
= count
;
232 curr_table
->time_info
[count
].time
= 0;
234 spin_unlock_bh(&curr_table
->list_lock
);
238 check_set
= IPT_RECENT_SET
;
240 case '+': check_set
= IPT_RECENT_SET
; cp
++; used
++; break;
241 case '-': check_set
= IPT_RECENT_REMOVE
; cp
++; used
++; break;
242 default: if(!isdigit(*cp
)) return (used
+1); break;
246 if(debug
) printk(KERN_INFO RECENT_NAME
": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp
,check_set
);
248 /* Get addr (effectively inet_aton()) */
249 /* Shamelessly stolen from libc, a function in the kernel for doing
250 * this would, of course, be greatly preferred, but our options appear
251 * to be rather limited, so we will just do it ourselves here.
257 if(!isdigit(c
)) return used
;
258 val
= 0; base
= 10; digit
= 0;
261 if(c
== 'x' || c
== 'X') base
= 16, c
= *++cp
;
262 else { base
= 8; digit
= 1; }
265 if(isascii(c
) && isdigit(c
)) {
266 if(base
== 8 && (c
== '8' || c
== '0')) return used
;
267 val
= (val
* base
) + (c
- '0');
270 } else if(base
== 16 && isascii(c
) && isxdigit(c
)) {
271 val
= (val
<< 4) | (c
+ 10 - (islower(c
) ? 'a' : 'A'));
277 if(pp
> res
.bytes
+ 2 || val
> 0xff) return used
;
283 if(c
!= '\0' && (!isascii(c
) || !isspace(c
))) return used
;
284 if(c
== '\n') used
++;
285 if(!digit
) return used
;
287 if(val
> max
[pp
- res
.bytes
]) return used
;
288 addr
= res
.word
| htonl(val
);
290 if(!addr
&& check_set
== IPT_RECENT_SET
) return used
;
293 if(debug
) printk(KERN_INFO RECENT_NAME
": ip_recent_ctrl c: %c, addr: %u used: %d\n",c
,addr
,used
);
296 /* Set up and just call match */
297 info
= kmalloc(sizeof(struct ipt_recent_info
),GFP_KERNEL
);
298 if(!info
) { return -ENOMEM
; }
301 info
->check_set
= check_set
;
303 info
->side
= IPT_RECENT_SOURCE
;
304 strncpy(info
->name
,curr_table
->name
,IPT_RECENT_NAME_LEN
);
305 info
->name
[IPT_RECENT_NAME_LEN
-1] = '\0';
307 skb
= kmalloc(sizeof(struct sk_buff
),GFP_KERNEL
);
312 skb
->nh
.iph
= kmalloc(sizeof(struct iphdr
),GFP_KERNEL
);
318 skb
->nh
.iph
->saddr
= addr
;
319 skb
->nh
.iph
->daddr
= 0;
320 /* Clear ttl since we have no way of knowing it */
321 skb
->nh
.iph
->ttl
= 0;
322 match(skb
,NULL
,NULL
,NULL
,info
,0,0,NULL
);
331 if(debug
) printk(KERN_INFO RECENT_NAME
": Leaving ip_recent_ctrl addr: %u used: %d\n",addr
,used
);
336 #endif /* CONFIG_PROC_FS */
338 /* 'match' is our primary function, called by the kernel whenever a rule is
339 * hit with our module as an option to it.
340 * What this function does depends on what was specifically asked of it by
342 * --set -- Add or update last seen time of the source address of the packet
343 * -- matchinfo->check_set == IPT_RECENT_SET
344 * --rcheck -- Just check if the source address is in the list
345 * -- matchinfo->check_set == IPT_RECENT_CHECK
346 * --update -- If the source address is in the list, update last_seen
347 * -- matchinfo->check_set == IPT_RECENT_UPDATE
348 * --remove -- If the source address is in the list, remove it
349 * -- matchinfo->check_set == IPT_RECENT_REMOVE
350 * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
351 * -- matchinfo->seconds
352 * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
353 * -- matchinfo->hit_count
354 * --seconds and --hitcount can be combined
357 match(const struct sk_buff
*skb
,
358 const struct net_device
*in
,
359 const struct net_device
*out
,
360 const struct xt_match
*match
,
361 const void *matchinfo
,
363 unsigned int protoff
,
366 int pkt_count
, hits_found
, ans
;
368 const struct ipt_recent_info
*info
= matchinfo
;
369 u_int32_t addr
= 0, time_temp
;
370 u_int8_t ttl
= skb
->nh
.iph
->ttl
;
372 int orig_hash_result
, hash_result
, temp
, location
= 0, time_loc
, end_collision_chain
= -1;
373 struct time_info_list
*time_info
;
374 struct recent_ip_tables
*curr_table
;
375 struct recent_ip_tables
*last_table
;
376 struct recent_ip_list
*r_list
;
379 if(debug
) printk(KERN_INFO RECENT_NAME
": match() called\n");
382 /* Default is false ^ info->invert */
386 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): name = '%s'\n",info
->name
);
389 /* if out != NULL then routing has been done and TTL changed.
390 * We change it back here internally for match what came in before routing. */
393 /* Find the right table */
394 spin_lock_bh(&recent_lock
);
395 curr_table
= r_tables
;
396 while( (last_table
= curr_table
) && strncmp(info
->name
,curr_table
->name
,IPT_RECENT_NAME_LEN
) && (curr_table
= curr_table
->next
) );
399 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): table found('%s')\n",info
->name
);
402 spin_unlock_bh(&recent_lock
);
404 /* Table with this name not found, match impossible */
405 if(!curr_table
) { return ans
; }
407 /* Make sure no one is changing the list while we work with it */
408 spin_lock_bh(&curr_table
->list_lock
);
410 r_list
= curr_table
->table
;
411 if(info
->side
== IPT_RECENT_DEST
) addr
= skb
->nh
.iph
->daddr
; else addr
= skb
->nh
.iph
->saddr
;
415 if(debug
) printk(KERN_INFO RECENT_NAME
": match() address (%u) invalid, leaving.\n",addr
);
417 spin_unlock_bh(&curr_table
->list_lock
);
422 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr
,ttl
,skb
->nh
.iph
->ttl
);
425 /* Get jiffies now in case they changed while we were waiting for a lock */
427 hash_table
= curr_table
->hash_table
;
428 time_info
= curr_table
->time_info
;
430 orig_hash_result
= hash_result
= hash_func(addr
,ip_list_hash_size
);
431 /* Hash entry at this result used */
432 /* Check for TTL match if requested. If TTL is zero then a match would never
433 * happen, so match regardless of existing TTL in that case. Zero means the
434 * entry was added via the /proc interface anyway, so we will just use the
435 * first TTL we get for that IP address. */
436 if(info
->check_set
& IPT_RECENT_TTL
) {
437 while(hash_table
[hash_result
] != -1 && !(r_list
[hash_table
[hash_result
]].addr
== addr
&&
438 (!r_list
[hash_table
[hash_result
]].ttl
|| r_list
[hash_table
[hash_result
]].ttl
== ttl
))) {
439 /* Collision in hash table */
440 hash_result
= (hash_result
+ 1) % ip_list_hash_size
;
443 while(hash_table
[hash_result
] != -1 && r_list
[hash_table
[hash_result
]].addr
!= addr
) {
444 /* Collision in hash table */
445 hash_result
= (hash_result
+ 1) % ip_list_hash_size
;
449 if(hash_table
[hash_result
] == -1 && !(info
->check_set
& IPT_RECENT_SET
)) {
450 /* IP not in list and not asked to SET */
451 spin_unlock_bh(&curr_table
->list_lock
);
455 /* Check if we need to handle the collision, do not need to on REMOVE */
456 if(orig_hash_result
!= hash_result
&& !(info
->check_set
& IPT_RECENT_REMOVE
)) {
458 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
461 r_list
[hash_table
[orig_hash_result
]].addr
,
465 /* We had a collision.
466 * orig_hash_result is where we started, hash_result is where we ended up.
467 * So, swap them because we are likely to see the same guy again sooner */
470 printk(KERN_INFO RECENT_NAME
": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table
[orig_hash_result
]);
471 printk(KERN_INFO RECENT_NAME
": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
472 r_list
[hash_table
[orig_hash_result
]].hash_entry
);
476 r_list
[hash_table
[orig_hash_result
]].hash_entry
= hash_result
;
479 temp
= hash_table
[orig_hash_result
];
481 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): Collision; hash_table[hash_result] = %d\n",hash_table
[hash_result
]);
483 hash_table
[orig_hash_result
] = hash_table
[hash_result
];
484 hash_table
[hash_result
] = temp
;
486 hash_result
= orig_hash_result
;
487 orig_hash_result
= temp
;
488 time_info
[r_list
[hash_table
[orig_hash_result
]].time_pos
].position
= hash_table
[orig_hash_result
];
489 if(hash_table
[hash_result
] != -1) {
490 r_list
[hash_table
[hash_result
]].hash_entry
= hash_result
;
491 time_info
[r_list
[hash_table
[hash_result
]].time_pos
].position
= hash_table
[hash_result
];
495 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): Collision handled.\n");
499 if(hash_table
[hash_result
] == -1) {
501 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): New table entry. (hr: %d,ha: %u)\n",
505 /* New item found and IPT_RECENT_SET, so we need to add it */
506 location
= time_info
[curr_table
->time_pos
].position
;
507 hash_table
[r_list
[location
].hash_entry
] = -1;
508 hash_table
[hash_result
] = location
;
509 memset(r_list
[location
].last_pkts
,0,ip_pkt_list_tot
*sizeof(unsigned long));
510 r_list
[location
].time_pos
= curr_table
->time_pos
;
511 r_list
[location
].addr
= addr
;
512 r_list
[location
].ttl
= ttl
;
513 r_list
[location
].last_seen
= now
;
514 r_list
[location
].oldest_pkt
= 1;
515 r_list
[location
].last_pkts
[0] = now
;
516 r_list
[location
].hash_entry
= hash_result
;
517 time_info
[curr_table
->time_pos
].time
= r_list
[location
].last_seen
;
518 curr_table
->time_pos
= (curr_table
->time_pos
+ 1) % ip_list_tot
;
523 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): Existing table entry. (hr: %d,ha: %u)\n",
528 /* Existing item found */
529 location
= hash_table
[hash_result
];
530 /* We have a match on address, now to make sure it meets all requirements for a
532 if(info
->check_set
& IPT_RECENT_CHECK
|| info
->check_set
& IPT_RECENT_UPDATE
) {
533 if(!info
->seconds
&& !info
->hit_count
) ans
= !info
->invert
; else ans
= info
->invert
;
534 if(info
->seconds
&& !info
->hit_count
) {
535 if(time_before_eq(now
,r_list
[location
].last_seen
+info
->seconds
*HZ
)) ans
= !info
->invert
; else ans
= info
->invert
;
537 if(info
->seconds
&& info
->hit_count
) {
538 for(pkt_count
= 0, hits_found
= 0; pkt_count
< ip_pkt_list_tot
; pkt_count
++) {
539 if(r_list
[location
].last_pkts
[pkt_count
] == 0) break;
540 if(time_before_eq(now
,r_list
[location
].last_pkts
[pkt_count
]+info
->seconds
*HZ
)) hits_found
++;
542 if(hits_found
>= info
->hit_count
) ans
= !info
->invert
; else ans
= info
->invert
;
544 if(info
->hit_count
&& !info
->seconds
) {
545 for(pkt_count
= 0, hits_found
= 0; pkt_count
< ip_pkt_list_tot
; pkt_count
++) {
546 if(r_list
[location
].last_pkts
[pkt_count
] == 0) break;
549 if(hits_found
>= info
->hit_count
) ans
= !info
->invert
; else ans
= info
->invert
;
555 printk(KERN_INFO RECENT_NAME
": match(): match addr: %u\n",addr
);
557 printk(KERN_INFO RECENT_NAME
": match(): no match addr: %u\n",addr
);
561 /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
562 * current timestamp to the last_seen. */
563 if((info
->check_set
& IPT_RECENT_SET
&& (ans
= !info
->invert
)) || (info
->check_set
& IPT_RECENT_UPDATE
&& ans
)) {
565 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): SET or UPDATE; updating time info.\n");
567 /* Have to update our time info */
568 time_loc
= r_list
[location
].time_pos
;
569 time_info
[time_loc
].time
= now
;
570 time_info
[time_loc
].position
= location
;
571 while((time_info
[(time_loc
+1) % ip_list_tot
].time
< time_info
[time_loc
].time
) && ((time_loc
+1) % ip_list_tot
) != curr_table
->time_pos
) {
572 time_temp
= time_info
[time_loc
].time
;
573 time_info
[time_loc
].time
= time_info
[(time_loc
+1)%ip_list_tot
].time
;
574 time_info
[(time_loc
+1)%ip_list_tot
].time
= time_temp
;
575 time_temp
= time_info
[time_loc
].position
;
576 time_info
[time_loc
].position
= time_info
[(time_loc
+1)%ip_list_tot
].position
;
577 time_info
[(time_loc
+1)%ip_list_tot
].position
= time_temp
;
578 r_list
[time_info
[time_loc
].position
].time_pos
= time_loc
;
579 r_list
[time_info
[(time_loc
+1)%ip_list_tot
].position
].time_pos
= (time_loc
+1)%ip_list_tot
;
580 time_loc
= (time_loc
+1) % ip_list_tot
;
582 r_list
[location
].time_pos
= time_loc
;
583 r_list
[location
].ttl
= ttl
;
584 r_list
[location
].last_pkts
[r_list
[location
].oldest_pkt
] = now
;
585 r_list
[location
].oldest_pkt
= ++r_list
[location
].oldest_pkt
% ip_pkt_list_tot
;
586 r_list
[location
].last_seen
= now
;
588 /* If we have been asked to remove the entry from the list, just set it to 0 */
589 if(info
->check_set
& IPT_RECENT_REMOVE
) {
591 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result
,hash_result
);
593 /* Check if this is part of a collision chain */
594 while(hash_table
[(orig_hash_result
+1) % ip_list_hash_size
] != -1) {
596 if(hash_func(r_list
[hash_table
[orig_hash_result
]].addr
,ip_list_hash_size
) == hash_result
) {
597 /* Found collision chain, how deep does this rabbit hole go? */
599 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): REMOVE; found collision chain.\n");
601 end_collision_chain
= orig_hash_result
;
604 if(end_collision_chain
!= -1) {
606 if(debug
) printk(KERN_INFO RECENT_NAME
": match(): REMOVE; part of collision chain, moving to end.\n");
608 /* Part of a collision chain, swap it with the end of the chain
609 * before removing. */
610 r_list
[hash_table
[end_collision_chain
]].hash_entry
= hash_result
;
611 temp
= hash_table
[end_collision_chain
];
612 hash_table
[end_collision_chain
] = hash_table
[hash_result
];
613 hash_table
[hash_result
] = temp
;
614 time_info
[r_list
[hash_table
[hash_result
]].time_pos
].position
= hash_table
[hash_result
];
615 hash_result
= end_collision_chain
;
616 r_list
[hash_table
[hash_result
]].hash_entry
= hash_result
;
617 time_info
[r_list
[hash_table
[hash_result
]].time_pos
].position
= hash_table
[hash_result
];
619 location
= hash_table
[hash_result
];
620 hash_table
[r_list
[location
].hash_entry
] = -1;
621 time_loc
= r_list
[location
].time_pos
;
622 time_info
[time_loc
].time
= 0;
623 time_info
[time_loc
].position
= location
;
624 while((time_info
[(time_loc
+1) % ip_list_tot
].time
< time_info
[time_loc
].time
) && ((time_loc
+1) % ip_list_tot
) != curr_table
->time_pos
) {
625 time_temp
= time_info
[time_loc
].time
;
626 time_info
[time_loc
].time
= time_info
[(time_loc
+1)%ip_list_tot
].time
;
627 time_info
[(time_loc
+1)%ip_list_tot
].time
= time_temp
;
628 time_temp
= time_info
[time_loc
].position
;
629 time_info
[time_loc
].position
= time_info
[(time_loc
+1)%ip_list_tot
].position
;
630 time_info
[(time_loc
+1)%ip_list_tot
].position
= time_temp
;
631 r_list
[time_info
[time_loc
].position
].time_pos
= time_loc
;
632 r_list
[time_info
[(time_loc
+1)%ip_list_tot
].position
].time_pos
= (time_loc
+1)%ip_list_tot
;
633 time_loc
= (time_loc
+1) % ip_list_tot
;
635 r_list
[location
].time_pos
= time_loc
;
636 r_list
[location
].last_seen
= 0;
637 r_list
[location
].addr
= 0;
638 r_list
[location
].ttl
= 0;
639 memset(r_list
[location
].last_pkts
,0,ip_pkt_list_tot
*sizeof(unsigned long));
640 r_list
[location
].oldest_pkt
= 0;
643 spin_unlock_bh(&curr_table
->list_lock
);
647 spin_unlock_bh(&curr_table
->list_lock
);
649 if(debug
) printk(KERN_INFO RECENT_NAME
": match() left.\n");
654 /* This function is to verify that the rule given during the userspace iptables
655 * command is correct.
656 * If the command is valid then we check if the table name referred to by the
657 * rule exists, if not it is created.
660 checkentry(const char *tablename
,
662 const struct xt_match
*match
,
664 unsigned int matchsize
,
665 unsigned int hook_mask
)
669 const struct ipt_recent_info
*info
= matchinfo
;
670 struct recent_ip_tables
*curr_table
, *find_table
, *last_table
;
673 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry() entered.\n");
676 /* seconds and hit_count only valid for CHECK/UPDATE */
677 if(info
->check_set
& IPT_RECENT_SET
) { flag
++; if(info
->seconds
|| info
->hit_count
) return 0; }
678 if(info
->check_set
& IPT_RECENT_REMOVE
) { flag
++; if(info
->seconds
|| info
->hit_count
) return 0; }
679 if(info
->check_set
& IPT_RECENT_CHECK
) flag
++;
680 if(info
->check_set
& IPT_RECENT_UPDATE
) flag
++;
682 /* One and only one of these should ever be set */
683 if(flag
!= 1) return 0;
685 /* Name must be set to something */
686 if(!info
->name
|| !info
->name
[0]) return 0;
688 /* Things look good, create a list for this if it does not exist */
689 /* Lock the linked list while we play with it */
690 spin_lock_bh(&recent_lock
);
692 /* Look for an entry with this name already created */
693 /* Finds the end of the list and the entry before the end if current name does not exist */
694 find_table
= r_tables
;
695 while( (last_table
= find_table
) && strncmp(info
->name
,find_table
->name
,IPT_RECENT_NAME_LEN
) && (find_table
= find_table
->next
) );
697 /* If a table already exists just increment the count on that table and return */
700 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: table found (%s), incrementing count.\n",info
->name
);
703 spin_unlock_bh(&recent_lock
);
707 spin_unlock_bh(&recent_lock
);
709 /* Table with this name not found */
710 /* Allocate memory for new linked list item */
714 printk(KERN_INFO RECENT_NAME
": checkentry: no table found (%s)\n",info
->name
);
715 printk(KERN_INFO RECENT_NAME
": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables
));
719 curr_table
= vmalloc(sizeof(struct recent_ip_tables
));
720 if(curr_table
== NULL
) return 0;
722 spin_lock_init(&curr_table
->list_lock
);
723 curr_table
->next
= NULL
;
724 curr_table
->count
= 1;
725 curr_table
->time_pos
= 0;
726 strncpy(curr_table
->name
,info
->name
,IPT_RECENT_NAME_LEN
);
727 curr_table
->name
[IPT_RECENT_NAME_LEN
-1] = '\0';
729 /* Allocate memory for this table and the list of packets in each entry. */
731 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: Allocating %d for table (%s).\n",
732 sizeof(struct recent_ip_list
)*ip_list_tot
,
736 curr_table
->table
= vmalloc(sizeof(struct recent_ip_list
)*ip_list_tot
);
737 if(curr_table
->table
== NULL
) { vfree(curr_table
); return 0; }
738 memset(curr_table
->table
,0,sizeof(struct recent_ip_list
)*ip_list_tot
);
740 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: Allocating %d for pkt_list.\n",
741 sizeof(unsigned long)*ip_pkt_list_tot
*ip_list_tot
);
744 hold
= vmalloc(sizeof(unsigned long)*ip_pkt_list_tot
*ip_list_tot
);
746 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: After pkt_list allocation.\n");
749 printk(KERN_INFO RECENT_NAME
": checkentry: unable to allocate for pkt_list.\n");
750 vfree(curr_table
->table
);
754 for(c
= 0; c
< ip_list_tot
; c
++) {
755 curr_table
->table
[c
].last_pkts
= hold
+ c
*ip_pkt_list_tot
;
758 /* Allocate memory for the hash table */
760 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: Allocating %d for hash_table.\n",
761 sizeof(int)*ip_list_hash_size
);
764 curr_table
->hash_table
= vmalloc(sizeof(int)*ip_list_hash_size
);
765 if(!curr_table
->hash_table
) {
766 printk(KERN_INFO RECENT_NAME
": checkentry: unable to allocate for hash_table.\n");
768 vfree(curr_table
->table
);
773 for(c
= 0; c
< ip_list_hash_size
; c
++) {
774 curr_table
->hash_table
[c
] = -1;
777 /* Allocate memory for the time info */
779 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: Allocating %d for time_info.\n",
780 sizeof(struct time_info_list
)*ip_list_tot
);
783 curr_table
->time_info
= vmalloc(sizeof(struct time_info_list
)*ip_list_tot
);
784 if(!curr_table
->time_info
) {
785 printk(KERN_INFO RECENT_NAME
": checkentry: unable to allocate for time_info.\n");
786 vfree(curr_table
->hash_table
);
788 vfree(curr_table
->table
);
792 for(c
= 0; c
< ip_list_tot
; c
++) {
793 curr_table
->time_info
[c
].position
= c
;
794 curr_table
->time_info
[c
].time
= 0;
797 /* Put the new table in place */
798 spin_lock_bh(&recent_lock
);
799 find_table
= r_tables
;
800 while( (last_table
= find_table
) && strncmp(info
->name
,find_table
->name
,IPT_RECENT_NAME_LEN
) && (find_table
= find_table
->next
) );
802 /* If a table already exists just increment the count on that table and return */
805 spin_unlock_bh(&recent_lock
);
807 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry: table found (%s), created by other process.\n",info
->name
);
809 vfree(curr_table
->time_info
);
810 vfree(curr_table
->hash_table
);
812 vfree(curr_table
->table
);
816 if(!last_table
) r_tables
= curr_table
; else last_table
->next
= curr_table
;
818 spin_unlock_bh(&recent_lock
);
820 #ifdef CONFIG_PROC_FS
821 /* Create our proc 'status' entry. */
822 curr_table
->status_proc
= create_proc_entry(curr_table
->name
, ip_list_perms
, proc_net_ipt_recent
);
823 if (!curr_table
->status_proc
) {
824 printk(KERN_INFO RECENT_NAME
": checkentry: unable to allocate for /proc entry.\n");
825 /* Destroy the created table */
826 spin_lock_bh(&recent_lock
);
828 curr_table
= r_tables
;
831 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry() create_proc failed, no tables.\n");
833 spin_unlock_bh(&recent_lock
);
836 while( strncmp(info
->name
,curr_table
->name
,IPT_RECENT_NAME_LEN
) && (last_table
= curr_table
) && (curr_table
= curr_table
->next
) );
839 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry() create_proc failed, table already destroyed.\n");
841 spin_unlock_bh(&recent_lock
);
844 if(last_table
) last_table
->next
= curr_table
->next
; else r_tables
= curr_table
->next
;
845 spin_unlock_bh(&recent_lock
);
846 vfree(curr_table
->time_info
);
847 vfree(curr_table
->hash_table
);
849 vfree(curr_table
->table
);
854 curr_table
->status_proc
->owner
= THIS_MODULE
;
855 curr_table
->status_proc
->data
= curr_table
;
857 curr_table
->status_proc
->read_proc
= ip_recent_get_info
;
858 curr_table
->status_proc
->write_proc
= ip_recent_ctrl
;
859 #endif /* CONFIG_PROC_FS */
862 if(debug
) printk(KERN_INFO RECENT_NAME
": checkentry() left.\n");
868 /* This function is called in the event that a rule matching this module is
870 * When this happens we need to check if there are no other rules matching
871 * the table given. If that is the case then we remove the table and clean
875 destroy(const struct xt_match
*match
, void *matchinfo
, unsigned int matchsize
)
877 const struct ipt_recent_info
*info
= matchinfo
;
878 struct recent_ip_tables
*curr_table
, *last_table
;
881 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() entered.\n");
884 if(matchsize
!= IPT_ALIGN(sizeof(struct ipt_recent_info
))) return;
886 /* Lock the linked list while we play with it */
887 spin_lock_bh(&recent_lock
);
889 /* Look for an entry with this name already created */
890 /* Finds the end of the list and the entry before the end if current name does not exist */
892 curr_table
= r_tables
;
895 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() No tables found, leaving.\n");
897 spin_unlock_bh(&recent_lock
);
900 while( strncmp(info
->name
,curr_table
->name
,IPT_RECENT_NAME_LEN
) && (last_table
= curr_table
) && (curr_table
= curr_table
->next
) );
902 /* If a table does not exist then do nothing and return */
905 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() table not found, leaving.\n");
907 spin_unlock_bh(&recent_lock
);
913 /* If count is still non-zero then there are still rules referenceing it so we do nothing */
914 if(curr_table
->count
) {
916 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() table found, non-zero count, leaving.\n");
918 spin_unlock_bh(&recent_lock
);
923 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() table found, zero count, removing.\n");
926 /* Count must be zero so we remove this table from the list */
927 if(last_table
) last_table
->next
= curr_table
->next
; else r_tables
= curr_table
->next
;
929 spin_unlock_bh(&recent_lock
);
931 /* lock to make sure any late-runners still using this after we removed it from
932 * the list finish up then remove everything */
933 spin_lock_bh(&curr_table
->list_lock
);
934 spin_unlock_bh(&curr_table
->list_lock
);
936 #ifdef CONFIG_PROC_FS
937 if(curr_table
->status_proc
) remove_proc_entry(curr_table
->name
,proc_net_ipt_recent
);
938 #endif /* CONFIG_PROC_FS */
939 vfree(curr_table
->table
[0].last_pkts
);
940 vfree(curr_table
->table
);
941 vfree(curr_table
->hash_table
);
942 vfree(curr_table
->time_info
);
946 if(debug
) printk(KERN_INFO RECENT_NAME
": destroy() left.\n");
952 /* This is the structure we pass to ipt_register to register our
953 * module with iptables.
955 static struct ipt_match recent_match
= {
958 .matchsize
= sizeof(struct ipt_recent_info
),
959 .checkentry
= checkentry
,
964 /* Kernel module initialization. */
965 static int __init
ipt_recent_init(void)
970 #ifdef CONFIG_PROC_FS
971 proc_net_ipt_recent
= proc_mkdir("ipt_recent",proc_net
);
972 if(!proc_net_ipt_recent
) return -ENOMEM
;
975 if(ip_list_hash_size
&& ip_list_hash_size
<= ip_list_tot
) {
976 printk(KERN_WARNING RECENT_NAME
": ip_list_hash_size too small, resetting to default.\n");
977 ip_list_hash_size
= 0;
980 if(!ip_list_hash_size
) {
981 ip_list_hash_size
= ip_list_tot
*3;
983 while(ip_list_hash_size
> count
) count
= count
*2;
984 ip_list_hash_size
= count
;
988 if(debug
) printk(KERN_INFO RECENT_NAME
": ip_list_hash_size: %d\n",ip_list_hash_size
);
991 err
= ipt_register_match(&recent_match
);
993 remove_proc_entry("ipt_recent", proc_net
);
997 /* Kernel module destruction. */
998 static void __exit
ipt_recent_fini(void)
1000 ipt_unregister_match(&recent_match
);
1002 remove_proc_entry("ipt_recent",proc_net
);
1005 /* Register our module with the kernel. */
1006 module_init(ipt_recent_init
);
1007 module_exit(ipt_recent_fini
);
This page took 0.056257 seconds and 5 git commands to generate.