linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] fix mem-leak in netfilter
@ 2006-05-07  2:26 Jesper Juhl
  2006-05-07  9:36 ` Willy Tarreau
  0 siblings, 1 reply; 32+ messages in thread
From: Jesper Juhl @ 2006-05-07  2:26 UTC (permalink / raw)
  To: linux-kernel; +Cc: Stephen Frost, laforge, netfilter-devel, Jesper Juhl

The Coverity checker spotted that we may leak 'hold' in 
net/ipv4/netfilter/ipt_recent.c::checkentry() when the following
is true : 
  if (!curr_table->status_proc) {
    ...
    if(!curr_table) {
    ...
      return 0;  <-- here we leak.
Simply moving an existing vfree(hold); up a bit avoids the possible leak.


(please keep me on CC when replying since I'm not subscribed 
 to netfilter-devel)


Signed-off-by: Jesper Juhl <jesper.juhl@gmail.com>
---

 net/ipv4/netfilter/ipt_recent.c |    2 +-
 1 files changed, 1 insertion(+), 1 deletion(-)

--- linux-2.6.17-rc3-git12-orig/net/ipv4/netfilter/ipt_recent.c	2006-05-07 03:25:38.000000000 +0200
+++ linux-2.6.17-rc3-git12/net/ipv4/netfilter/ipt_recent.c	2006-05-07 04:16:26.000000000 +0200
@@ -821,6 +821,7 @@ checkentry(const char *tablename,
 	/* Create our proc 'status' entry. */
 	curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
 	if (!curr_table->status_proc) {
+		vfree(hold);
 		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
 		/* Destroy the created table */
 		spin_lock_bh(&recent_lock);
@@ -845,7 +846,6 @@ checkentry(const char *tablename,
 		spin_unlock_bh(&recent_lock);
 		vfree(curr_table->time_info);
 		vfree(curr_table->hash_table);
-		vfree(hold);
 		vfree(curr_table->table);
 		vfree(curr_table);
 		return 0;



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-07  2:26 [PATCH] fix mem-leak in netfilter Jesper Juhl
@ 2006-05-07  9:36 ` Willy Tarreau
  2006-05-07 22:42   ` Grant Coady
  0 siblings, 1 reply; 32+ messages in thread
From: Willy Tarreau @ 2006-05-07  9:36 UTC (permalink / raw)
  To: Jesper Juhl
  Cc: linux-kernel, Stephen Frost, laforge, netfilter-devel, marcelo

On Sun, May 07, 2006 at 04:26:10AM +0200, Jesper Juhl wrote:
> The Coverity checker spotted that we may leak 'hold' in 
> net/ipv4/netfilter/ipt_recent.c::checkentry() when the following
> is true : 
>   if (!curr_table->status_proc) {
>     ...
>     if(!curr_table) {
>     ...
>       return 0;  <-- here we leak.
> Simply moving an existing vfree(hold); up a bit avoids the possible leak.
> 
> 
> (please keep me on CC when replying since I'm not subscribed 
>  to netfilter-devel)
> 
> 
> Signed-off-by: Jesper Juhl <jesper.juhl@gmail.com>
> ---
> 
>  net/ipv4/netfilter/ipt_recent.c |    2 +-
>  1 files changed, 1 insertion(+), 1 deletion(-)
> 
> --- linux-2.6.17-rc3-git12-orig/net/ipv4/netfilter/ipt_recent.c	2006-05-07 03:25:38.000000000 +0200
> +++ linux-2.6.17-rc3-git12/net/ipv4/netfilter/ipt_recent.c	2006-05-07 04:16:26.000000000 +0200
> @@ -821,6 +821,7 @@ checkentry(const char *tablename,
>  	/* Create our proc 'status' entry. */
>  	curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
>  	if (!curr_table->status_proc) {
> +		vfree(hold);
>  		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
>  		/* Destroy the created table */
>  		spin_lock_bh(&recent_lock);
> @@ -845,7 +846,6 @@ checkentry(const char *tablename,
>  		spin_unlock_bh(&recent_lock);
>  		vfree(curr_table->time_info);
>  		vfree(curr_table->hash_table);
> -		vfree(hold);
>  		vfree(curr_table->table);
>  		vfree(curr_table);
>  		return 0;

Seems valid for 2.4.32 too. I'm queuing it up for Marcelo.

Regards,
Willy


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-07  9:36 ` Willy Tarreau
@ 2006-05-07 22:42   ` Grant Coady
  2006-05-08  5:07     ` Willy Tarreau
  0 siblings, 1 reply; 32+ messages in thread
From: Grant Coady @ 2006-05-07 22:42 UTC (permalink / raw)
  To: Willy Tarreau
  Cc: Jesper Juhl, linux-kernel, Stephen Frost, laforge,
	netfilter-devel, marcelo

On Sun, 7 May 2006 11:36:40 +0200, Willy Tarreau <willy@w.ods.org> wrote:

>On Sun, May 07, 2006 at 04:26:10AM +0200, Jesper Juhl wrote:
>> The Coverity checker spotted that we may leak 'hold' in 
>> net/ipv4/netfilter/ipt_recent.c::checkentry() when the following
>> is true : 
>>   if (!curr_table->status_proc) {
>>     ...
>>     if(!curr_table) {
>>     ...
>>       return 0;  <-- here we leak.
>> Simply moving an existing vfree(hold); up a bit avoids the possible leak.
>> 
>> 
>> (please keep me on CC when replying since I'm not subscribed 
>>  to netfilter-devel)
>> 
>> 
>> Signed-off-by: Jesper Juhl <jesper.juhl@gmail.com>
>> ---
>> 
>>  net/ipv4/netfilter/ipt_recent.c |    2 +-
>>  1 files changed, 1 insertion(+), 1 deletion(-)
>> 
>> --- linux-2.6.17-rc3-git12-orig/net/ipv4/netfilter/ipt_recent.c	2006-05-07 03:25:38.000000000 +0200
>> +++ linux-2.6.17-rc3-git12/net/ipv4/netfilter/ipt_recent.c	2006-05-07 04:16:26.000000000 +0200
>> @@ -821,6 +821,7 @@ checkentry(const char *tablename,
>>  	/* Create our proc 'status' entry. */
>>  	curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
>>  	if (!curr_table->status_proc) {
>> +		vfree(hold);
>>  		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
>>  		/* Destroy the created table */
>>  		spin_lock_bh(&recent_lock);
>> @@ -845,7 +846,6 @@ checkentry(const char *tablename,
>>  		spin_unlock_bh(&recent_lock);
>>  		vfree(curr_table->time_info);
>>  		vfree(curr_table->hash_table);
>> -		vfree(hold);
>>  		vfree(curr_table->table);
>>  		vfree(curr_table);
>>  		return 0;
>
>Seems valid for 2.4.32 too. I'm queuing it up for Marcelo.

When CONFIG_PROC_FS is not set the function looks like it may exit 
without doing the vfree()s for stuff allocated above the #ifdef 
CONFIG_PROC_FS.  

I wonder if the larger view of the function is also correct?  The 
coding style is difficult to work with as my terminal only goes to 
156 characters wide ;)  

Grant.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-07 22:42   ` Grant Coady
@ 2006-05-08  5:07     ` Willy Tarreau
  2006-05-08  5:43       ` David S. Miller
  0 siblings, 1 reply; 32+ messages in thread
From: Willy Tarreau @ 2006-05-08  5:07 UTC (permalink / raw)
  To: Grant Coady
  Cc: Jesper Juhl, linux-kernel, Stephen Frost, laforge,
	netfilter-devel, marcelo

Hi Grant,

On Mon, May 08, 2006 at 08:42:53AM +1000, Grant Coady wrote:
> On Sun, 7 May 2006 11:36:40 +0200, Willy Tarreau <willy@w.ods.org> wrote:
> 
> >On Sun, May 07, 2006 at 04:26:10AM +0200, Jesper Juhl wrote:
> >> The Coverity checker spotted that we may leak 'hold' in 
> >> net/ipv4/netfilter/ipt_recent.c::checkentry() when the following
> >> is true : 
> >>   if (!curr_table->status_proc) {
> >>     ...
> >>     if(!curr_table) {
> >>     ...
> >>       return 0;  <-- here we leak.
> >> Simply moving an existing vfree(hold); up a bit avoids the possible leak.
> >> 
> >> 
> >> (please keep me on CC when replying since I'm not subscribed 
> >>  to netfilter-devel)
> >> 
> >> 
> >> Signed-off-by: Jesper Juhl <jesper.juhl@gmail.com>
> >> ---
> >> 
> >>  net/ipv4/netfilter/ipt_recent.c |    2 +-
> >>  1 files changed, 1 insertion(+), 1 deletion(-)
> >> 
> >> --- linux-2.6.17-rc3-git12-orig/net/ipv4/netfilter/ipt_recent.c	2006-05-07 03:25:38.000000000 +0200
> >> +++ linux-2.6.17-rc3-git12/net/ipv4/netfilter/ipt_recent.c	2006-05-07 04:16:26.000000000 +0200
> >> @@ -821,6 +821,7 @@ checkentry(const char *tablename,
> >>  	/* Create our proc 'status' entry. */
> >>  	curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
> >>  	if (!curr_table->status_proc) {
> >> +		vfree(hold);
> >>  		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
> >>  		/* Destroy the created table */
> >>  		spin_lock_bh(&recent_lock);
> >> @@ -845,7 +846,6 @@ checkentry(const char *tablename,
> >>  		spin_unlock_bh(&recent_lock);
> >>  		vfree(curr_table->time_info);
> >>  		vfree(curr_table->hash_table);
> >> -		vfree(hold);
> >>  		vfree(curr_table->table);
> >>  		vfree(curr_table);
> >>  		return 0;
> >
> >Seems valid for 2.4.32 too. I'm queuing it up for Marcelo.
> 
> When CONFIG_PROC_FS is not set the function looks like it may exit 
> without doing the vfree()s for stuff allocated above the #ifdef 
> CONFIG_PROC_FS.  

At first, I thought you were right. But after a night long rest,
I'm doubting. In fact, I'm not even sure that we can free 'hold' :

    753         for(c = 0; c < ip_list_tot; c++) {
    754                 curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot;
    755         }
    756 

So it seems like the vfree(hold) must not be performed if curr_table
is not unlinked. If this is the case, even Jesper's patch might be
wrong. Otherwise, vfree(hold) should be called unconditionnally
after #endif CONFIG_PROC_FS.

> I wonder if the larger view of the function is also correct?  The 
> coding style is difficult to work with as my terminal only goes to 
> 156 characters wide ;)  

Agreed ! Reading this code is really painful. Even after one long
night, I have huge trouble understanding it. Here are some good
excerpts, that we might honnestly call 'obfuscation' :

    799  while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
    836  while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
    844  if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;

I wonder how such unmaintainable code has been merged in the first
place. Obviously, Davem has never seen it ! He has already annoyed
me for 81-chars wide lines because his terminal is 80 columns. Or
he has given up from the very beginning. The fact is it's a tool
which has found a potential memory leak.

> Grant.

Regards,
Willy


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-08  5:07     ` Willy Tarreau
@ 2006-05-08  5:43       ` David S. Miller
  2006-05-08  8:36         ` Amin Azez
  2006-05-12  7:40         ` Patrick McHardy
  0 siblings, 2 replies; 32+ messages in thread
From: David S. Miller @ 2006-05-08  5:43 UTC (permalink / raw)
  To: willy
  Cc: gcoady.lk, sfrost, laforge, jesper.juhl, netfilter-devel,
	linux-kernel, marcelo

From: Willy Tarreau <willy@w.ods.org>
Date: Mon, 8 May 2006 07:07:48 +0200

> I wonder how such unmaintainable code has been merged in the first
> place. Obviously, Davem has never seen it !

Oh I've seen ipt_recent.c, it's one huge pile of trash
that needs to be rewritten.  It has all sorts of problems.

This is well understood on the netfilter-devel list and
I am to understand that someone has taken up the task to
finally rewrite the thing.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-08  5:43       ` David S. Miller
@ 2006-05-08  8:36         ` Amin Azez
  2006-05-08  9:08           ` Juergen Kreileder
  2006-05-12  7:40         ` Patrick McHardy
  1 sibling, 1 reply; 32+ messages in thread
From: Amin Azez @ 2006-05-08  8:36 UTC (permalink / raw)
  To: David S. Miller
  Cc: sfrost, gcoady.lk, laforge, jesper.juhl, netfilter-devel,
	linux-kernel, marcelo, Juergen.Kreileder

David S. Miller wrote:
> From: Willy Tarreau <willy@w.ods.org>
> Date: Mon, 8 May 2006 07:07:48 +0200
> 
>> I wonder how such unmaintainable code has been merged in the first
>> place. Obviously, Davem has never seen it !
> 
> Oh I've seen ipt_recent.c, it's one huge pile of trash
> that needs to be rewritten.  It has all sorts of problems.
> 
> This is well understood on the netfilter-devel list and
> I am to understand that someone has taken up the task to
> finally rewrite the thing.


Is that Juergen.Kreileder@empolis.com ?
...just checking... he seemed to volunteer in December last year but
Stephen Frost has been taking recent questions.

Sam

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-08  8:36         ` Amin Azez
@ 2006-05-08  9:08           ` Juergen Kreileder
  0 siblings, 0 replies; 32+ messages in thread
From: Juergen Kreileder @ 2006-05-08  9:08 UTC (permalink / raw)
  To: Amin Azez
  Cc: David S. Miller, sfrost, gcoady.lk, laforge, jesper.juhl,
	netfilter-devel, linux-kernel, marcelo

Amin Azez wrote:
> David S. Miller wrote:
>> From: Willy Tarreau <willy@w.ods.org>
>> Date: Mon, 8 May 2006 07:07:48 +0200
>>
>>> I wonder how such unmaintainable code has been merged in the first
>>> place. Obviously, Davem has never seen it !
>> Oh I've seen ipt_recent.c, it's one huge pile of trash
>> that needs to be rewritten.  It has all sorts of problems.
>>
>> This is well understood on the netfilter-devel list and
>> I am to understand that someone has taken up the task to
>> finally rewrite the thing.
> 
> 
> Is that Juergen.Kreileder@empolis.com ?

Please use jk@blackdown.de (@empolis.com is just an address at
a client's site).

> ...just checking... he seemed to volunteer in December

but not for a rewrite.  Anyhow, if somebody is planning to do that
I'll gladly help.

> last year but Stephen Frost has been taking recent questions.


	Juergen

-- 
Juergen Kreileder, Blackdown Java-Linux Team
http://blog.blackdown.de/

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-08  5:43       ` David S. Miller
  2006-05-08  8:36         ` Amin Azez
@ 2006-05-12  7:40         ` Patrick McHardy
  2006-05-12 11:09           ` Jesper Juhl
  1 sibling, 1 reply; 32+ messages in thread
From: Patrick McHardy @ 2006-05-12  7:40 UTC (permalink / raw)
  To: David S. Miller
  Cc: willy, sfrost, gcoady.lk, laforge, jesper.juhl, netfilter-devel,
	linux-kernel, marcelo

David S. Miller wrote:
> From: Willy Tarreau <willy@w.ods.org>
> Date: Mon, 8 May 2006 07:07:48 +0200
> 
> 
>>I wonder how such unmaintainable code has been merged in the first
>>place. Obviously, Davem has never seen it !
> 
> 
> Oh I've seen ipt_recent.c, it's one huge pile of trash
> that needs to be rewritten.  It has all sorts of problems.
> 
> This is well understood on the netfilter-devel list and
> I am to understand that someone has taken up the task to
> finally rewrite the thing.


I haven't seen any cleanup patches so far, so I think I'm
going to start my nth try at cleaning up this mess.
Unfortunately its even immune to Lindent ..

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-12  7:40         ` Patrick McHardy
@ 2006-05-12 11:09           ` Jesper Juhl
  2006-05-12 11:33             ` Patrick McHardy
  0 siblings, 1 reply; 32+ messages in thread
From: Jesper Juhl @ 2006-05-12 11:09 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: David S. Miller, willy, sfrost, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

On 5/12/06, Patrick McHardy <kaber@trash.net> wrote:
> David S. Miller wrote:
> > From: Willy Tarreau <willy@w.ods.org>
> > Date: Mon, 8 May 2006 07:07:48 +0200
> >
> >
> >>I wonder how such unmaintainable code has been merged in the first
> >>place. Obviously, Davem has never seen it !
> >
> >
> > Oh I've seen ipt_recent.c, it's one huge pile of trash
> > that needs to be rewritten.  It has all sorts of problems.
> >
> > This is well understood on the netfilter-devel list and
> > I am to understand that someone has taken up the task to
> > finally rewrite the thing.
>
>
> I haven't seen any cleanup patches so far, so I think I'm
> going to start my nth try at cleaning up this mess.
> Unfortunately its even immune to Lindent ..
>

If you get too fed up with it, let me know, and I'll give it a go as well.

-- 
Jesper Juhl <jesper.juhl@gmail.com>
Don't top-post  http://www.catb.org/~esr/jargon/html/T/top-post.html
Plain text mails only, please      http://www.expita.com/nomime.html

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-12 11:09           ` Jesper Juhl
@ 2006-05-12 11:33             ` Patrick McHardy
  2006-05-12 12:13               ` Jesper Juhl
       [not found]               ` <446490BB.10801@ufomechanic.net>
  0 siblings, 2 replies; 32+ messages in thread
From: Patrick McHardy @ 2006-05-12 11:33 UTC (permalink / raw)
  To: Jesper Juhl
  Cc: David S. Miller, willy, sfrost, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 680 bytes --]

Jesper Juhl wrote:
> On 5/12/06, Patrick McHardy <kaber@trash.net> wrote:
> 
>> I haven't seen any cleanup patches so far, so I think I'm
>> going to start my nth try at cleaning up this mess.
>> Unfortunately its even immune to Lindent ..
>>
> 
> If you get too fed up with it, let me know, and I'll give it a go as well.

Thanks, I'm about half-way through (and about to kill someone),
just started with the biggest pile of crap (the match function)
and already noticed a possible endless loop within the first
couple of lines.

Unfortunately this stuff is so unreadable that I'm not exactly
sure if the loop really won't terminate, an extra pair of eyes
would be appreciated.


[-- Attachment #2: x --]
[-- Type: text/plain, Size: 712 bytes --]

diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
index 1438432..b8850a2 100644
--- a/net/ipv4/netfilter/ipt_recent.c
+++ b/net/ipv4/netfilter/ipt_recent.c
@@ -438,11 +438,15 @@ #endif
 			(!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) {
 			/* Collision in hash table */
 			hash_result = (hash_result + 1) % ip_list_hash_size;
+			if (hash_result == orig_hash_result)
+				break;
 		}
 	} else {
 		while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) {
 			/* Collision in hash table */
 			hash_result = (hash_result + 1) % ip_list_hash_size;
+			if (hash_result == orig_hash_result)
+				break;
 		}
 	}
 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-12 11:33             ` Patrick McHardy
@ 2006-05-12 12:13               ` Jesper Juhl
  2006-05-12 12:40                 ` Willy Tarreau
       [not found]               ` <446490BB.10801@ufomechanic.net>
  1 sibling, 1 reply; 32+ messages in thread
From: Jesper Juhl @ 2006-05-12 12:13 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: David S. Miller, willy, sfrost, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

On 5/12/06, Patrick McHardy <kaber@trash.net> wrote:
> Jesper Juhl wrote:
> > On 5/12/06, Patrick McHardy <kaber@trash.net> wrote:
> >
> >> I haven't seen any cleanup patches so far, so I think I'm
> >> going to start my nth try at cleaning up this mess.
> >> Unfortunately its even immune to Lindent ..
> >>
> >
> > If you get too fed up with it, let me know, and I'll give it a go as well.
>
> Thanks, I'm about half-way through (and about to kill someone),
> just started with the biggest pile of crap (the match function)
> and already noticed a possible endless loop within the first
> couple of lines.
>
> Unfortunately this stuff is so unreadable that I'm not exactly
> sure if the loop really won't terminate, an extra pair of eyes
> would be appreciated.
>

Sure thing.

I don't have time to look at it today (friends comming over for
dinner), but I should have plenty of time for it tomorrow. So, if you
could send me your patch once you are done for the day, then I'll look
it over and see if I can find anything to add on top of your work (or
have anything to comment on) and bounce it back to you sometime during
tomorrow.


-- 
Jesper Juhl <jesper.juhl@gmail.com>
Don't top-post  http://www.catb.org/~esr/jargon/html/T/top-post.html
Plain text mails only, please      http://www.expita.com/nomime.html

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-12 12:13               ` Jesper Juhl
@ 2006-05-12 12:40                 ` Willy Tarreau
  2006-05-12 12:49                   ` Patrick McHardy
  0 siblings, 1 reply; 32+ messages in thread
From: Willy Tarreau @ 2006-05-12 12:40 UTC (permalink / raw)
  To: Jesper Juhl
  Cc: Patrick McHardy, David S. Miller, sfrost, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

On Fri, May 12, 2006 at 02:13:32PM +0200, Jesper Juhl wrote:
> On 5/12/06, Patrick McHardy <kaber@trash.net> wrote:
> >Jesper Juhl wrote:
> >> On 5/12/06, Patrick McHardy <kaber@trash.net> wrote:
> >>
> >>> I haven't seen any cleanup patches so far, so I think I'm
> >>> going to start my nth try at cleaning up this mess.
> >>> Unfortunately its even immune to Lindent ..
> >>>
> >>
> >> If you get too fed up with it, let me know, and I'll give it a go as 
> >well.
> >
> >Thanks, I'm about half-way through (and about to kill someone),
> >just started with the biggest pile of crap (the match function)
> >and already noticed a possible endless loop within the first
> >couple of lines.
> >
> >Unfortunately this stuff is so unreadable that I'm not exactly
> >sure if the loop really won't terminate, an extra pair of eyes
> >would be appreciated.
> >
> 
> Sure thing.
> 
> I don't have time to look at it today (friends comming over for
> dinner), but I should have plenty of time for it tomorrow. So, if you
> could send me your patch once you are done for the day, then I'll look
> it over and see if I can find anything to add on top of your work (or
> have anything to comment on) and bounce it back to you sometime during
> tomorrow.

Please post it to the list, this coding style needs far more than two
pairs of eyes to be fixed. It has already discouraged several people,
the more we will be, the least pain we will feel :-)

Cheers
Willy


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-12 12:40                 ` Willy Tarreau
@ 2006-05-12 12:49                   ` Patrick McHardy
  0 siblings, 0 replies; 32+ messages in thread
From: Patrick McHardy @ 2006-05-12 12:49 UTC (permalink / raw)
  To: Willy Tarreau
  Cc: Jesper Juhl, David S. Miller, sfrost, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

Willy Tarreau wrote:
> Please post it to the list, this coding style needs far more than two
> pairs of eyes to be fixed. It has already discouraged several people,
> the more we will be, the least pain we will feel :-)

:)

I actually just got to fed up with this garbage (once again) and started
rewriting it from scratch, which looks like a lot less pain. I'll look
into these loops again for 2.4 and 2.6.17 once I'm done doing that.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
       [not found]               ` <446490BB.10801@ufomechanic.net>
@ 2006-05-15  8:25                 ` Patrick McHardy
  2006-05-15 14:28                   ` Stephen Frost
  0 siblings, 1 reply; 32+ messages in thread
From: Patrick McHardy @ 2006-05-15  8:25 UTC (permalink / raw)
  To: Amin Azez
  Cc: David S. Miller, willy, sfrost, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 545 bytes --]

Amin Azez wrote:
> When I saw it I thought it would be better to follow the conntrack hash
> method, where each hash bucket is a list head. The rabbit hole lark for
> hash collisions in ipt_recent is a burden.


True, parts of the problems originated in the use of closed hashing -
sadly enough this didn't even save memory, it used even more than
with open hashing. But thats only one part, its full of bad choices
and bad coding.

Anyway, here goes the first shot at a replacement, it should be fully
compatible. Comments and testing welcome.

[-- Attachment #2: x --]
[-- Type: text/plain, Size: 46772 bytes --]

[NETFILTER]: Replace ipt_recent module

Replace the totally unmaintainable ipt_recent module by a rewritten
version that should be fully compatible.

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit e84375630c59ad10eac6235f32ba4beb6921ff9e
tree d58afb0f5e552b3fefae5144414346a43ec9eeec
parent d8c3291c73b958243b33f8509d4507e76dafd055
author Patrick McHardy <kaber@trash.net> Mon, 15 May 2006 10:10:20 +0200
committer Patrick McHardy <kaber@trash.net> Mon, 15 May 2006 10:10:20 +0200

 net/ipv4/netfilter/ipt_recent.c | 1276 ++++++++++++---------------------------
 1 files changed, 382 insertions(+), 894 deletions(-)

diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
index 1438432..9c844d8 100644
--- a/net/ipv4/netfilter/ipt_recent.c
+++ b/net/ipv4/netfilter/ipt_recent.c
@@ -1,1007 +1,495 @@
-/* Kernel module to check if the source address has been seen recently. */
-/* Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org */
-/* Author: Stephen Frost <sfrost@snowman.net> */
-/* Project Page: http://snowman.net/projects/ipt_recent/ */
-/* This software is distributed under the terms of the GPL, Version 2 */
-/* This copyright does not cover user programs that use kernel services
- * by normal system calls. */
-
-#include <linux/module.h>
-#include <linux/skbuff.h>
+/*
+ * Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This is a replacement of the old ipt_recent module, which carried the
+ * following copyright notice:
+ *
+ * Author: Stephen Frost <sfrost@snowman.net>
+ * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org 
+ */
+#include <linux/init.h>
+#include <linux/moduleparam.h>
 #include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <asm/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
 #include <linux/ctype.h>
-#include <linux/ip.h>
-#include <linux/vmalloc.h>
-#include <linux/moduleparam.h>
+#include <linux/list.h>
+#include <linux/random.h>
+#include <linux/jhash.h>
+#include <linux/bitops.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
 
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_recent.h>
 
-#undef DEBUG
-#define HASH_LOG 9
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_DESCRIPTION("IP tables recently seen matching module");
+MODULE_LICENSE("GPL");
 
-/* Defaults, these can be overridden on the module command-line. */
 static unsigned int ip_list_tot = 100;
 static unsigned int ip_pkt_list_tot = 20;
 static unsigned int ip_list_hash_size = 0;
 static unsigned int ip_list_perms = 0644;
-#ifdef DEBUG
-static int debug = 1;
-#endif
-
-static char version[] =
-KERN_INFO RECENT_NAME " " RECENT_VER ": Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n";
-
-MODULE_AUTHOR("Stephen Frost <sfrost@snowman.net>");
-MODULE_DESCRIPTION("IP tables recently seen matching module " RECENT_VER);
-MODULE_LICENSE("GPL");
 module_param(ip_list_tot, uint, 0400);
 module_param(ip_pkt_list_tot, uint, 0400);
 module_param(ip_list_hash_size, uint, 0400);
 module_param(ip_list_perms, uint, 0400);
-#ifdef DEBUG
-module_param(debug, bool, 0600);
-MODULE_PARM_DESC(debug,"enable debugging output");
-#endif
-MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list");
-MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember");
-MODULE_PARM_DESC(ip_list_hash_size,"size of hash table used to look up IPs");
-MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_recent/* files");
-
-/* Structure of our list of recently seen addresses. */
-struct recent_ip_list {
-	u_int32_t addr;
-	u_int8_t  ttl;
-	unsigned long last_seen;
-	unsigned long *last_pkts;
-	u_int32_t oldest_pkt;
-	u_int32_t hash_entry;
-	u_int32_t time_pos;
-};
-
-struct time_info_list {
-	u_int32_t position;
-	u_int32_t time;
+MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
+MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember");
+MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
+MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files");
+
+
+struct recent_entry {
+	struct list_head	list;
+	u_int32_t		addr;
+	u_int8_t		ttl;
+	unsigned int		index;
+	unsigned int		nstamps;
+	unsigned long		stamps[0];
 };
 
-/* Structure of our linked list of tables of recent lists. */
-struct recent_ip_tables {
-	char name[IPT_RECENT_NAME_LEN];
-	int count;
-	int time_pos;
-	struct recent_ip_list *table;
-	struct recent_ip_tables *next;
-	spinlock_t list_lock;
-	int *hash_table;
-	struct time_info_list *time_info;
+struct recent_table {
+	struct list_head	list;
+	char			name[IPT_RECENT_NAME_LEN];
 #ifdef CONFIG_PROC_FS
-	struct proc_dir_entry *status_proc;
-#endif /* CONFIG_PROC_FS */
+	struct proc_dir_entry	*proc;
+#endif
+	unsigned int		refcnt;
+	unsigned int		entries;
+	struct list_head	iphash[0];
 };
 
-/* Our current list of addresses we have recently seen.
- * Only added to on a --set, and only updated on --set || --update 
- */
-static struct recent_ip_tables *r_tables = NULL;
-
-/* We protect r_list with this spinlock so two processors are not modifying
- * the list at the same time. 
- */
+static LIST_HEAD(tables);
 static DEFINE_SPINLOCK(recent_lock);
 
 #ifdef CONFIG_PROC_FS
-/* Our /proc/net/ipt_recent entry */
-static struct proc_dir_entry *proc_net_ipt_recent = NULL;
+static struct proc_dir_entry	*proc_dir;
+static struct file_operations	recent_fops;
 #endif
 
-/* Function declaration for later. */
-static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop);
-
-/* Function to hash a given address into the hash table of table_size size */
-static int hash_func(unsigned int addr, int table_size)
-{
-	int result = 0;
-	unsigned int value = addr;
-	do { result ^= value; } while((value >>= HASH_LOG));
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": %d = hash_func(%u,%d)\n",
-			 result & (table_size - 1),
-			 addr,
-			 table_size);
-#endif
+static u_int32_t hash_rnd;
+static int hash_rnd_initted;
 
-	return(result & (table_size - 1));
-}
-
-#ifdef CONFIG_PROC_FS
-/* This is the function which produces the output for our /proc output
- * interface which lists each IP address, the last seen time and the 
- * other recent times the address was seen.
- */
-
-static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+static u_int32_t recent_entry_hash(u_int32_t addr)
 {
-	int len = 0, count, last_len = 0, pkt_count;
-	off_t pos = 0;
-	off_t begin = 0;
-	struct recent_ip_tables *curr_table;
-
-	curr_table = (struct recent_ip_tables*) data;
-
-	spin_lock_bh(&curr_table->list_lock);
-	for(count = 0; count < ip_list_tot; count++) {
-		if(!curr_table->table[count].addr) continue;
-		last_len = len;
-		len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].addr));
-		len += sprintf(buffer+len,"ttl: %u ",curr_table->table[count].ttl);
-		len += sprintf(buffer+len,"last_seen: %lu ",curr_table->table[count].last_seen);
-		len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[count].oldest_pkt);
-		len += sprintf(buffer+len,"last_pkts: %lu",curr_table->table[count].last_pkts[0]);
-		for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) {
-			if(!curr_table->table[count].last_pkts[pkt_count]) break;
-			len += sprintf(buffer+len,", %lu",curr_table->table[count].last_pkts[pkt_count]);
-		}
-		len += sprintf(buffer+len,"\n");
-		pos = begin + len;
-		if(pos < offset) { len = 0; begin = pos; }
-		if(pos > offset + length) { len = last_len; break; }
+	if (!hash_rnd_initted) {
+		get_random_bytes(&hash_rnd, 4);
+		hash_rnd_initted = 1;
 	}
-
-	*start = buffer + (offset - begin);
-	len -= (offset - begin);
-	if(len > length) len = length;
-
-	spin_unlock_bh(&curr_table->list_lock);
-	return len;
+	return jhash_1word(addr, hash_rnd) & (ip_list_hash_size - 1);
 }
 
-/* ip_recent_ctrl provides an interface for users to modify the table
- * directly.  This allows adding entries, removing entries, and
- * flushing the entire table.
- * This is done by opening up the appropriate table for writing and
- * sending one of:
- * xx.xx.xx.xx   -- Add entry to table with current time
- * +xx.xx.xx.xx  -- Add entry to table with current time
- * -xx.xx.xx.xx  -- Remove entry from table
- * clear         -- Flush table, remove all entries
- */
-
-static int ip_recent_ctrl(struct file *file, const char __user *input, unsigned long size, void *data)
+static struct recent_entry *
+recent_entry_lookup(const struct recent_table *table, u_int32_t addr, u_int8_t ttl)
 {
-	static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
-	u_int32_t val;
-	int base, used = 0;
-	char c, *cp;
-	union iaddr {
-		uint8_t bytes[4];
-		uint32_t word;
-	} res;
-	uint8_t *pp = res.bytes;
-	int digit;
-
-	char buffer[20];
-	int len, check_set = 0, count;
-	u_int32_t addr = 0;
-	struct sk_buff *skb;
-	struct ipt_recent_info *info;
-	struct recent_ip_tables *curr_table;
-
-	curr_table = (struct recent_ip_tables*) data;
-
-	if(size > 20) len = 20; else len = size;
-
-	if(copy_from_user(buffer,input,len)) return -EFAULT;
-
-	if(len < 20) buffer[len] = '\0';
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl len: %d, input: `%.20s'\n",len,buffer);
-#endif
-
-	cp = buffer;
-	while(isspace(*cp)) { cp++; used++; if(used >= len-5) return used; }
-
-	/* Check if we are asked to flush the entire table */
-	if(!memcmp(cp,"clear",5)) {
-		used += 5;
-		spin_lock_bh(&curr_table->list_lock);
-		curr_table->time_pos = 0;
-		for(count = 0; count < ip_list_hash_size; count++) {
-			curr_table->hash_table[count] = -1;
-		}
-		for(count = 0; count < ip_list_tot; count++) {
-			curr_table->table[count].last_seen = 0;
-			curr_table->table[count].addr = 0;
-			curr_table->table[count].ttl = 0;
-			memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-			curr_table->table[count].oldest_pkt = 0;
-			curr_table->table[count].time_pos = 0;
-			curr_table->time_info[count].position = count;
-			curr_table->time_info[count].time = 0;
-		}
-		spin_unlock_bh(&curr_table->list_lock);
-		return used;
-	}
+	struct recent_entry *e;
+	unsigned int h;
+
+	h = recent_entry_hash(addr);
+	list_for_each_entry(e, &table->iphash[h], list)
+		if (e->addr == addr && (!ttl || !e->ttl || ttl == e->ttl))
+			return e;
+	return NULL;
+}
 
-        check_set = IPT_RECENT_SET;
-	switch(*cp) {
-		case '+': check_set = IPT_RECENT_SET; cp++; used++; break;
-		case '-': check_set = IPT_RECENT_REMOVE; cp++; used++; break;
-		default: if(!isdigit(*cp)) return (used+1); break;
-	}
+static void recent_entry_remove(struct recent_table *t, struct recent_entry *e)
+{
+	list_del(&e->list);
+	kfree(e);
+	t->entries--;
+}
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp,check_set);
-#endif
-	/* Get addr (effectively inet_aton()) */
-	/* Shamelessly stolen from libc, a function in the kernel for doing
-	 * this would, of course, be greatly preferred, but our options appear
-	 * to be rather limited, so we will just do it ourselves here.
-	 */
-	res.word = 0;
-
-	c = *cp;
-	for(;;) {
-		if(!isdigit(c)) return used;
-		val = 0; base = 10; digit = 0;
-		if(c == '0') {
-			c = *++cp;
-			if(c == 'x' || c == 'X') base = 16, c = *++cp;
-			else { base = 8; digit = 1; }
-		}
-		for(;;) {
-			if(isascii(c) && isdigit(c)) {
-				if(base == 8 && (c == '8' || c == '0')) return used;
-				val = (val * base) + (c - '0');
-				c = *++cp;
-				digit = 1;
-			} else if(base == 16 && isascii(c) && isxdigit(c)) {
-				val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A'));
-				c = *++cp;
-				digit = 1;
-			} else break;
+static struct recent_entry *
+recent_entry_init(struct recent_table *t, u_int32_t addr, u_int8_t ttl)
+{
+	struct recent_entry *e;
+	unsigned int i, h;
+
+	h = recent_entry_hash(addr);
+	if (t->entries >= ip_list_tot) {
+		for (i = h; ; i = (i + 1) % ip_list_hash_size) {
+			if (list_empty(&t->iphash[i]))
+				continue;
+			e = list_entry(t->iphash[i].next, struct recent_entry,
+				       list);
+			recent_entry_remove(t, e);
+			break;
 		}
-		if(c == '.') {
-			if(pp > res.bytes + 2 || val > 0xff) return used;
-			*pp++ = val;
-			c = *++cp;
-		} else break;
 	}
-	used = cp - buffer;
-	if(c != '\0' && (!isascii(c) || !isspace(c))) return used;
-	if(c == '\n') used++;
-	if(!digit) return used;
+	e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot,
+	            GFP_ATOMIC);
+	if (e == NULL)
+		return NULL;
+	e->addr      = addr;
+	e->ttl       = ttl;
+	e->stamps[0] = jiffies;
+	e->nstamps   = 1;
+	e->index     = 1;
+	list_add_tail(&e->list, &t->iphash[h]);
+	t->entries++;
+	return e;
+}
 
-	if(val > max[pp - res.bytes]) return used;
-	addr = res.word | htonl(val);
+static void recent_entry_update(struct recent_entry *e)
+{
+	e->stamps[e->index++] = jiffies;
+	if (e->index > e->nstamps)
+		e->nstamps = e->index;
+	e->index %= ip_pkt_list_tot;
+}
 
-	if(!addr && check_set == IPT_RECENT_SET) return used;
+static struct recent_table *recent_table_lookup(const char *name)
+{
+	struct recent_table *t;
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used);
-#endif
+	list_for_each_entry(t, &tables, list)
+		if (!strcmp(t->name, name))
+			return t;
+	return NULL;
+}
 
-	/* Set up and just call match */
-	info = kmalloc(sizeof(struct ipt_recent_info),GFP_KERNEL);
-	if(!info) { return -ENOMEM; }
-	info->seconds = 0;
-	info->hit_count = 0;
-	info->check_set = check_set;
-	info->invert = 0;
-	info->side = IPT_RECENT_SOURCE;
-	strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN);
-	info->name[IPT_RECENT_NAME_LEN-1] = '\0';
-
-	skb = kmalloc(sizeof(struct sk_buff),GFP_KERNEL);
-	if (!skb) {
-		used = -ENOMEM;
-		goto out_free_info;
-	}
-	skb->nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL);
-	if (!skb->nh.iph) {
-		used = -ENOMEM;
-		goto out_free_skb;
+static void recent_table_flush(struct recent_table *t)
+{
+	struct recent_entry *e, *next;
+	unsigned int i;
+	
+	for (i = 0; i < ip_list_hash_size; i++) {
+		list_for_each_entry_safe(e, next, &t->iphash[i], list)
+			recent_entry_remove(t, e);
 	}
-
-	skb->nh.iph->saddr = addr;
-	skb->nh.iph->daddr = 0;
-	/* Clear ttl since we have no way of knowing it */
-	skb->nh.iph->ttl = 0;
-	match(skb,NULL,NULL,NULL,info,0,0,NULL);
-
-	kfree(skb->nh.iph);
-out_free_skb:
-	kfree(skb);
-out_free_info:
-	kfree(info);
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": Leaving ip_recent_ctrl addr: %u used: %d\n",addr,used);
-#endif
-	return used;
 }
 
-#endif /* CONFIG_PROC_FS */
-
-/* 'match' is our primary function, called by the kernel whenever a rule is
- * hit with our module as an option to it.
- * What this function does depends on what was specifically asked of it by
- * the user:
- * --set -- Add or update last seen time of the source address of the packet
- *   -- matchinfo->check_set == IPT_RECENT_SET
- * --rcheck -- Just check if the source address is in the list
- *   -- matchinfo->check_set == IPT_RECENT_CHECK
- * --update -- If the source address is in the list, update last_seen
- *   -- matchinfo->check_set == IPT_RECENT_UPDATE
- * --remove -- If the source address is in the list, remove it
- *   -- matchinfo->check_set == IPT_RECENT_REMOVE
- * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
- *   -- matchinfo->seconds
- * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
- *   -- matchinfo->hit_count
- * --seconds and --hitcount can be combined
- */
 static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop)
+ipt_recent_match(const struct sk_buff *skb,
+		 const struct net_device *in, const struct net_device *out,
+		 const struct xt_match *match, const void *matchinfo,
+		 int offset, unsigned int protoff, int *hotdrop)
 {
-	int pkt_count, hits_found, ans;
-	unsigned long now;
 	const struct ipt_recent_info *info = matchinfo;
-	u_int32_t addr = 0, time_temp;
-	u_int8_t ttl = skb->nh.iph->ttl;
-	int *hash_table;
-	int orig_hash_result, hash_result, temp, location = 0, time_loc, end_collision_chain = -1;
-	struct time_info_list *time_info;
-	struct recent_ip_tables *curr_table;
-	struct recent_ip_tables *last_table;
-	struct recent_ip_list *r_list;
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match() called\n");
-#endif
-
-	/* Default is false ^ info->invert */
-	ans = info->invert;
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): name = '%s'\n",info->name);
-#endif
-
-	/* if out != NULL then routing has been done and TTL changed.
-	 * We change it back here internally for match what came in before routing. */
-	if(out) ttl++;
-
-	/* Find the right table */
-	spin_lock_bh(&recent_lock);
-	curr_table = r_tables;
-	while( (last_table = curr_table) && strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (curr_table = curr_table->next) );
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): table found('%s')\n",info->name);
-#endif
-
-	spin_unlock_bh(&recent_lock);
-
-	/* Table with this name not found, match impossible */
-	if(!curr_table) { return ans; }
-
-	/* Make sure no one is changing the list while we work with it */
-	spin_lock_bh(&curr_table->list_lock);
-
-	r_list = curr_table->table;
-	if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr;
-
-	if(!addr) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match() address (%u) invalid, leaving.\n",addr);
-#endif
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
-	}
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr,ttl,skb->nh.iph->ttl);
-#endif
-
-	/* Get jiffies now in case they changed while we were waiting for a lock */
-	now = jiffies;
-	hash_table = curr_table->hash_table;
-	time_info = curr_table->time_info;
-
-	orig_hash_result = hash_result = hash_func(addr,ip_list_hash_size);
-	/* Hash entry at this result used */
-	/* Check for TTL match if requested.  If TTL is zero then a match would never
-	 * happen, so match regardless of existing TTL in that case.  Zero means the
-	 * entry was added via the /proc interface anyway, so we will just use the
-	 * first TTL we get for that IP address. */
-	if(info->check_set & IPT_RECENT_TTL) {
-		while(hash_table[hash_result] != -1 && !(r_list[hash_table[hash_result]].addr == addr &&
-			(!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) {
-			/* Collision in hash table */
-			hash_result = (hash_result + 1) % ip_list_hash_size;
-		}
-	} else {
-		while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) {
-			/* Collision in hash table */
-			hash_result = (hash_result + 1) % ip_list_hash_size;
-		}
-	}
-
-	if(hash_table[hash_result] == -1 && !(info->check_set & IPT_RECENT_SET)) {
-		/* IP not in list and not asked to SET */
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
+	struct recent_table *t;
+	struct recent_entry *e;
+	u_int32_t addr;
+	u_int8_t ttl;
+	int ret = info->invert;
+
+	if (info->side == IPT_RECENT_DEST)
+		addr = skb->nh.iph->daddr;
+	else
+		addr = skb->nh.iph->saddr;
+
+	ttl = 0;
+	if (info->check_set & IPT_RECENT_TTL) {
+		ttl = skb->nh.iph->ttl;
+		/* use TTL as seen before forwaring */
+		if (out && !skb->sk)
+			ttl++;
 	}
 
-	/* Check if we need to handle the collision, do not need to on REMOVE */
-	if(orig_hash_result != hash_result && !(info->check_set & IPT_RECENT_REMOVE)) {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
-				 orig_hash_result,
-				 hash_result,
-				 r_list[hash_table[orig_hash_result]].addr,
-				 addr);
-#endif
-
-		/* We had a collision.
-		 * orig_hash_result is where we started, hash_result is where we ended up.
-		 * So, swap them because we are likely to see the same guy again sooner */
-#ifdef DEBUG
-		if(debug) {
-		  printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table[orig_hash_result]);
-		  printk(KERN_INFO RECENT_NAME ": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
-				r_list[hash_table[orig_hash_result]].hash_entry);
-		}
-#endif
-
-		r_list[hash_table[orig_hash_result]].hash_entry = hash_result;
-
-
-		temp = hash_table[orig_hash_result];
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[hash_result] = %d\n",hash_table[hash_result]);
-#endif
-		hash_table[orig_hash_result] = hash_table[hash_result];
-		hash_table[hash_result] = temp;
-		temp = hash_result;
-		hash_result = orig_hash_result;
-		orig_hash_result = temp;
-		time_info[r_list[hash_table[orig_hash_result]].time_pos].position = hash_table[orig_hash_result];
-		if(hash_table[hash_result] != -1) {
-			r_list[hash_table[hash_result]].hash_entry = hash_result;
-			time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-		}
-
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision handled.\n");
-#endif
+	spin_lock_bh(&recent_lock);
+	t = recent_table_lookup(info->name);
+	e = recent_entry_lookup(t, addr, ttl);
+	if (e == NULL) {
+		if (!(info->check_set & IPT_RECENT_SET))
+			goto out;
+		e = recent_entry_init(t, addr, ttl);
+		if (e == NULL)
+			*hotdrop = 1;
+		ret ^= 1;
+		goto out;
 	}
 
-	if(hash_table[hash_result] == -1) {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): New table entry. (hr: %d,ha: %u)\n",
-				 hash_result, addr);
-#endif
-
-		/* New item found and IPT_RECENT_SET, so we need to add it */
-		location = time_info[curr_table->time_pos].position;
-		hash_table[r_list[location].hash_entry] = -1;
-		hash_table[hash_result] = location;
-		memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-		r_list[location].time_pos = curr_table->time_pos;
-		r_list[location].addr = addr;
-		r_list[location].ttl = ttl;
-		r_list[location].last_seen = now;
-		r_list[location].oldest_pkt = 1;
-		r_list[location].last_pkts[0] = now;
-		r_list[location].hash_entry = hash_result;
-		time_info[curr_table->time_pos].time = r_list[location].last_seen;
-		curr_table->time_pos = (curr_table->time_pos + 1) % ip_list_tot;
-
-		ans = !info->invert;
-	} else {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Existing table entry. (hr: %d,ha: %u)\n",
-				 hash_result,
-				 addr);
-#endif
-
-		/* Existing item found */
-		location = hash_table[hash_result];
-		/* We have a match on address, now to make sure it meets all requirements for a
-		 * full match. */
-		if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) {
-			if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert;
-			if(info->seconds && !info->hit_count) {
-				if(time_before_eq(now,r_list[location].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert;
-			}
-			if(info->seconds && info->hit_count) {
-				for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
-					if(r_list[location].last_pkts[pkt_count] == 0) break;
-					if(time_before_eq(now,r_list[location].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++;
-				}
-				if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
-			}
-			if(info->hit_count && !info->seconds) {
-				for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
-					if(r_list[location].last_pkts[pkt_count] == 0) break;
-					hits_found++;
-				}
-				if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
-			}
-		}
-#ifdef DEBUG
-		if(debug) {
-			if(ans)
-				printk(KERN_INFO RECENT_NAME ": match(): match addr: %u\n",addr);
-			else
-				printk(KERN_INFO RECENT_NAME ": match(): no match addr: %u\n",addr);
-		}
-#endif
-
-		/* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
-		 * current timestamp to the last_seen. */
-		if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": match(): SET or UPDATE; updating time info.\n");
-#endif
-			/* Have to update our time info */
-			time_loc = r_list[location].time_pos;
-			time_info[time_loc].time = now;
-			time_info[time_loc].position = location;
-			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) {
-				time_temp = time_info[time_loc].time;
-				time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
-				time_info[(time_loc+1)%ip_list_tot].time = time_temp;
-				time_temp = time_info[time_loc].position;
-				time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
-				time_info[(time_loc+1)%ip_list_tot].position = time_temp;
-				r_list[time_info[time_loc].position].time_pos = time_loc;
-				r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
-				time_loc = (time_loc+1) % ip_list_tot;
-			}
-			r_list[location].time_pos = time_loc;
-			r_list[location].ttl = ttl;
-			r_list[location].last_pkts[r_list[location].oldest_pkt] = now;
-			r_list[location].oldest_pkt = ++r_list[location].oldest_pkt % ip_pkt_list_tot;
-			r_list[location].last_seen = now;
-		}
-		/* If we have been asked to remove the entry from the list, just set it to 0 */
-		if(info->check_set & IPT_RECENT_REMOVE) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result,hash_result);
-#endif
-			/* Check if this is part of a collision chain */
-			while(hash_table[(orig_hash_result+1) % ip_list_hash_size] != -1) {
-				orig_hash_result++;
-				if(hash_func(r_list[hash_table[orig_hash_result]].addr,ip_list_hash_size) == hash_result) {
-					/* Found collision chain, how deep does this rabbit hole go? */
-#ifdef DEBUG
-					if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; found collision chain.\n");
-#endif
-					end_collision_chain = orig_hash_result;
-				}
-			}
-			if(end_collision_chain != -1) {
-#ifdef DEBUG
-				if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; part of collision chain, moving to end.\n");
-#endif
-				/* Part of a collision chain, swap it with the end of the chain
-				 * before removing. */
-				r_list[hash_table[end_collision_chain]].hash_entry = hash_result;
-				temp = hash_table[end_collision_chain];
-				hash_table[end_collision_chain] = hash_table[hash_result];
-				hash_table[hash_result] = temp;
-				time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-				hash_result = end_collision_chain;
-				r_list[hash_table[hash_result]].hash_entry = hash_result;
-				time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-			}
-			location = hash_table[hash_result];
-			hash_table[r_list[location].hash_entry] = -1;
-			time_loc = r_list[location].time_pos;
-			time_info[time_loc].time = 0;
-			time_info[time_loc].position = location;
-			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) {
-				time_temp = time_info[time_loc].time;
-				time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
-				time_info[(time_loc+1)%ip_list_tot].time = time_temp;
-				time_temp = time_info[time_loc].position;
-				time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
-				time_info[(time_loc+1)%ip_list_tot].position = time_temp;
-				r_list[time_info[time_loc].position].time_pos = time_loc;
-				r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
-				time_loc = (time_loc+1) % ip_list_tot;
+	if (info->check_set & IPT_RECENT_SET)
+		ret ^= 1;
+	else if (info->check_set & IPT_RECENT_REMOVE) {
+		recent_entry_remove(t, e);
+		ret ^= 1;
+	} else if (info->check_set & (IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) {
+		unsigned long t = jiffies - info->seconds * HZ;
+		unsigned int i, hits = 0;
+
+		for (i = 0; i < e->nstamps; i++) {
+			if (info->seconds && time_after(t, e->stamps[i]))
+				continue;
+			if (!info->hit_count || ++hits >= info->hit_count) {
+				ret ^= 1;
+				break;
 			}
-			r_list[location].time_pos = time_loc;
-			r_list[location].last_seen = 0;
-			r_list[location].addr = 0;
-			r_list[location].ttl = 0;
-			memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-			r_list[location].oldest_pkt = 0;
-			ans = !info->invert;
 		}
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
 	}
 
-	spin_unlock_bh(&curr_table->list_lock);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match() left.\n");
-#endif
-	return ans;
+	if (info->check_set & IPT_RECENT_SET ||
+	    (info->check_set & IPT_RECENT_UPDATE && ret)) {
+		recent_entry_update(e);
+		e->ttl = ttl;
+	}
+out:
+	spin_unlock_bh(&recent_lock);
+	return ret;
 }
 
-/* This function is to verify that the rule given during the userspace iptables
- * command is correct.
- * If the command is valid then we check if the table name referred to by the
- * rule exists, if not it is created.
- */
 static int
-checkentry(const char *tablename,
-           const void *ip,
-	   const struct xt_match *match,
-           void *matchinfo,
-           unsigned int matchsize,
-           unsigned int hook_mask)
+ipt_recent_checkentry(const char *tablename, const void *ip,
+		      const struct xt_match *match, void *matchinfo,
+		      unsigned int matchsize, unsigned int hook_mask)
 {
-	int flag = 0, c;
-	unsigned long *hold;
 	const struct ipt_recent_info *info = matchinfo;
-	struct recent_ip_tables *curr_table, *find_table, *last_table;
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() entered.\n");
-#endif
+	struct recent_table *t;
+	unsigned i;
+	int ret = 0;
 
-	/* seconds and hit_count only valid for CHECK/UPDATE */
-	if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; }
-	if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; }
-	if(info->check_set & IPT_RECENT_CHECK) flag++;
-	if(info->check_set & IPT_RECENT_UPDATE) flag++;
-
-	/* One and only one of these should ever be set */
-	if(flag != 1) return 0;
-
-	/* Name must be set to something */
-	if(!info->name || !info->name[0]) return 0;
+	if (hweight8(info->check_set &
+		     (IPT_RECENT_SET | IPT_RECENT_REMOVE |
+		      IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) != 1)
+		return 0;
+	if (info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE) &&
+	    (info->seconds || info->hit_count))
+		return 0;
+	if (info->name[0] == '\0' ||
+	    strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN)
+		return 0;
 
-	/* Things look good, create a list for this if it does not exist */
-	/* Lock the linked list while we play with it */
 	spin_lock_bh(&recent_lock);
-
-	/* Look for an entry with this name already created */
-	/* Finds the end of the list and the entry before the end if current name does not exist */
-	find_table = r_tables;
-	while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
-
-	/* If a table already exists just increment the count on that table and return */
-	if(find_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), incrementing count.\n",info->name);
-#endif
-		find_table->count++;
-		spin_unlock_bh(&recent_lock);
-		return 1;
+	t = recent_table_lookup(info->name);
+	if (t != NULL) {
+		t->refcnt++;
+		ret = 1;
+		goto out;
 	}
 
-	spin_unlock_bh(&recent_lock);
-
-	/* Table with this name not found */
-	/* Allocate memory for new linked list item */
-
-#ifdef DEBUG
-	if(debug) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: no table found (%s)\n",info->name);
-		printk(KERN_INFO RECENT_NAME ": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables));
+	t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
+		    GFP_ATOMIC);
+	if (t == NULL)
+		goto out;
+	strcpy(t->name, info->name);
+	for (i = 0; i < ip_list_hash_size; i++)
+		INIT_LIST_HEAD(&t->iphash[i]);
+#ifdef CONFIG_PROC_FS
+	t->proc = create_proc_entry(t->name, ip_list_perms, proc_dir);
+	if (t->proc == NULL) {
+		kfree(t);
+		goto out;
 	}
+	t->proc->proc_fops = &recent_fops;
+	t->proc->data      = t;
 #endif
+	list_add_tail(&t->list, &tables);
+	ret = 1;
+out:
+	spin_unlock_bh(&recent_lock);
+	return ret;
+}
 
-	curr_table = vmalloc(sizeof(struct recent_ip_tables));
-	if(curr_table == NULL) return 0;
-
-	spin_lock_init(&curr_table->list_lock);
-	curr_table->next = NULL;
-	curr_table->count = 1;
-	curr_table->time_pos = 0;
-	strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN);
-	curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0';
-
-	/* Allocate memory for this table and the list of packets in each entry. */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for table (%s).\n",
-			sizeof(struct recent_ip_list)*ip_list_tot,
-			info->name);
-#endif
-
-	curr_table->table = vmalloc(sizeof(struct recent_ip_list)*ip_list_tot);
-	if(curr_table->table == NULL) { vfree(curr_table); return 0; }
-	memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for pkt_list.\n",
-			sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
-#endif
-
-	hold = vmalloc(sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: After pkt_list allocation.\n");
-#endif
-	if(hold == NULL) { 
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for pkt_list.\n");
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-	for(c = 0; c < ip_list_tot; c++) {
-		curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot;
-	}
+static void
+ipt_recent_destroy(const struct xt_match *match, void *matchinfo,
+		   unsigned int matchsize)
+{
+	const struct ipt_recent_info *info = matchinfo;
+	struct recent_table *t;
 
-	/* Allocate memory for the hash table */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for hash_table.\n",
-			sizeof(int)*ip_list_hash_size);
+	spin_lock_bh(&recent_lock);
+	t = recent_table_lookup(info->name);
+	if (--t->refcnt == 0) {
+		list_del(&t->list);
+		recent_table_flush(t);
+#ifdef CONFIG_PROC_FS
+		remove_proc_entry(t->name, proc_dir);
 #endif
-
-	curr_table->hash_table = vmalloc(sizeof(int)*ip_list_hash_size);
-	if(!curr_table->hash_table) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for hash_table.\n");
-		vfree(hold);
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-
-	for(c = 0; c < ip_list_hash_size; c++) {
-		curr_table->hash_table[c] = -1;
+		kfree(t);
 	}
+	spin_unlock_bh(&recent_lock);
+}
 
-	/* Allocate memory for the time info */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for time_info.\n",
-			sizeof(struct time_info_list)*ip_list_tot);
-#endif
+#ifdef CONFIG_PROC_FS
+struct recent_iter_state {
+	struct recent_table	*table;
+	unsigned int		bucket;
+};
 
-	curr_table->time_info = vmalloc(sizeof(struct time_info_list)*ip_list_tot);
-	if(!curr_table->time_info) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for time_info.\n");
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-	for(c = 0; c < ip_list_tot; c++) {
-		curr_table->time_info[c].position = c;
-		curr_table->time_info[c].time = 0;
-	}
+static void *recent_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct recent_iter_state *st = seq->private;
+	struct recent_table *t = st->table;
+	struct recent_entry *e;
+	loff_t p = *pos;
 
-	/* Put the new table in place */
 	spin_lock_bh(&recent_lock);
-	find_table = r_tables;
-	while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
-
-	/* If a table already exists just increment the count on that table and return */
-	if(find_table) { 
-		find_table->count++;	
-		spin_unlock_bh(&recent_lock);
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), created by other process.\n",info->name);
-#endif
-		vfree(curr_table->time_info);
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table);
-		vfree(curr_table);
-		return 1;
-	}
-	if(!last_table) r_tables = curr_table; else last_table->next = curr_table;
-
-	spin_unlock_bh(&recent_lock);
 
-#ifdef CONFIG_PROC_FS
-	/* Create our proc 'status' entry. */
-	curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
-	if (!curr_table->status_proc) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
-		/* Destroy the created table */
-		spin_lock_bh(&recent_lock);
-		last_table = NULL;
-		curr_table = r_tables;
-		if(!curr_table) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, no tables.\n");
-#endif
-			spin_unlock_bh(&recent_lock);
-			return 0;
-		}
-		while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
-		if(!curr_table) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, table already destroyed.\n");
-#endif
-			spin_unlock_bh(&recent_lock);
-			return 0;
+	for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) {
+		list_for_each_entry(e, &t->iphash[st->bucket], list) {
+			if (p-- == 0)
+				return e;
 		}
-		if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
-		spin_unlock_bh(&recent_lock);
-		vfree(curr_table->time_info);
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table);
-		vfree(curr_table);
-		return 0;
 	}
-	
-	curr_table->status_proc->owner = THIS_MODULE;
-	curr_table->status_proc->data = curr_table;
-	wmb();
-	curr_table->status_proc->read_proc = ip_recent_get_info;
-	curr_table->status_proc->write_proc = ip_recent_ctrl;
-#endif /* CONFIG_PROC_FS */
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() left.\n");
-#endif
+	return NULL;
+}
 
-	return 1;
+static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct recent_iter_state *st = seq->private;
+	struct recent_table *t = st->table;
+	struct recent_entry *e = v;
+	struct list_head *head = e->list.next;
+
+	while (head == &t->iphash[st->bucket]) {
+		if (++st->bucket >= ip_list_hash_size)
+			return NULL;
+		head = t->iphash[st->bucket].next;
+	}
+	(*pos)++;
+	return list_entry(head, struct recent_entry, list);
 }
 
-/* This function is called in the event that a rule matching this module is
- * removed.
- * When this happens we need to check if there are no other rules matching
- * the table given.  If that is the case then we remove the table and clean
- * up its memory.
- */
-static void
-destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
+static void recent_seq_stop(struct seq_file *s, void *v)
 {
-	const struct ipt_recent_info *info = matchinfo;
-	struct recent_ip_tables *curr_table, *last_table;
+	spin_unlock_bh(&recent_lock);
+}
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() entered.\n");
-#endif
+static int recent_seq_show(struct seq_file *seq, void *v)
+{
+	struct recent_entry *e = v;
+	unsigned int i;
+
+	i = (e->index - 1) % ip_pkt_list_tot;
+	seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u",
+		   NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index);
+	for (i = 0; i < e->nstamps; i++)
+		seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]);
+	seq_printf(seq, "\n");
+	return 0;
+}
 
-	if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return;
+static struct seq_operations recent_seq_ops = {
+	.start		= recent_seq_start,
+	.next		= recent_seq_next,
+	.stop		= recent_seq_stop,
+	.show		= recent_seq_show,
+};
 
-	/* Lock the linked list while we play with it */
-	spin_lock_bh(&recent_lock);
+static int recent_seq_open(struct inode *inode, struct file *file)
+{
+	struct proc_dir_entry *pde = PDE(inode);
+	struct seq_file *seq;
+	struct recent_iter_state *st;
+	int ret;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL)
+		return -ENOMEM;
+	ret = seq_open(file, &recent_seq_ops);
+	if (ret)
+		kfree(st);
+	st->table    = pde->data;
+	seq          = file->private_data;
+	seq->private = st;
+	return ret;
+}
 
-	/* Look for an entry with this name already created */
-	/* Finds the end of the list and the entry before the end if current name does not exist */
-	last_table = NULL;
-	curr_table = r_tables;
-	if(!curr_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() No tables found, leaving.\n");
-#endif
+static ssize_t recent_proc_write(struct file *file, const char __user *input,
+				 size_t size, loff_t *loff)
+{
+	struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+	struct recent_table *t = pde->data;
+	struct recent_entry *e;
+	char buf[sizeof("+255.255.255.255")], *c = buf;
+	u_int32_t addr;
+	int add;
+
+	if (size > sizeof(buf))
+		size = sizeof(buf);
+	if (copy_from_user(buf, input, size))
+		return -EFAULT;
+	while (isspace(*c))
+		c++;
+
+	if (size - (c - buf) < 5)
+		return c - buf;
+	if (!memcmp(c, "clear", 5)) {
+		spin_lock_bh(&recent_lock);
+		recent_table_flush(t);
 		spin_unlock_bh(&recent_lock);
-		return;
+		return c - buf;
 	}
-	while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
 
-	/* If a table does not exist then do nothing and return */
-	if(!curr_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table not found, leaving.\n");
-#endif
-		spin_unlock_bh(&recent_lock);
-		return;
+	switch (*c) {
+	case '-':
+		add = 0;
+		c++;
+		break;
+	case '+':
+		c++;
+	default:
+		add = 1;
+		break;
 	}
+	addr = in_aton(c);
 
-	curr_table->count--;
-
-	/* If count is still non-zero then there are still rules referenceing it so we do nothing */
-	if(curr_table->count) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, non-zero count, leaving.\n");
-#endif
-		spin_unlock_bh(&recent_lock);
-		return;
+	spin_lock_bh(&recent_lock);
+	e = recent_entry_lookup(t, addr, 0);
+	if (e == NULL) {
+		if (add)
+			recent_entry_init(t, addr, 0);
+	} else {
+		if (add)
+			recent_entry_update(e);
+		else
+			recent_entry_remove(t, e);
 	}
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, zero count, removing.\n");
-#endif
-
-	/* Count must be zero so we remove this table from the list */
-	if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
-
 	spin_unlock_bh(&recent_lock);
+	return size;
+}
 
-	/* lock to make sure any late-runners still using this after we removed it from
-	 * the list finish up then remove everything */
-	spin_lock_bh(&curr_table->list_lock);
-	spin_unlock_bh(&curr_table->list_lock);
-
-#ifdef CONFIG_PROC_FS
-	if(curr_table->status_proc) remove_proc_entry(curr_table->name,proc_net_ipt_recent);
+static struct file_operations recent_fops = {
+	.open		= recent_seq_open,
+	.read		= seq_read,
+	.write		= recent_proc_write,
+	.release	= seq_release_private,
+	.owner		= THIS_MODULE,
+};
 #endif /* CONFIG_PROC_FS */
-	vfree(curr_table->table[0].last_pkts);
-	vfree(curr_table->table);
-	vfree(curr_table->hash_table);
-	vfree(curr_table->time_info);
-	vfree(curr_table);
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() left.\n");
-#endif
 
-	return;
-}
-
-/* This is the structure we pass to ipt_register to register our
- * module with iptables.
- */
 static struct ipt_match recent_match = {
 	.name		= "recent",
-	.match		= match,
+	.match		= ipt_recent_match,
 	.matchsize	= sizeof(struct ipt_recent_info),
-	.checkentry	= checkentry,
-	.destroy	= destroy,
-	.me		= THIS_MODULE
+	.checkentry	= ipt_recent_checkentry,
+	.destroy	= ipt_recent_destroy,
+	.me		= THIS_MODULE,
 };
 
-/* Kernel module initialization. */
 static int __init ipt_recent_init(void)
 {
-	int err, count;
+	int err;
 
-	printk(version);
-#ifdef CONFIG_PROC_FS
-	proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net);
-	if(!proc_net_ipt_recent) return -ENOMEM;
-#endif
-
-	if(ip_list_hash_size && ip_list_hash_size <= ip_list_tot) {
-	  printk(KERN_WARNING RECENT_NAME ": ip_list_hash_size too small, resetting to default.\n");
-	  ip_list_hash_size = 0;
-	}
-
-	if(!ip_list_hash_size) {
-		ip_list_hash_size = ip_list_tot*3;
-		count = 2*2;
-		while(ip_list_hash_size > count) count = count*2;
-		ip_list_hash_size = count;
-	}
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_list_hash_size: %d\n",ip_list_hash_size);
-#endif
+	if (!ip_list_tot || !ip_pkt_list_tot)
+		return -EINVAL;
+	ip_list_hash_size = 1 << fls(ip_list_tot);
 
 	err = ipt_register_match(&recent_match);
+#ifdef CONFIG_PROC_FS
 	if (err)
-		remove_proc_entry("ipt_recent", proc_net);
+		return err;
+	proc_dir = proc_mkdir("ipt_recent", proc_net);
+	if (proc_dir == NULL) {
+		ipt_unregister_match(&recent_match);
+		err = -ENOMEM;
+	}
+#endif
 	return err;
 }
 
-/* Kernel module destruction. */
-static void __exit ipt_recent_fini(void)
+static void __exit ipt_recent_exit(void)
 {
 	ipt_unregister_match(&recent_match);
-
-	remove_proc_entry("ipt_recent",proc_net);
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("ipt_recent", proc_net);
+#endif
 }
 
-/* Register our module with the kernel. */
 module_init(ipt_recent_init);
-module_exit(ipt_recent_fini);
+module_exit(ipt_recent_exit);

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-15  8:25                 ` Patrick McHardy
@ 2006-05-15 14:28                   ` Stephen Frost
  2006-05-15 18:49                     ` Patrick McHardy
  0 siblings, 1 reply; 32+ messages in thread
From: Stephen Frost @ 2006-05-15 14:28 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Amin Azez, David S. Miller, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 2029 bytes --]

* Patrick McHardy (kaber@trash.net) wrote:
> Anyway, here goes the first shot at a replacement, it should be fully
> compatible. Comments and testing welcome.

This patch didn't apply cleanly against 2.6.16; I didn't think there had
been other changes since then.  As it was an entire replacement I just
pulled out the '[+ ]' lines from the patch.  Hopefully this doesn't lead
to problems in my review.

It probably would have been better to integrate it with ipset, as I've
mentioned previously.  Other comments:

recent_entry_init() appears to just look for something to delete when
the maximum number of entries has been reached, starting from the hash
position of the address.  The original ipt_recent, quite intentionally,
looked for the *oldest* address to replace.  This meant that the list
only had to be large enough to cover the number of addresses seen in a
given time-period.  This change would mean that the list would need to
be large enough to hold all addresses seen always, to be able to enforce
the time-based rules ipt_recent was written for.

ie: List of 100 addresses.  Highest timeout value in the ruleset is 60
seconds.  Average of 100 individual addresses in a 60-second timeframe.
The old ipt_recent would correctly enforce the 60-second requirement in
the ruleset.  With the new version, as soon as the list was full the
next address could replace any address in the list, even if that address
was only 15 seconds old.

One way to handle this would be to track the highest time value in the
rulesets but as the ruleset is dynamic you could end up throwing away an
address which would have been caught by a rule that was about to be
added.  The old module was written with the expectation of the list
always being full and that it would only be less-than-full shortly after
booting.  By then only removing the oldest entry in the table for each
new address seen the maximum amount of time possible for the given table
size and distinct addresses seen is achieved.

The rest looks good, thanks.

	Stephen

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-15 14:28                   ` Stephen Frost
@ 2006-05-15 18:49                     ` Patrick McHardy
  2006-05-15 19:27                       ` Stephen Frost
  0 siblings, 1 reply; 32+ messages in thread
From: Patrick McHardy @ 2006-05-15 18:49 UTC (permalink / raw)
  To: Stephen Frost
  Cc: Amin Azez, David S. Miller, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

Stephen Frost wrote:
> * Patrick McHardy (kaber@trash.net) wrote:
> 
>>Anyway, here goes the first shot at a replacement, it should be fully
>>compatible. Comments and testing welcome.
> 
> 
> This patch didn't apply cleanly against 2.6.16; I didn't think there had
> been other changes since then.  As it was an entire replacement I just
> pulled out the '[+ ]' lines from the patch.  Hopefully this doesn't lead
> to problems in my review.


That should be fine. That patch applies on top of Jespers patch which
started this thread, which I plan to push to Dave today.

> It probably would have been better to integrate it with ipset, as I've
> mentioned previously.  Other comments:


Unfortunately we need to provide compatibility.

> recent_entry_init() appears to just look for something to delete when
> the maximum number of entries has been reached, starting from the hash
> position of the address.  The original ipt_recent, quite intentionally,
> looked for the *oldest* address to replace.  This meant that the list
> only had to be large enough to cover the number of addresses seen in a
> given time-period.  This change would mean that the list would need to
> be large enough to hold all addresses seen always, to be able to enforce
> the time-based rules ipt_recent was written for.
> 
> ie: List of 100 addresses.  Highest timeout value in the ruleset is 60
> seconds.  Average of 100 individual addresses in a 60-second timeframe.
> The old ipt_recent would correctly enforce the 60-second requirement in
> the ruleset.  With the new version, as soon as the list was full the
> next address could replace any address in the list, even if that address
> was only 15 seconds old.
> 
> One way to handle this would be to track the highest time value in the
> rulesets but as the ruleset is dynamic you could end up throwing away an
> address which would have been caught by a rule that was about to be
> added.  The old module was written with the expectation of the list
> always being full and that it would only be less-than-full shortly after
> booting.  By then only removing the oldest entry in the table for each
> new address seen the maximum amount of time possible for the given table
> size and distinct addresses seen is achieved.


I wasn't sure whether eviction was happening intentional in the old code
at all - still not able to locate the code where this happens, just
noticed that it does do eviction when I manually tried to trigger
a table overflow by adding entries through /proc. Anyway, it should
be easy to fix by keeping an additional lru list. I'll post
an updated patch soon.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-15 18:49                     ` Patrick McHardy
@ 2006-05-15 19:27                       ` Stephen Frost
  2006-05-15 20:09                         ` Patrick McHardy
  0 siblings, 1 reply; 32+ messages in thread
From: Stephen Frost @ 2006-05-15 19:27 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Amin Azez, David S. Miller, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 3080 bytes --]

* Patrick McHardy (kaber@trash.net) wrote:
> I wasn't sure whether eviction was happening intentional in the old code
> at all - still not able to locate the code where this happens, just
> noticed that it does do eviction when I manually tried to trigger
> a table overflow by adding entries through /proc. Anyway, it should
> be easy to fix by keeping an additional lru list. I'll post
> an updated patch soon.

It was always done intentionally; as I mentioned, it was originally
written with the expectation of the table always being full.  That was
also why I used one large malloc'd table and the hash chaining that I
did- I always knew ahead of time exactly how much memory I'd be using as
a running-set and never needed to do any allocation during operation.
In hindsight I can see that the additional complexity from it was
perhaps not worth the benefit that I saw from it.

The eviction is handled through the 'time_info_list'.  This is basically
just an always-ordered (by time) array of positions into the main table.
Line 504 (from stock 2.6.16) is where the list is used to add a new
entry at the end of the list (replacing the oldest address).  'time_pos'
points to the oldest entry.  The 'position' is then used to clear out
the entry associated with this address from the hash table and the main
table.  These are then replaced with the new address information and the
time_pos is adjusted accordingly.  This didn't help the complexity as it
meant I was tracking through different systems the position of each
address in the time_info_list, the main table, and the hash table.
Using the lists might make this a bit easier to implement though.

Then on line 566, if a new packet has come in for an existing address,
we have to move that address up to the top of the time_info_list as it
is now the 'most recent'.  As someone else mentioned, this might have
been better done using 'memmove' but I wasn't sure about its use or
performance in the kernel.  This is done again on line 617 when removing
an address, which is expected to be a somewhat rare event (where an
address is explicitly removed instead of just expiring).  One issue I
was concerned about was that I really didn't want the system to become
unhappy if a huge number of different addresses suddenly came in (more
than the list could support and/or more than would be sensible to try to
allocate memory to track).

I'm really not sure why I didn't break out this code into more
functions.  It certainly would have made things much clearer/simpler.  I
think I was (without any particular reason for it) concerned about
adding too many functions or calling things from the match() function.
As for why I didn't use existing kernel structures, well, I wasn't aware
of them in part and when I was asking about things I was asking about
more complicated things (such as a generic storage/hashing system) than
really made sense.  I'm not sure I would have used the lists anyway
since I liked the general idea of just having the one 'main' table but
it does seem to make things cleaner.

	Thanks,

		Stephen

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-15 19:27                       ` Stephen Frost
@ 2006-05-15 20:09                         ` Patrick McHardy
  2006-05-15 20:41                           ` Stephen Frost
  0 siblings, 1 reply; 32+ messages in thread
From: Patrick McHardy @ 2006-05-15 20:09 UTC (permalink / raw)
  To: Stephen Frost
  Cc: Amin Azez, David S. Miller, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 3434 bytes --]

Stephen Frost wrote:
> * Patrick McHardy (kaber@trash.net) wrote:
> 
>>I wasn't sure whether eviction was happening intentional in the old code
>>at all - still not able to locate the code where this happens, just
>>noticed that it does do eviction when I manually tried to trigger
>>a table overflow by adding entries through /proc. Anyway, it should
>>be easy to fix by keeping an additional lru list. I'll post
>>an updated patch soon.
> 
> 
> It was always done intentionally; as I mentioned, it was originally
> written with the expectation of the table always being full.  That was
> also why I used one large malloc'd table and the hash chaining that I
> did- I always knew ahead of time exactly how much memory I'd be using as
> a running-set and never needed to do any allocation during operation.
> In hindsight I can see that the additional complexity from it was
> perhaps not worth the benefit that I saw from it.
> 
> The eviction is handled through the 'time_info_list'.  This is basically
> just an always-ordered (by time) array of positions into the main table.
> Line 504 (from stock 2.6.16) is where the list is used to add a new
> entry at the end of the list (replacing the oldest address).  'time_pos'
> points to the oldest entry.  The 'position' is then used to clear out
> the entry associated with this address from the hash table and the main
> table.  These are then replaced with the new address information and the
> time_pos is adjusted accordingly.  This didn't help the complexity as it
> meant I was tracking through different systems the position of each
> address in the time_info_list, the main table, and the hash table.
> Using the lists might make this a bit easier to implement though.
> 
> Then on line 566, if a new packet has come in for an existing address,
> we have to move that address up to the top of the time_info_list as it
> is now the 'most recent'.  As someone else mentioned, this might have
> been better done using 'memmove' but I wasn't sure about its use or
> performance in the kernel.  This is done again on line 617 when removing
> an address, which is expected to be a somewhat rare event (where an
> address is explicitly removed instead of just expiring).  One issue I
> was concerned about was that I really didn't want the system to become
> unhappy if a huge number of different addresses suddenly came in (more
> than the list could support and/or more than would be sensible to try to
> allocate memory to track).
> 
> I'm really not sure why I didn't break out this code into more
> functions.  It certainly would have made things much clearer/simpler.  I
> think I was (without any particular reason for it) concerned about
> adding too many functions or calling things from the match() function.
> As for why I didn't use existing kernel structures, well, I wasn't aware
> of them in part and when I was asking about things I was asking about
> more complicated things (such as a generic storage/hashing system) than
> really made sense.  I'm not sure I would have used the lists anyway
> since I liked the general idea of just having the one 'main' table but
> it does seem to make things cleaner.


Indeed :) Thanks for your explanation and review Stephen.

This is the updated patch, it changes the eviction strategy
to LRU and fixes a bug related to TTL handling, the TTL stored
in the entry should only be overwritten if the IPT_RECENT_TTL
flag is set.


[-- Attachment #2: x --]
[-- Type: text/plain, Size: 46819 bytes --]

[NETFILTER]: Replace ipt_recent module

Replace the totally unmaintainable ipt_recent module by a rewritten
version that should be fully compatible.

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit e4f33e5c65efaf65d558365fd49ad5d83b13813d
tree fc8a4681aad273bb4f9a6c9c9484d331d7aac064
parent d8c3291c73b958243b33f8509d4507e76dafd055
author Patrick McHardy <kaber@trash.net> Mon, 15 May 2006 22:03:22 +0200
committer Patrick McHardy <kaber@trash.net> Mon, 15 May 2006 22:03:22 +0200

 net/ipv4/netfilter/ipt_recent.c | 1274 ++++++++++++---------------------------
 1 files changed, 380 insertions(+), 894 deletions(-)

diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
index 1438432..2e54062 100644
--- a/net/ipv4/netfilter/ipt_recent.c
+++ b/net/ipv4/netfilter/ipt_recent.c
@@ -1,1007 +1,493 @@
-/* Kernel module to check if the source address has been seen recently. */
-/* Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org */
-/* Author: Stephen Frost <sfrost@snowman.net> */
-/* Project Page: http://snowman.net/projects/ipt_recent/ */
-/* This software is distributed under the terms of the GPL, Version 2 */
-/* This copyright does not cover user programs that use kernel services
- * by normal system calls. */
-
-#include <linux/module.h>
-#include <linux/skbuff.h>
+/*
+ * Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This is a replacement of the old ipt_recent module, which carried the
+ * following copyright notice:
+ *
+ * Author: Stephen Frost <sfrost@snowman.net>
+ * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org 
+ */
+#include <linux/init.h>
+#include <linux/moduleparam.h>
 #include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <asm/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
 #include <linux/ctype.h>
-#include <linux/ip.h>
-#include <linux/vmalloc.h>
-#include <linux/moduleparam.h>
+#include <linux/list.h>
+#include <linux/random.h>
+#include <linux/jhash.h>
+#include <linux/bitops.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
 
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_recent.h>
 
-#undef DEBUG
-#define HASH_LOG 9
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_DESCRIPTION("IP tables recently seen matching module");
+MODULE_LICENSE("GPL");
 
-/* Defaults, these can be overridden on the module command-line. */
 static unsigned int ip_list_tot = 100;
 static unsigned int ip_pkt_list_tot = 20;
 static unsigned int ip_list_hash_size = 0;
 static unsigned int ip_list_perms = 0644;
-#ifdef DEBUG
-static int debug = 1;
-#endif
-
-static char version[] =
-KERN_INFO RECENT_NAME " " RECENT_VER ": Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n";
-
-MODULE_AUTHOR("Stephen Frost <sfrost@snowman.net>");
-MODULE_DESCRIPTION("IP tables recently seen matching module " RECENT_VER);
-MODULE_LICENSE("GPL");
 module_param(ip_list_tot, uint, 0400);
 module_param(ip_pkt_list_tot, uint, 0400);
 module_param(ip_list_hash_size, uint, 0400);
 module_param(ip_list_perms, uint, 0400);
-#ifdef DEBUG
-module_param(debug, bool, 0600);
-MODULE_PARM_DESC(debug,"enable debugging output");
-#endif
-MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list");
-MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember");
-MODULE_PARM_DESC(ip_list_hash_size,"size of hash table used to look up IPs");
-MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_recent/* files");
-
-/* Structure of our list of recently seen addresses. */
-struct recent_ip_list {
-	u_int32_t addr;
-	u_int8_t  ttl;
-	unsigned long last_seen;
-	unsigned long *last_pkts;
-	u_int32_t oldest_pkt;
-	u_int32_t hash_entry;
-	u_int32_t time_pos;
-};
-
-struct time_info_list {
-	u_int32_t position;
-	u_int32_t time;
+MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
+MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember");
+MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
+MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files");
+
+
+struct recent_entry {
+	struct list_head	list;
+	struct list_head	lru_list;
+	u_int32_t		addr;
+	u_int8_t		ttl;
+	unsigned int		index;
+	unsigned int		nstamps;
+	unsigned long		stamps[0];
 };
 
-/* Structure of our linked list of tables of recent lists. */
-struct recent_ip_tables {
-	char name[IPT_RECENT_NAME_LEN];
-	int count;
-	int time_pos;
-	struct recent_ip_list *table;
-	struct recent_ip_tables *next;
-	spinlock_t list_lock;
-	int *hash_table;
-	struct time_info_list *time_info;
+struct recent_table {
+	struct list_head	list;
+	char			name[IPT_RECENT_NAME_LEN];
 #ifdef CONFIG_PROC_FS
-	struct proc_dir_entry *status_proc;
-#endif /* CONFIG_PROC_FS */
+	struct proc_dir_entry	*proc;
+#endif
+	unsigned int		refcnt;
+	unsigned int		entries;
+	struct list_head	lru_list;
+	struct list_head	iphash[0];
 };
 
-/* Our current list of addresses we have recently seen.
- * Only added to on a --set, and only updated on --set || --update 
- */
-static struct recent_ip_tables *r_tables = NULL;
-
-/* We protect r_list with this spinlock so two processors are not modifying
- * the list at the same time. 
- */
+static LIST_HEAD(tables);
 static DEFINE_SPINLOCK(recent_lock);
 
 #ifdef CONFIG_PROC_FS
-/* Our /proc/net/ipt_recent entry */
-static struct proc_dir_entry *proc_net_ipt_recent = NULL;
+static struct proc_dir_entry	*proc_dir;
+static struct file_operations	recent_fops;
 #endif
 
-/* Function declaration for later. */
-static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop);
-
-/* Function to hash a given address into the hash table of table_size size */
-static int hash_func(unsigned int addr, int table_size)
-{
-	int result = 0;
-	unsigned int value = addr;
-	do { result ^= value; } while((value >>= HASH_LOG));
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": %d = hash_func(%u,%d)\n",
-			 result & (table_size - 1),
-			 addr,
-			 table_size);
-#endif
+static u_int32_t hash_rnd;
+static int hash_rnd_initted;
 
-	return(result & (table_size - 1));
-}
-
-#ifdef CONFIG_PROC_FS
-/* This is the function which produces the output for our /proc output
- * interface which lists each IP address, the last seen time and the 
- * other recent times the address was seen.
- */
-
-static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+static u_int32_t recent_entry_hash(u_int32_t addr)
 {
-	int len = 0, count, last_len = 0, pkt_count;
-	off_t pos = 0;
-	off_t begin = 0;
-	struct recent_ip_tables *curr_table;
-
-	curr_table = (struct recent_ip_tables*) data;
-
-	spin_lock_bh(&curr_table->list_lock);
-	for(count = 0; count < ip_list_tot; count++) {
-		if(!curr_table->table[count].addr) continue;
-		last_len = len;
-		len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].addr));
-		len += sprintf(buffer+len,"ttl: %u ",curr_table->table[count].ttl);
-		len += sprintf(buffer+len,"last_seen: %lu ",curr_table->table[count].last_seen);
-		len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[count].oldest_pkt);
-		len += sprintf(buffer+len,"last_pkts: %lu",curr_table->table[count].last_pkts[0]);
-		for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) {
-			if(!curr_table->table[count].last_pkts[pkt_count]) break;
-			len += sprintf(buffer+len,", %lu",curr_table->table[count].last_pkts[pkt_count]);
-		}
-		len += sprintf(buffer+len,"\n");
-		pos = begin + len;
-		if(pos < offset) { len = 0; begin = pos; }
-		if(pos > offset + length) { len = last_len; break; }
+	if (!hash_rnd_initted) {
+		get_random_bytes(&hash_rnd, 4);
+		hash_rnd_initted = 1;
 	}
-
-	*start = buffer + (offset - begin);
-	len -= (offset - begin);
-	if(len > length) len = length;
-
-	spin_unlock_bh(&curr_table->list_lock);
-	return len;
+	return jhash_1word(addr, hash_rnd) & (ip_list_hash_size - 1);
 }
 
-/* ip_recent_ctrl provides an interface for users to modify the table
- * directly.  This allows adding entries, removing entries, and
- * flushing the entire table.
- * This is done by opening up the appropriate table for writing and
- * sending one of:
- * xx.xx.xx.xx   -- Add entry to table with current time
- * +xx.xx.xx.xx  -- Add entry to table with current time
- * -xx.xx.xx.xx  -- Remove entry from table
- * clear         -- Flush table, remove all entries
- */
-
-static int ip_recent_ctrl(struct file *file, const char __user *input, unsigned long size, void *data)
+static struct recent_entry *
+recent_entry_lookup(const struct recent_table *table, u_int32_t addr, u_int8_t ttl)
 {
-	static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
-	u_int32_t val;
-	int base, used = 0;
-	char c, *cp;
-	union iaddr {
-		uint8_t bytes[4];
-		uint32_t word;
-	} res;
-	uint8_t *pp = res.bytes;
-	int digit;
-
-	char buffer[20];
-	int len, check_set = 0, count;
-	u_int32_t addr = 0;
-	struct sk_buff *skb;
-	struct ipt_recent_info *info;
-	struct recent_ip_tables *curr_table;
-
-	curr_table = (struct recent_ip_tables*) data;
-
-	if(size > 20) len = 20; else len = size;
-
-	if(copy_from_user(buffer,input,len)) return -EFAULT;
-
-	if(len < 20) buffer[len] = '\0';
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl len: %d, input: `%.20s'\n",len,buffer);
-#endif
+	struct recent_entry *e;
+	unsigned int h;
+
+	h = recent_entry_hash(addr);
+	list_for_each_entry(e, &table->iphash[h], list)
+		if (e->addr == addr && (!ttl || !e->ttl || ttl == e->ttl))
+			return e;
+	return NULL;
+}
 
-	cp = buffer;
-	while(isspace(*cp)) { cp++; used++; if(used >= len-5) return used; }
+static void recent_entry_remove(struct recent_table *t, struct recent_entry *e)
+{
+	list_del(&e->list);
+	list_del(&e->lru_list);
+	kfree(e);
+	t->entries--;
+}
 
-	/* Check if we are asked to flush the entire table */
-	if(!memcmp(cp,"clear",5)) {
-		used += 5;
-		spin_lock_bh(&curr_table->list_lock);
-		curr_table->time_pos = 0;
-		for(count = 0; count < ip_list_hash_size; count++) {
-			curr_table->hash_table[count] = -1;
-		}
-		for(count = 0; count < ip_list_tot; count++) {
-			curr_table->table[count].last_seen = 0;
-			curr_table->table[count].addr = 0;
-			curr_table->table[count].ttl = 0;
-			memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-			curr_table->table[count].oldest_pkt = 0;
-			curr_table->table[count].time_pos = 0;
-			curr_table->time_info[count].position = count;
-			curr_table->time_info[count].time = 0;
-		}
-		spin_unlock_bh(&curr_table->list_lock);
-		return used;
-	}
+static struct recent_entry *
+recent_entry_init(struct recent_table *t, u_int32_t addr, u_int8_t ttl)
+{
+	struct recent_entry *e;
 
-        check_set = IPT_RECENT_SET;
-	switch(*cp) {
-		case '+': check_set = IPT_RECENT_SET; cp++; used++; break;
-		case '-': check_set = IPT_RECENT_REMOVE; cp++; used++; break;
-		default: if(!isdigit(*cp)) return (used+1); break;
+	if (t->entries >= ip_list_tot) {
+		e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
+		recent_entry_remove(t, e);
 	}
+	e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot,
+	            GFP_ATOMIC);
+	if (e == NULL)
+		return NULL;
+	e->addr      = addr;
+	e->ttl       = ttl;
+	e->stamps[0] = jiffies;
+	e->nstamps   = 1;
+	e->index     = 1;
+	list_add_tail(&e->list, &t->iphash[recent_entry_hash(addr)]);
+	list_add_tail(&e->lru_list, &t->lru_list);
+	t->entries++;
+	return e;
+}
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp,check_set);
-#endif
-	/* Get addr (effectively inet_aton()) */
-	/* Shamelessly stolen from libc, a function in the kernel for doing
-	 * this would, of course, be greatly preferred, but our options appear
-	 * to be rather limited, so we will just do it ourselves here.
-	 */
-	res.word = 0;
-
-	c = *cp;
-	for(;;) {
-		if(!isdigit(c)) return used;
-		val = 0; base = 10; digit = 0;
-		if(c == '0') {
-			c = *++cp;
-			if(c == 'x' || c == 'X') base = 16, c = *++cp;
-			else { base = 8; digit = 1; }
-		}
-		for(;;) {
-			if(isascii(c) && isdigit(c)) {
-				if(base == 8 && (c == '8' || c == '0')) return used;
-				val = (val * base) + (c - '0');
-				c = *++cp;
-				digit = 1;
-			} else if(base == 16 && isascii(c) && isxdigit(c)) {
-				val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A'));
-				c = *++cp;
-				digit = 1;
-			} else break;
-		}
-		if(c == '.') {
-			if(pp > res.bytes + 2 || val > 0xff) return used;
-			*pp++ = val;
-			c = *++cp;
-		} else break;
-	}
-	used = cp - buffer;
-	if(c != '\0' && (!isascii(c) || !isspace(c))) return used;
-	if(c == '\n') used++;
-	if(!digit) return used;
-
-	if(val > max[pp - res.bytes]) return used;
-	addr = res.word | htonl(val);
+static void recent_entry_update(struct recent_entry *e)
+{
+	e->stamps[e->index++] = jiffies;
+	if (e->index > e->nstamps)
+		e->nstamps = e->index;
+	e->index %= ip_pkt_list_tot;
+}
 
-	if(!addr && check_set == IPT_RECENT_SET) return used;
+static struct recent_table *recent_table_lookup(const char *name)
+{
+	struct recent_table *t;
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used);
-#endif
+	list_for_each_entry(t, &tables, list)
+		if (!strcmp(t->name, name))
+			return t;
+	return NULL;
+}
 
-	/* Set up and just call match */
-	info = kmalloc(sizeof(struct ipt_recent_info),GFP_KERNEL);
-	if(!info) { return -ENOMEM; }
-	info->seconds = 0;
-	info->hit_count = 0;
-	info->check_set = check_set;
-	info->invert = 0;
-	info->side = IPT_RECENT_SOURCE;
-	strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN);
-	info->name[IPT_RECENT_NAME_LEN-1] = '\0';
-
-	skb = kmalloc(sizeof(struct sk_buff),GFP_KERNEL);
-	if (!skb) {
-		used = -ENOMEM;
-		goto out_free_info;
-	}
-	skb->nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL);
-	if (!skb->nh.iph) {
-		used = -ENOMEM;
-		goto out_free_skb;
+static void recent_table_flush(struct recent_table *t)
+{
+	struct recent_entry *e, *next;
+	unsigned int i;
+	
+	for (i = 0; i < ip_list_hash_size; i++) {
+		list_for_each_entry_safe(e, next, &t->iphash[i], list)
+			recent_entry_remove(t, e);
 	}
-
-	skb->nh.iph->saddr = addr;
-	skb->nh.iph->daddr = 0;
-	/* Clear ttl since we have no way of knowing it */
-	skb->nh.iph->ttl = 0;
-	match(skb,NULL,NULL,NULL,info,0,0,NULL);
-
-	kfree(skb->nh.iph);
-out_free_skb:
-	kfree(skb);
-out_free_info:
-	kfree(info);
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": Leaving ip_recent_ctrl addr: %u used: %d\n",addr,used);
-#endif
-	return used;
 }
 
-#endif /* CONFIG_PROC_FS */
-
-/* 'match' is our primary function, called by the kernel whenever a rule is
- * hit with our module as an option to it.
- * What this function does depends on what was specifically asked of it by
- * the user:
- * --set -- Add or update last seen time of the source address of the packet
- *   -- matchinfo->check_set == IPT_RECENT_SET
- * --rcheck -- Just check if the source address is in the list
- *   -- matchinfo->check_set == IPT_RECENT_CHECK
- * --update -- If the source address is in the list, update last_seen
- *   -- matchinfo->check_set == IPT_RECENT_UPDATE
- * --remove -- If the source address is in the list, remove it
- *   -- matchinfo->check_set == IPT_RECENT_REMOVE
- * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
- *   -- matchinfo->seconds
- * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
- *   -- matchinfo->hit_count
- * --seconds and --hitcount can be combined
- */
 static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop)
+ipt_recent_match(const struct sk_buff *skb,
+		 const struct net_device *in, const struct net_device *out,
+		 const struct xt_match *match, const void *matchinfo,
+		 int offset, unsigned int protoff, int *hotdrop)
 {
-	int pkt_count, hits_found, ans;
-	unsigned long now;
 	const struct ipt_recent_info *info = matchinfo;
-	u_int32_t addr = 0, time_temp;
-	u_int8_t ttl = skb->nh.iph->ttl;
-	int *hash_table;
-	int orig_hash_result, hash_result, temp, location = 0, time_loc, end_collision_chain = -1;
-	struct time_info_list *time_info;
-	struct recent_ip_tables *curr_table;
-	struct recent_ip_tables *last_table;
-	struct recent_ip_list *r_list;
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match() called\n");
-#endif
-
-	/* Default is false ^ info->invert */
-	ans = info->invert;
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): name = '%s'\n",info->name);
-#endif
-
-	/* if out != NULL then routing has been done and TTL changed.
-	 * We change it back here internally for match what came in before routing. */
-	if(out) ttl++;
-
-	/* Find the right table */
-	spin_lock_bh(&recent_lock);
-	curr_table = r_tables;
-	while( (last_table = curr_table) && strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (curr_table = curr_table->next) );
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): table found('%s')\n",info->name);
-#endif
-
-	spin_unlock_bh(&recent_lock);
-
-	/* Table with this name not found, match impossible */
-	if(!curr_table) { return ans; }
-
-	/* Make sure no one is changing the list while we work with it */
-	spin_lock_bh(&curr_table->list_lock);
-
-	r_list = curr_table->table;
-	if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr;
-
-	if(!addr) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match() address (%u) invalid, leaving.\n",addr);
-#endif
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
-	}
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr,ttl,skb->nh.iph->ttl);
-#endif
-
-	/* Get jiffies now in case they changed while we were waiting for a lock */
-	now = jiffies;
-	hash_table = curr_table->hash_table;
-	time_info = curr_table->time_info;
-
-	orig_hash_result = hash_result = hash_func(addr,ip_list_hash_size);
-	/* Hash entry at this result used */
-	/* Check for TTL match if requested.  If TTL is zero then a match would never
-	 * happen, so match regardless of existing TTL in that case.  Zero means the
-	 * entry was added via the /proc interface anyway, so we will just use the
-	 * first TTL we get for that IP address. */
-	if(info->check_set & IPT_RECENT_TTL) {
-		while(hash_table[hash_result] != -1 && !(r_list[hash_table[hash_result]].addr == addr &&
-			(!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) {
-			/* Collision in hash table */
-			hash_result = (hash_result + 1) % ip_list_hash_size;
-		}
-	} else {
-		while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) {
-			/* Collision in hash table */
-			hash_result = (hash_result + 1) % ip_list_hash_size;
-		}
-	}
-
-	if(hash_table[hash_result] == -1 && !(info->check_set & IPT_RECENT_SET)) {
-		/* IP not in list and not asked to SET */
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
+	struct recent_table *t;
+	struct recent_entry *e;
+	u_int32_t addr;
+	u_int8_t ttl;
+	int ret = info->invert;
+
+	if (info->side == IPT_RECENT_DEST)
+		addr = skb->nh.iph->daddr;
+	else
+		addr = skb->nh.iph->saddr;
+
+	ttl = 0;
+	if (info->check_set & IPT_RECENT_TTL) {
+		ttl = skb->nh.iph->ttl;
+		/* use TTL as seen before forwaring */
+		if (out && !skb->sk)
+			ttl++;
 	}
 
-	/* Check if we need to handle the collision, do not need to on REMOVE */
-	if(orig_hash_result != hash_result && !(info->check_set & IPT_RECENT_REMOVE)) {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
-				 orig_hash_result,
-				 hash_result,
-				 r_list[hash_table[orig_hash_result]].addr,
-				 addr);
-#endif
-
-		/* We had a collision.
-		 * orig_hash_result is where we started, hash_result is where we ended up.
-		 * So, swap them because we are likely to see the same guy again sooner */
-#ifdef DEBUG
-		if(debug) {
-		  printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table[orig_hash_result]);
-		  printk(KERN_INFO RECENT_NAME ": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
-				r_list[hash_table[orig_hash_result]].hash_entry);
-		}
-#endif
-
-		r_list[hash_table[orig_hash_result]].hash_entry = hash_result;
-
-
-		temp = hash_table[orig_hash_result];
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[hash_result] = %d\n",hash_table[hash_result]);
-#endif
-		hash_table[orig_hash_result] = hash_table[hash_result];
-		hash_table[hash_result] = temp;
-		temp = hash_result;
-		hash_result = orig_hash_result;
-		orig_hash_result = temp;
-		time_info[r_list[hash_table[orig_hash_result]].time_pos].position = hash_table[orig_hash_result];
-		if(hash_table[hash_result] != -1) {
-			r_list[hash_table[hash_result]].hash_entry = hash_result;
-			time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-		}
-
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision handled.\n");
-#endif
+	spin_lock_bh(&recent_lock);
+	t = recent_table_lookup(info->name);
+	e = recent_entry_lookup(t, addr, ttl);
+	if (e == NULL) {
+		if (!(info->check_set & IPT_RECENT_SET))
+			goto out;
+		e = recent_entry_init(t, addr, ttl);
+		if (e == NULL)
+			*hotdrop = 1;
+		ret ^= 1;
+		goto out;
 	}
 
-	if(hash_table[hash_result] == -1) {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): New table entry. (hr: %d,ha: %u)\n",
-				 hash_result, addr);
-#endif
-
-		/* New item found and IPT_RECENT_SET, so we need to add it */
-		location = time_info[curr_table->time_pos].position;
-		hash_table[r_list[location].hash_entry] = -1;
-		hash_table[hash_result] = location;
-		memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-		r_list[location].time_pos = curr_table->time_pos;
-		r_list[location].addr = addr;
-		r_list[location].ttl = ttl;
-		r_list[location].last_seen = now;
-		r_list[location].oldest_pkt = 1;
-		r_list[location].last_pkts[0] = now;
-		r_list[location].hash_entry = hash_result;
-		time_info[curr_table->time_pos].time = r_list[location].last_seen;
-		curr_table->time_pos = (curr_table->time_pos + 1) % ip_list_tot;
-
-		ans = !info->invert;
-	} else {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Existing table entry. (hr: %d,ha: %u)\n",
-				 hash_result,
-				 addr);
-#endif
-
-		/* Existing item found */
-		location = hash_table[hash_result];
-		/* We have a match on address, now to make sure it meets all requirements for a
-		 * full match. */
-		if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) {
-			if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert;
-			if(info->seconds && !info->hit_count) {
-				if(time_before_eq(now,r_list[location].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert;
-			}
-			if(info->seconds && info->hit_count) {
-				for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
-					if(r_list[location].last_pkts[pkt_count] == 0) break;
-					if(time_before_eq(now,r_list[location].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++;
-				}
-				if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
-			}
-			if(info->hit_count && !info->seconds) {
-				for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
-					if(r_list[location].last_pkts[pkt_count] == 0) break;
-					hits_found++;
-				}
-				if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
-			}
-		}
-#ifdef DEBUG
-		if(debug) {
-			if(ans)
-				printk(KERN_INFO RECENT_NAME ": match(): match addr: %u\n",addr);
-			else
-				printk(KERN_INFO RECENT_NAME ": match(): no match addr: %u\n",addr);
-		}
-#endif
-
-		/* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
-		 * current timestamp to the last_seen. */
-		if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": match(): SET or UPDATE; updating time info.\n");
-#endif
-			/* Have to update our time info */
-			time_loc = r_list[location].time_pos;
-			time_info[time_loc].time = now;
-			time_info[time_loc].position = location;
-			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) {
-				time_temp = time_info[time_loc].time;
-				time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
-				time_info[(time_loc+1)%ip_list_tot].time = time_temp;
-				time_temp = time_info[time_loc].position;
-				time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
-				time_info[(time_loc+1)%ip_list_tot].position = time_temp;
-				r_list[time_info[time_loc].position].time_pos = time_loc;
-				r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
-				time_loc = (time_loc+1) % ip_list_tot;
-			}
-			r_list[location].time_pos = time_loc;
-			r_list[location].ttl = ttl;
-			r_list[location].last_pkts[r_list[location].oldest_pkt] = now;
-			r_list[location].oldest_pkt = ++r_list[location].oldest_pkt % ip_pkt_list_tot;
-			r_list[location].last_seen = now;
-		}
-		/* If we have been asked to remove the entry from the list, just set it to 0 */
-		if(info->check_set & IPT_RECENT_REMOVE) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result,hash_result);
-#endif
-			/* Check if this is part of a collision chain */
-			while(hash_table[(orig_hash_result+1) % ip_list_hash_size] != -1) {
-				orig_hash_result++;
-				if(hash_func(r_list[hash_table[orig_hash_result]].addr,ip_list_hash_size) == hash_result) {
-					/* Found collision chain, how deep does this rabbit hole go? */
-#ifdef DEBUG
-					if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; found collision chain.\n");
-#endif
-					end_collision_chain = orig_hash_result;
-				}
-			}
-			if(end_collision_chain != -1) {
-#ifdef DEBUG
-				if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; part of collision chain, moving to end.\n");
-#endif
-				/* Part of a collision chain, swap it with the end of the chain
-				 * before removing. */
-				r_list[hash_table[end_collision_chain]].hash_entry = hash_result;
-				temp = hash_table[end_collision_chain];
-				hash_table[end_collision_chain] = hash_table[hash_result];
-				hash_table[hash_result] = temp;
-				time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-				hash_result = end_collision_chain;
-				r_list[hash_table[hash_result]].hash_entry = hash_result;
-				time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-			}
-			location = hash_table[hash_result];
-			hash_table[r_list[location].hash_entry] = -1;
-			time_loc = r_list[location].time_pos;
-			time_info[time_loc].time = 0;
-			time_info[time_loc].position = location;
-			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) {
-				time_temp = time_info[time_loc].time;
-				time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
-				time_info[(time_loc+1)%ip_list_tot].time = time_temp;
-				time_temp = time_info[time_loc].position;
-				time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
-				time_info[(time_loc+1)%ip_list_tot].position = time_temp;
-				r_list[time_info[time_loc].position].time_pos = time_loc;
-				r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
-				time_loc = (time_loc+1) % ip_list_tot;
+	if (info->check_set & IPT_RECENT_SET)
+		ret ^= 1;
+	else if (info->check_set & IPT_RECENT_REMOVE) {
+		recent_entry_remove(t, e);
+		ret ^= 1;
+	} else if (info->check_set & (IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) {
+		unsigned long t = jiffies - info->seconds * HZ;
+		unsigned int i, hits = 0;
+
+		for (i = 0; i < e->nstamps; i++) {
+			if (info->seconds && time_after(t, e->stamps[i]))
+				continue;
+			if (!info->hit_count || ++hits >= info->hit_count) {
+				ret ^= 1;
+				break;
 			}
-			r_list[location].time_pos = time_loc;
-			r_list[location].last_seen = 0;
-			r_list[location].addr = 0;
-			r_list[location].ttl = 0;
-			memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-			r_list[location].oldest_pkt = 0;
-			ans = !info->invert;
 		}
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
 	}
 
-	spin_unlock_bh(&curr_table->list_lock);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match() left.\n");
-#endif
-	return ans;
+	if (info->check_set & IPT_RECENT_SET ||
+	    (info->check_set & IPT_RECENT_UPDATE && ret)) {
+		recent_entry_update(e);
+		if (info->check_set & IPT_RECENT_TTL)
+			e->ttl = ttl;
+	}
+out:
+	spin_unlock_bh(&recent_lock);
+	return ret;
 }
 
-/* This function is to verify that the rule given during the userspace iptables
- * command is correct.
- * If the command is valid then we check if the table name referred to by the
- * rule exists, if not it is created.
- */
 static int
-checkentry(const char *tablename,
-           const void *ip,
-	   const struct xt_match *match,
-           void *matchinfo,
-           unsigned int matchsize,
-           unsigned int hook_mask)
+ipt_recent_checkentry(const char *tablename, const void *ip,
+		      const struct xt_match *match, void *matchinfo,
+		      unsigned int matchsize, unsigned int hook_mask)
 {
-	int flag = 0, c;
-	unsigned long *hold;
 	const struct ipt_recent_info *info = matchinfo;
-	struct recent_ip_tables *curr_table, *find_table, *last_table;
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() entered.\n");
-#endif
+	struct recent_table *t;
+	unsigned i;
+	int ret = 0;
 
-	/* seconds and hit_count only valid for CHECK/UPDATE */
-	if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; }
-	if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; }
-	if(info->check_set & IPT_RECENT_CHECK) flag++;
-	if(info->check_set & IPT_RECENT_UPDATE) flag++;
-
-	/* One and only one of these should ever be set */
-	if(flag != 1) return 0;
-
-	/* Name must be set to something */
-	if(!info->name || !info->name[0]) return 0;
+	if (hweight8(info->check_set &
+		     (IPT_RECENT_SET | IPT_RECENT_REMOVE |
+		      IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) != 1)
+		return 0;
+	if (info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE) &&
+	    (info->seconds || info->hit_count))
+		return 0;
+	if (info->name[0] == '\0' ||
+	    strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN)
+		return 0;
 
-	/* Things look good, create a list for this if it does not exist */
-	/* Lock the linked list while we play with it */
 	spin_lock_bh(&recent_lock);
-
-	/* Look for an entry with this name already created */
-	/* Finds the end of the list and the entry before the end if current name does not exist */
-	find_table = r_tables;
-	while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
-
-	/* If a table already exists just increment the count on that table and return */
-	if(find_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), incrementing count.\n",info->name);
-#endif
-		find_table->count++;
-		spin_unlock_bh(&recent_lock);
-		return 1;
+	t = recent_table_lookup(info->name);
+	if (t != NULL) {
+		t->refcnt++;
+		ret = 1;
+		goto out;
 	}
 
-	spin_unlock_bh(&recent_lock);
-
-	/* Table with this name not found */
-	/* Allocate memory for new linked list item */
-
-#ifdef DEBUG
-	if(debug) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: no table found (%s)\n",info->name);
-		printk(KERN_INFO RECENT_NAME ": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables));
+	t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
+		    GFP_ATOMIC);
+	if (t == NULL)
+		goto out;
+	strcpy(t->name, info->name);
+	INIT_LIST_HEAD(&t->lru_list);
+	for (i = 0; i < ip_list_hash_size; i++)
+		INIT_LIST_HEAD(&t->iphash[i]);
+#ifdef CONFIG_PROC_FS
+	t->proc = create_proc_entry(t->name, ip_list_perms, proc_dir);
+	if (t->proc == NULL) {
+		kfree(t);
+		goto out;
 	}
+	t->proc->proc_fops = &recent_fops;
+	t->proc->data      = t;
 #endif
+	list_add_tail(&t->list, &tables);
+	ret = 1;
+out:
+	spin_unlock_bh(&recent_lock);
+	return ret;
+}
 
-	curr_table = vmalloc(sizeof(struct recent_ip_tables));
-	if(curr_table == NULL) return 0;
-
-	spin_lock_init(&curr_table->list_lock);
-	curr_table->next = NULL;
-	curr_table->count = 1;
-	curr_table->time_pos = 0;
-	strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN);
-	curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0';
-
-	/* Allocate memory for this table and the list of packets in each entry. */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for table (%s).\n",
-			sizeof(struct recent_ip_list)*ip_list_tot,
-			info->name);
-#endif
-
-	curr_table->table = vmalloc(sizeof(struct recent_ip_list)*ip_list_tot);
-	if(curr_table->table == NULL) { vfree(curr_table); return 0; }
-	memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for pkt_list.\n",
-			sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
-#endif
-
-	hold = vmalloc(sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: After pkt_list allocation.\n");
-#endif
-	if(hold == NULL) { 
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for pkt_list.\n");
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-	for(c = 0; c < ip_list_tot; c++) {
-		curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot;
-	}
+static void
+ipt_recent_destroy(const struct xt_match *match, void *matchinfo,
+		   unsigned int matchsize)
+{
+	const struct ipt_recent_info *info = matchinfo;
+	struct recent_table *t;
 
-	/* Allocate memory for the hash table */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for hash_table.\n",
-			sizeof(int)*ip_list_hash_size);
+	spin_lock_bh(&recent_lock);
+	t = recent_table_lookup(info->name);
+	if (--t->refcnt == 0) {
+		list_del(&t->list);
+		recent_table_flush(t);
+#ifdef CONFIG_PROC_FS
+		remove_proc_entry(t->name, proc_dir);
 #endif
-
-	curr_table->hash_table = vmalloc(sizeof(int)*ip_list_hash_size);
-	if(!curr_table->hash_table) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for hash_table.\n");
-		vfree(hold);
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-
-	for(c = 0; c < ip_list_hash_size; c++) {
-		curr_table->hash_table[c] = -1;
+		kfree(t);
 	}
+	spin_unlock_bh(&recent_lock);
+}
 
-	/* Allocate memory for the time info */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for time_info.\n",
-			sizeof(struct time_info_list)*ip_list_tot);
-#endif
+#ifdef CONFIG_PROC_FS
+struct recent_iter_state {
+	struct recent_table	*table;
+	unsigned int		bucket;
+};
 
-	curr_table->time_info = vmalloc(sizeof(struct time_info_list)*ip_list_tot);
-	if(!curr_table->time_info) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for time_info.\n");
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-	for(c = 0; c < ip_list_tot; c++) {
-		curr_table->time_info[c].position = c;
-		curr_table->time_info[c].time = 0;
-	}
+static void *recent_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct recent_iter_state *st = seq->private;
+	struct recent_table *t = st->table;
+	struct recent_entry *e;
+	loff_t p = *pos;
 
-	/* Put the new table in place */
 	spin_lock_bh(&recent_lock);
-	find_table = r_tables;
-	while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
-
-	/* If a table already exists just increment the count on that table and return */
-	if(find_table) { 
-		find_table->count++;	
-		spin_unlock_bh(&recent_lock);
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), created by other process.\n",info->name);
-#endif
-		vfree(curr_table->time_info);
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table);
-		vfree(curr_table);
-		return 1;
-	}
-	if(!last_table) r_tables = curr_table; else last_table->next = curr_table;
-
-	spin_unlock_bh(&recent_lock);
 
-#ifdef CONFIG_PROC_FS
-	/* Create our proc 'status' entry. */
-	curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
-	if (!curr_table->status_proc) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
-		/* Destroy the created table */
-		spin_lock_bh(&recent_lock);
-		last_table = NULL;
-		curr_table = r_tables;
-		if(!curr_table) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, no tables.\n");
-#endif
-			spin_unlock_bh(&recent_lock);
-			return 0;
-		}
-		while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
-		if(!curr_table) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, table already destroyed.\n");
-#endif
-			spin_unlock_bh(&recent_lock);
-			return 0;
+	for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) {
+		list_for_each_entry(e, &t->iphash[st->bucket], list) {
+			if (p-- == 0)
+				return e;
 		}
-		if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
-		spin_unlock_bh(&recent_lock);
-		vfree(curr_table->time_info);
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table);
-		vfree(curr_table);
-		return 0;
 	}
-	
-	curr_table->status_proc->owner = THIS_MODULE;
-	curr_table->status_proc->data = curr_table;
-	wmb();
-	curr_table->status_proc->read_proc = ip_recent_get_info;
-	curr_table->status_proc->write_proc = ip_recent_ctrl;
-#endif /* CONFIG_PROC_FS */
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() left.\n");
-#endif
+	return NULL;
+}
 
-	return 1;
+static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct recent_iter_state *st = seq->private;
+	struct recent_table *t = st->table;
+	struct recent_entry *e = v;
+	struct list_head *head = e->list.next;
+
+	while (head == &t->iphash[st->bucket]) {
+		if (++st->bucket >= ip_list_hash_size)
+			return NULL;
+		head = t->iphash[st->bucket].next;
+	}
+	(*pos)++;
+	return list_entry(head, struct recent_entry, list);
 }
 
-/* This function is called in the event that a rule matching this module is
- * removed.
- * When this happens we need to check if there are no other rules matching
- * the table given.  If that is the case then we remove the table and clean
- * up its memory.
- */
-static void
-destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
+static void recent_seq_stop(struct seq_file *s, void *v)
 {
-	const struct ipt_recent_info *info = matchinfo;
-	struct recent_ip_tables *curr_table, *last_table;
+	spin_unlock_bh(&recent_lock);
+}
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() entered.\n");
-#endif
+static int recent_seq_show(struct seq_file *seq, void *v)
+{
+	struct recent_entry *e = v;
+	unsigned int i;
+
+	i = (e->index - 1) % ip_pkt_list_tot;
+	seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u",
+		   NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index);
+	for (i = 0; i < e->nstamps; i++)
+		seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]);
+	seq_printf(seq, "\n");
+	return 0;
+}
 
-	if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return;
+static struct seq_operations recent_seq_ops = {
+	.start		= recent_seq_start,
+	.next		= recent_seq_next,
+	.stop		= recent_seq_stop,
+	.show		= recent_seq_show,
+};
 
-	/* Lock the linked list while we play with it */
-	spin_lock_bh(&recent_lock);
+static int recent_seq_open(struct inode *inode, struct file *file)
+{
+	struct proc_dir_entry *pde = PDE(inode);
+	struct seq_file *seq;
+	struct recent_iter_state *st;
+	int ret;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL)
+		return -ENOMEM;
+	ret = seq_open(file, &recent_seq_ops);
+	if (ret)
+		kfree(st);
+	st->table    = pde->data;
+	seq          = file->private_data;
+	seq->private = st;
+	return ret;
+}
 
-	/* Look for an entry with this name already created */
-	/* Finds the end of the list and the entry before the end if current name does not exist */
-	last_table = NULL;
-	curr_table = r_tables;
-	if(!curr_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() No tables found, leaving.\n");
-#endif
+static ssize_t recent_proc_write(struct file *file, const char __user *input,
+				 size_t size, loff_t *loff)
+{
+	struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+	struct recent_table *t = pde->data;
+	struct recent_entry *e;
+	char buf[sizeof("+255.255.255.255")], *c = buf;
+	u_int32_t addr;
+	int add;
+
+	if (size > sizeof(buf))
+		size = sizeof(buf);
+	if (copy_from_user(buf, input, size))
+		return -EFAULT;
+	while (isspace(*c))
+		c++;
+
+	if (size - (c - buf) < 5)
+		return c - buf;
+	if (!memcmp(c, "clear", 5)) {
+		spin_lock_bh(&recent_lock);
+		recent_table_flush(t);
 		spin_unlock_bh(&recent_lock);
-		return;
+		return c - buf;
 	}
-	while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
 
-	/* If a table does not exist then do nothing and return */
-	if(!curr_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table not found, leaving.\n");
-#endif
-		spin_unlock_bh(&recent_lock);
-		return;
+	switch (*c) {
+	case '-':
+		add = 0;
+		c++;
+		break;
+	case '+':
+		c++;
+	default:
+		add = 1;
+		break;
 	}
+	addr = in_aton(c);
 
-	curr_table->count--;
-
-	/* If count is still non-zero then there are still rules referenceing it so we do nothing */
-	if(curr_table->count) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, non-zero count, leaving.\n");
-#endif
-		spin_unlock_bh(&recent_lock);
-		return;
+	spin_lock_bh(&recent_lock);
+	e = recent_entry_lookup(t, addr, 0);
+	if (e == NULL) {
+		if (add)
+			recent_entry_init(t, addr, 0);
+	} else {
+		if (add)
+			recent_entry_update(e);
+		else
+			recent_entry_remove(t, e);
 	}
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, zero count, removing.\n");
-#endif
-
-	/* Count must be zero so we remove this table from the list */
-	if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
-
 	spin_unlock_bh(&recent_lock);
+	return size;
+}
 
-	/* lock to make sure any late-runners still using this after we removed it from
-	 * the list finish up then remove everything */
-	spin_lock_bh(&curr_table->list_lock);
-	spin_unlock_bh(&curr_table->list_lock);
-
-#ifdef CONFIG_PROC_FS
-	if(curr_table->status_proc) remove_proc_entry(curr_table->name,proc_net_ipt_recent);
+static struct file_operations recent_fops = {
+	.open		= recent_seq_open,
+	.read		= seq_read,
+	.write		= recent_proc_write,
+	.release	= seq_release_private,
+	.owner		= THIS_MODULE,
+};
 #endif /* CONFIG_PROC_FS */
-	vfree(curr_table->table[0].last_pkts);
-	vfree(curr_table->table);
-	vfree(curr_table->hash_table);
-	vfree(curr_table->time_info);
-	vfree(curr_table);
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() left.\n");
-#endif
 
-	return;
-}
-
-/* This is the structure we pass to ipt_register to register our
- * module with iptables.
- */
 static struct ipt_match recent_match = {
 	.name		= "recent",
-	.match		= match,
+	.match		= ipt_recent_match,
 	.matchsize	= sizeof(struct ipt_recent_info),
-	.checkentry	= checkentry,
-	.destroy	= destroy,
-	.me		= THIS_MODULE
+	.checkentry	= ipt_recent_checkentry,
+	.destroy	= ipt_recent_destroy,
+	.me		= THIS_MODULE,
 };
 
-/* Kernel module initialization. */
 static int __init ipt_recent_init(void)
 {
-	int err, count;
+	int err;
 
-	printk(version);
-#ifdef CONFIG_PROC_FS
-	proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net);
-	if(!proc_net_ipt_recent) return -ENOMEM;
-#endif
-
-	if(ip_list_hash_size && ip_list_hash_size <= ip_list_tot) {
-	  printk(KERN_WARNING RECENT_NAME ": ip_list_hash_size too small, resetting to default.\n");
-	  ip_list_hash_size = 0;
-	}
-
-	if(!ip_list_hash_size) {
-		ip_list_hash_size = ip_list_tot*3;
-		count = 2*2;
-		while(ip_list_hash_size > count) count = count*2;
-		ip_list_hash_size = count;
-	}
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_list_hash_size: %d\n",ip_list_hash_size);
-#endif
+	if (!ip_list_tot || !ip_pkt_list_tot)
+		return -EINVAL;
+	ip_list_hash_size = 1 << fls(ip_list_tot);
 
 	err = ipt_register_match(&recent_match);
+#ifdef CONFIG_PROC_FS
 	if (err)
-		remove_proc_entry("ipt_recent", proc_net);
+		return err;
+	proc_dir = proc_mkdir("ipt_recent", proc_net);
+	if (proc_dir == NULL) {
+		ipt_unregister_match(&recent_match);
+		err = -ENOMEM;
+	}
+#endif
 	return err;
 }
 
-/* Kernel module destruction. */
-static void __exit ipt_recent_fini(void)
+static void __exit ipt_recent_exit(void)
 {
 	ipt_unregister_match(&recent_match);
-
-	remove_proc_entry("ipt_recent",proc_net);
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("ipt_recent", proc_net);
+#endif
 }
 
-/* Register our module with the kernel. */
 module_init(ipt_recent_init);
-module_exit(ipt_recent_fini);
+module_exit(ipt_recent_exit);

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-15 20:09                         ` Patrick McHardy
@ 2006-05-15 20:41                           ` Stephen Frost
  2006-05-15 20:45                             ` Patrick McHardy
  2006-05-15 21:03                             ` Stephen Frost
  0 siblings, 2 replies; 32+ messages in thread
From: Stephen Frost @ 2006-05-15 20:41 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Amin Azez, David S. Miller, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 1013 bytes --]

* Patrick McHardy (kaber@trash.net) wrote:
> This is the updated patch, it changes the eviction strategy
> to LRU and fixes a bug related to TTL handling, the TTL stored
> in the entry should only be overwritten if the IPT_RECENT_TTL
> flag is set.

This looks like least-recently-added as opposed to least-recently-used
(or, really, least-recently-updated).  Not sure how you move an entry in
the lru list (perhaps just delete/add?) but I'm pretty sure
recent_entry_update() needs to be modified to move the updated entry to
the end of the list for correct operation.

You also don't appear to check if 't' (the table following the
recent_table_lookup() call) is valid in the 'match' (around
line 191).  recent_entry_lookup() doesn't check that either.  It seems
like you should be guarenteed to always get a table back but it might be
prudent to check anyway.

I thought that I had convinced myself that the TTL handling was okay and
that where it was overwritten wasn't harmful.  Oh well.

	Thanks,

		Stephen

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-15 20:41                           ` Stephen Frost
@ 2006-05-15 20:45                             ` Patrick McHardy
  2006-05-15 21:03                             ` Stephen Frost
  1 sibling, 0 replies; 32+ messages in thread
From: Patrick McHardy @ 2006-05-15 20:45 UTC (permalink / raw)
  To: Stephen Frost
  Cc: Amin Azez, David S. Miller, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

Stephen Frost wrote:
> * Patrick McHardy (kaber@trash.net) wrote:
> 
>>This is the updated patch, it changes the eviction strategy
>>to LRU and fixes a bug related to TTL handling, the TTL stored
>>in the entry should only be overwritten if the IPT_RECENT_TTL
>>flag is set.
> 
> 
> This looks like least-recently-added as opposed to least-recently-used
> (or, really, least-recently-updated).  Not sure how you move an entry in
> the lru list (perhaps just delete/add?) but I'm pretty sure
> recent_entry_update() needs to be modified to move the updated entry to
> the end of the list for correct operation.


Good point, I'll fix the patch.

> You also don't appear to check if 't' (the table following the
> recent_table_lookup() call) is valid in the 'match' (around
> line 191).  recent_entry_lookup() doesn't check that either.  It seems
> like you should be guarenteed to always get a table back but it might be
> prudent to check anyway.


It is guaranteed that we will get a valid table back, otherwise
there must be a serious bug somewhere else, in which case I
prefer to crash instead of hiding it away.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-15 20:41                           ` Stephen Frost
  2006-05-15 20:45                             ` Patrick McHardy
@ 2006-05-15 21:03                             ` Stephen Frost
  2006-05-17  6:26                               ` Patrick McHardy
  1 sibling, 1 reply; 32+ messages in thread
From: Stephen Frost @ 2006-05-15 21:03 UTC (permalink / raw)
  To: Patrick McHardy, Amin Azez, David S. Miller, willy, gcoady.lk,
	laforge, netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 1032 bytes --]

* Stephen Frost (sfrost@snowman.net) wrote:
> * Patrick McHardy (kaber@trash.net) wrote:
> > This is the updated patch, it changes the eviction strategy
> > to LRU and fixes a bug related to TTL handling, the TTL stored
> > in the entry should only be overwritten if the IPT_RECENT_TTL
> > flag is set.
> 
> I thought that I had convinced myself that the TTL handling was okay and
> that where it was overwritten wasn't harmful.  Oh well.

Looking at this again...  The ttl isn't copied into 'ttl' unless the
check_set has TTL turned on.  This means that the overwritting was fine,
if you accept that you can only ever match on TTL, or never match on it.
That doesn't seem right to me.  The TTL in the table should always be
kept up-to-date and the only question is if the current rule requires it
for a match or not.  This isn't a huge change, just set the local
variable always but check for if it's asked to match before calling the
lookup.  Or you could move it into an if/else block.

	Thanks,

		Stephen

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-15 21:03                             ` Stephen Frost
@ 2006-05-17  6:26                               ` Patrick McHardy
  2006-05-17  6:59                                 ` David S. Miller
                                                   ` (3 more replies)
  0 siblings, 4 replies; 32+ messages in thread
From: Patrick McHardy @ 2006-05-17  6:26 UTC (permalink / raw)
  To: Stephen Frost
  Cc: Amin Azez, David S. Miller, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 481 bytes --]

Stephen Frost wrote:
> Looking at this again...  The ttl isn't copied into 'ttl' unless the
> check_set has TTL turned on.  This means that the overwritting was fine,
> if you accept that you can only ever match on TTL, or never match on it.
> That doesn't seem right to me.  The TTL in the table should always be
> kept up-to-date and the only question is if the current rule requires it
> for a match or not.


OK, updated patch attached. The TTL is now always kept up-to-date.


[-- Attachment #2: x --]
[-- Type: text/plain, Size: 46810 bytes --]

[NETFILTER]: Replace ipt_recent module

Replace the totally unmaintainable ipt_recent module by a rewritten
version that should be fully compatible.

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit 791489887d0984df96ac098707993bf01a4804a9
tree ea9a218b86d9a922ba69f9ae87cca826d9b52d87
parent d8c3291c73b958243b33f8509d4507e76dafd055
author Patrick McHardy <kaber@trash.net> Wed, 17 May 2006 08:22:14 +0200
committer Patrick McHardy <kaber@trash.net> Wed, 17 May 2006 08:22:14 +0200

 net/ipv4/netfilter/ipt_recent.c | 1268 ++++++++++++---------------------------
 1 files changed, 377 insertions(+), 891 deletions(-)

diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
index 1438432..9dc4dea 100644
--- a/net/ipv4/netfilter/ipt_recent.c
+++ b/net/ipv4/netfilter/ipt_recent.c
@@ -1,1007 +1,493 @@
-/* Kernel module to check if the source address has been seen recently. */
-/* Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org */
-/* Author: Stephen Frost <sfrost@snowman.net> */
-/* Project Page: http://snowman.net/projects/ipt_recent/ */
-/* This software is distributed under the terms of the GPL, Version 2 */
-/* This copyright does not cover user programs that use kernel services
- * by normal system calls. */
-
-#include <linux/module.h>
-#include <linux/skbuff.h>
+/*
+ * Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This is a replacement of the old ipt_recent module, which carried the
+ * following copyright notice:
+ *
+ * Author: Stephen Frost <sfrost@snowman.net>
+ * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org
+ */
+#include <linux/init.h>
+#include <linux/moduleparam.h>
 #include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <asm/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
 #include <linux/ctype.h>
-#include <linux/ip.h>
-#include <linux/vmalloc.h>
-#include <linux/moduleparam.h>
+#include <linux/list.h>
+#include <linux/random.h>
+#include <linux/jhash.h>
+#include <linux/bitops.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
 
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_recent.h>
 
-#undef DEBUG
-#define HASH_LOG 9
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_DESCRIPTION("IP tables recently seen matching module");
+MODULE_LICENSE("GPL");
 
-/* Defaults, these can be overridden on the module command-line. */
 static unsigned int ip_list_tot = 100;
 static unsigned int ip_pkt_list_tot = 20;
 static unsigned int ip_list_hash_size = 0;
 static unsigned int ip_list_perms = 0644;
-#ifdef DEBUG
-static int debug = 1;
-#endif
-
-static char version[] =
-KERN_INFO RECENT_NAME " " RECENT_VER ": Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n";
-
-MODULE_AUTHOR("Stephen Frost <sfrost@snowman.net>");
-MODULE_DESCRIPTION("IP tables recently seen matching module " RECENT_VER);
-MODULE_LICENSE("GPL");
 module_param(ip_list_tot, uint, 0400);
 module_param(ip_pkt_list_tot, uint, 0400);
 module_param(ip_list_hash_size, uint, 0400);
 module_param(ip_list_perms, uint, 0400);
-#ifdef DEBUG
-module_param(debug, bool, 0600);
-MODULE_PARM_DESC(debug,"enable debugging output");
-#endif
-MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list");
-MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember");
-MODULE_PARM_DESC(ip_list_hash_size,"size of hash table used to look up IPs");
-MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_recent/* files");
-
-/* Structure of our list of recently seen addresses. */
-struct recent_ip_list {
-	u_int32_t addr;
-	u_int8_t  ttl;
-	unsigned long last_seen;
-	unsigned long *last_pkts;
-	u_int32_t oldest_pkt;
-	u_int32_t hash_entry;
-	u_int32_t time_pos;
-};
-
-struct time_info_list {
-	u_int32_t position;
-	u_int32_t time;
+MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
+MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember");
+MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
+MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files");
+
+
+struct recent_entry {
+	struct list_head	list;
+	struct list_head	lru_list;
+	u_int32_t		addr;
+	u_int8_t		ttl;
+	unsigned int		index;
+	unsigned int		nstamps;
+	unsigned long		stamps[0];
 };
 
-/* Structure of our linked list of tables of recent lists. */
-struct recent_ip_tables {
-	char name[IPT_RECENT_NAME_LEN];
-	int count;
-	int time_pos;
-	struct recent_ip_list *table;
-	struct recent_ip_tables *next;
-	spinlock_t list_lock;
-	int *hash_table;
-	struct time_info_list *time_info;
+struct recent_table {
+	struct list_head	list;
+	char			name[IPT_RECENT_NAME_LEN];
 #ifdef CONFIG_PROC_FS
-	struct proc_dir_entry *status_proc;
-#endif /* CONFIG_PROC_FS */
+	struct proc_dir_entry	*proc;
+#endif
+	unsigned int		refcnt;
+	unsigned int		entries;
+	struct list_head	lru_list;
+	struct list_head	iphash[0];
 };
 
-/* Our current list of addresses we have recently seen.
- * Only added to on a --set, and only updated on --set || --update 
- */
-static struct recent_ip_tables *r_tables = NULL;
-
-/* We protect r_list with this spinlock so two processors are not modifying
- * the list at the same time. 
- */
+static LIST_HEAD(tables);
 static DEFINE_SPINLOCK(recent_lock);
 
 #ifdef CONFIG_PROC_FS
-/* Our /proc/net/ipt_recent entry */
-static struct proc_dir_entry *proc_net_ipt_recent = NULL;
-#endif
-
-/* Function declaration for later. */
-static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop);
-
-/* Function to hash a given address into the hash table of table_size size */
-static int hash_func(unsigned int addr, int table_size)
-{
-	int result = 0;
-	unsigned int value = addr;
-	do { result ^= value; } while((value >>= HASH_LOG));
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": %d = hash_func(%u,%d)\n",
-			 result & (table_size - 1),
-			 addr,
-			 table_size);
+static struct proc_dir_entry	*proc_dir;
+static struct file_operations	recent_fops;
 #endif
 
-	return(result & (table_size - 1));
-}
+static u_int32_t hash_rnd;
+static int hash_rnd_initted;
 
-#ifdef CONFIG_PROC_FS
-/* This is the function which produces the output for our /proc output
- * interface which lists each IP address, the last seen time and the 
- * other recent times the address was seen.
- */
-
-static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+static unsigned int recent_entry_hash(u_int32_t addr)
 {
-	int len = 0, count, last_len = 0, pkt_count;
-	off_t pos = 0;
-	off_t begin = 0;
-	struct recent_ip_tables *curr_table;
-
-	curr_table = (struct recent_ip_tables*) data;
-
-	spin_lock_bh(&curr_table->list_lock);
-	for(count = 0; count < ip_list_tot; count++) {
-		if(!curr_table->table[count].addr) continue;
-		last_len = len;
-		len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].addr));
-		len += sprintf(buffer+len,"ttl: %u ",curr_table->table[count].ttl);
-		len += sprintf(buffer+len,"last_seen: %lu ",curr_table->table[count].last_seen);
-		len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[count].oldest_pkt);
-		len += sprintf(buffer+len,"last_pkts: %lu",curr_table->table[count].last_pkts[0]);
-		for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) {
-			if(!curr_table->table[count].last_pkts[pkt_count]) break;
-			len += sprintf(buffer+len,", %lu",curr_table->table[count].last_pkts[pkt_count]);
-		}
-		len += sprintf(buffer+len,"\n");
-		pos = begin + len;
-		if(pos < offset) { len = 0; begin = pos; }
-		if(pos > offset + length) { len = last_len; break; }
+	if (!hash_rnd_initted) {
+		get_random_bytes(&hash_rnd, 4);
+		hash_rnd_initted = 1;
 	}
-
-	*start = buffer + (offset - begin);
-	len -= (offset - begin);
-	if(len > length) len = length;
-
-	spin_unlock_bh(&curr_table->list_lock);
-	return len;
+	return jhash_1word(addr, hash_rnd) & (ip_list_hash_size - 1);
 }
 
-/* ip_recent_ctrl provides an interface for users to modify the table
- * directly.  This allows adding entries, removing entries, and
- * flushing the entire table.
- * This is done by opening up the appropriate table for writing and
- * sending one of:
- * xx.xx.xx.xx   -- Add entry to table with current time
- * +xx.xx.xx.xx  -- Add entry to table with current time
- * -xx.xx.xx.xx  -- Remove entry from table
- * clear         -- Flush table, remove all entries
- */
-
-static int ip_recent_ctrl(struct file *file, const char __user *input, unsigned long size, void *data)
+static struct recent_entry *
+recent_entry_lookup(const struct recent_table *table, u_int32_t addr, u_int8_t ttl)
 {
-	static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
-	u_int32_t val;
-	int base, used = 0;
-	char c, *cp;
-	union iaddr {
-		uint8_t bytes[4];
-		uint32_t word;
-	} res;
-	uint8_t *pp = res.bytes;
-	int digit;
-
-	char buffer[20];
-	int len, check_set = 0, count;
-	u_int32_t addr = 0;
-	struct sk_buff *skb;
-	struct ipt_recent_info *info;
-	struct recent_ip_tables *curr_table;
-
-	curr_table = (struct recent_ip_tables*) data;
-
-	if(size > 20) len = 20; else len = size;
-
-	if(copy_from_user(buffer,input,len)) return -EFAULT;
-
-	if(len < 20) buffer[len] = '\0';
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl len: %d, input: `%.20s'\n",len,buffer);
-#endif
+	struct recent_entry *e;
+	unsigned int h;
+
+	h = recent_entry_hash(addr);
+	list_for_each_entry(e, &table->iphash[h], list)
+		if (e->addr == addr && (ttl == e->ttl || !ttl || !e->ttl))
+			return e;
+	return NULL;
+}
 
-	cp = buffer;
-	while(isspace(*cp)) { cp++; used++; if(used >= len-5) return used; }
+static void recent_entry_remove(struct recent_table *t, struct recent_entry *e)
+{
+	list_del(&e->list);
+	list_del(&e->lru_list);
+	kfree(e);
+	t->entries--;
+}
 
-	/* Check if we are asked to flush the entire table */
-	if(!memcmp(cp,"clear",5)) {
-		used += 5;
-		spin_lock_bh(&curr_table->list_lock);
-		curr_table->time_pos = 0;
-		for(count = 0; count < ip_list_hash_size; count++) {
-			curr_table->hash_table[count] = -1;
-		}
-		for(count = 0; count < ip_list_tot; count++) {
-			curr_table->table[count].last_seen = 0;
-			curr_table->table[count].addr = 0;
-			curr_table->table[count].ttl = 0;
-			memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-			curr_table->table[count].oldest_pkt = 0;
-			curr_table->table[count].time_pos = 0;
-			curr_table->time_info[count].position = count;
-			curr_table->time_info[count].time = 0;
-		}
-		spin_unlock_bh(&curr_table->list_lock);
-		return used;
-	}
+static struct recent_entry *
+recent_entry_init(struct recent_table *t, u_int32_t addr, u_int8_t ttl)
+{
+	struct recent_entry *e;
 
-        check_set = IPT_RECENT_SET;
-	switch(*cp) {
-		case '+': check_set = IPT_RECENT_SET; cp++; used++; break;
-		case '-': check_set = IPT_RECENT_REMOVE; cp++; used++; break;
-		default: if(!isdigit(*cp)) return (used+1); break;
+	if (t->entries >= ip_list_tot) {
+		e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
+		recent_entry_remove(t, e);
 	}
+	e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot,
+		    GFP_ATOMIC);
+	if (e == NULL)
+		return NULL;
+	e->addr      = addr;
+	e->ttl       = ttl;
+	e->stamps[0] = jiffies;
+	e->nstamps   = 1;
+	e->index     = 1;
+	INIT_LIST_HEAD(&e->lru_list);
+	list_add_tail(&e->list, &t->iphash[recent_entry_hash(addr)]);
+	t->entries++;
+	return e;
+}
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp,check_set);
-#endif
-	/* Get addr (effectively inet_aton()) */
-	/* Shamelessly stolen from libc, a function in the kernel for doing
-	 * this would, of course, be greatly preferred, but our options appear
-	 * to be rather limited, so we will just do it ourselves here.
-	 */
-	res.word = 0;
-
-	c = *cp;
-	for(;;) {
-		if(!isdigit(c)) return used;
-		val = 0; base = 10; digit = 0;
-		if(c == '0') {
-			c = *++cp;
-			if(c == 'x' || c == 'X') base = 16, c = *++cp;
-			else { base = 8; digit = 1; }
-		}
-		for(;;) {
-			if(isascii(c) && isdigit(c)) {
-				if(base == 8 && (c == '8' || c == '0')) return used;
-				val = (val * base) + (c - '0');
-				c = *++cp;
-				digit = 1;
-			} else if(base == 16 && isascii(c) && isxdigit(c)) {
-				val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A'));
-				c = *++cp;
-				digit = 1;
-			} else break;
-		}
-		if(c == '.') {
-			if(pp > res.bytes + 2 || val > 0xff) return used;
-			*pp++ = val;
-			c = *++cp;
-		} else break;
-	}
-	used = cp - buffer;
-	if(c != '\0' && (!isascii(c) || !isspace(c))) return used;
-	if(c == '\n') used++;
-	if(!digit) return used;
+static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
+{
+	e->stamps[e->index++] = jiffies;
+	if (e->index > e->nstamps)
+		e->nstamps = e->index;
+	e->index %= ip_pkt_list_tot;
+	list_move_tail(&e->lru_list, &t->lru_list);
+}
 
-	if(val > max[pp - res.bytes]) return used;
-	addr = res.word | htonl(val);
+static struct recent_table *recent_table_lookup(const char *name)
+{
+	struct recent_table *t;
 
-	if(!addr && check_set == IPT_RECENT_SET) return used;
+	list_for_each_entry(t, &tables, list)
+		if (!strcmp(t->name, name))
+			return t;
+	return NULL;
+}
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used);
-#endif
+static void recent_table_flush(struct recent_table *t)
+{
+	struct recent_entry *e, *next;
+	unsigned int i;
 
-	/* Set up and just call match */
-	info = kmalloc(sizeof(struct ipt_recent_info),GFP_KERNEL);
-	if(!info) { return -ENOMEM; }
-	info->seconds = 0;
-	info->hit_count = 0;
-	info->check_set = check_set;
-	info->invert = 0;
-	info->side = IPT_RECENT_SOURCE;
-	strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN);
-	info->name[IPT_RECENT_NAME_LEN-1] = '\0';
-
-	skb = kmalloc(sizeof(struct sk_buff),GFP_KERNEL);
-	if (!skb) {
-		used = -ENOMEM;
-		goto out_free_info;
-	}
-	skb->nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL);
-	if (!skb->nh.iph) {
-		used = -ENOMEM;
-		goto out_free_skb;
+	for (i = 0; i < ip_list_hash_size; i++) {
+		list_for_each_entry_safe(e, next, &t->iphash[i], list)
+			recent_entry_remove(t, e);
 	}
-
-	skb->nh.iph->saddr = addr;
-	skb->nh.iph->daddr = 0;
-	/* Clear ttl since we have no way of knowing it */
-	skb->nh.iph->ttl = 0;
-	match(skb,NULL,NULL,NULL,info,0,0,NULL);
-
-	kfree(skb->nh.iph);
-out_free_skb:
-	kfree(skb);
-out_free_info:
-	kfree(info);
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": Leaving ip_recent_ctrl addr: %u used: %d\n",addr,used);
-#endif
-	return used;
 }
 
-#endif /* CONFIG_PROC_FS */
-
-/* 'match' is our primary function, called by the kernel whenever a rule is
- * hit with our module as an option to it.
- * What this function does depends on what was specifically asked of it by
- * the user:
- * --set -- Add or update last seen time of the source address of the packet
- *   -- matchinfo->check_set == IPT_RECENT_SET
- * --rcheck -- Just check if the source address is in the list
- *   -- matchinfo->check_set == IPT_RECENT_CHECK
- * --update -- If the source address is in the list, update last_seen
- *   -- matchinfo->check_set == IPT_RECENT_UPDATE
- * --remove -- If the source address is in the list, remove it
- *   -- matchinfo->check_set == IPT_RECENT_REMOVE
- * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
- *   -- matchinfo->seconds
- * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
- *   -- matchinfo->hit_count
- * --seconds and --hitcount can be combined
- */
 static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop)
+ipt_recent_match(const struct sk_buff *skb,
+		 const struct net_device *in, const struct net_device *out,
+		 const struct xt_match *match, const void *matchinfo,
+		 int offset, unsigned int protoff, int *hotdrop)
 {
-	int pkt_count, hits_found, ans;
-	unsigned long now;
 	const struct ipt_recent_info *info = matchinfo;
-	u_int32_t addr = 0, time_temp;
-	u_int8_t ttl = skb->nh.iph->ttl;
-	int *hash_table;
-	int orig_hash_result, hash_result, temp, location = 0, time_loc, end_collision_chain = -1;
-	struct time_info_list *time_info;
-	struct recent_ip_tables *curr_table;
-	struct recent_ip_tables *last_table;
-	struct recent_ip_list *r_list;
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match() called\n");
-#endif
-
-	/* Default is false ^ info->invert */
-	ans = info->invert;
+	struct recent_table *t;
+	struct recent_entry *e;
+	u_int32_t addr;
+	u_int8_t ttl;
+	int ret = info->invert;
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): name = '%s'\n",info->name);
-#endif
+	if (info->side == IPT_RECENT_DEST)
+		addr = skb->nh.iph->daddr;
+	else
+		addr = skb->nh.iph->saddr;
 
-	/* if out != NULL then routing has been done and TTL changed.
-	 * We change it back here internally for match what came in before routing. */
-	if(out) ttl++;
+	ttl = skb->nh.iph->ttl;
+	/* use TTL as seen before forwarding */
+	if (out && !skb->sk)
+		ttl++;
 
-	/* Find the right table */
 	spin_lock_bh(&recent_lock);
-	curr_table = r_tables;
-	while( (last_table = curr_table) && strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (curr_table = curr_table->next) );
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): table found('%s')\n",info->name);
-#endif
-
-	spin_unlock_bh(&recent_lock);
-
-	/* Table with this name not found, match impossible */
-	if(!curr_table) { return ans; }
-
-	/* Make sure no one is changing the list while we work with it */
-	spin_lock_bh(&curr_table->list_lock);
-
-	r_list = curr_table->table;
-	if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr;
-
-	if(!addr) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match() address (%u) invalid, leaving.\n",addr);
-#endif
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
+	t = recent_table_lookup(info->name);
+	e = recent_entry_lookup(t, addr,
+				info->check_set & IPT_RECENT_TTL ? ttl : 0);
+	if (e == NULL) {
+		if (!(info->check_set & IPT_RECENT_SET))
+			goto out;
+		e = recent_entry_init(t, addr, ttl);
+		if (e == NULL)
+			*hotdrop = 1;
+		ret ^= 1;
+		goto out;
 	}
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr,ttl,skb->nh.iph->ttl);
-#endif
-
-	/* Get jiffies now in case they changed while we were waiting for a lock */
-	now = jiffies;
-	hash_table = curr_table->hash_table;
-	time_info = curr_table->time_info;
-
-	orig_hash_result = hash_result = hash_func(addr,ip_list_hash_size);
-	/* Hash entry at this result used */
-	/* Check for TTL match if requested.  If TTL is zero then a match would never
-	 * happen, so match regardless of existing TTL in that case.  Zero means the
-	 * entry was added via the /proc interface anyway, so we will just use the
-	 * first TTL we get for that IP address. */
-	if(info->check_set & IPT_RECENT_TTL) {
-		while(hash_table[hash_result] != -1 && !(r_list[hash_table[hash_result]].addr == addr &&
-			(!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) {
-			/* Collision in hash table */
-			hash_result = (hash_result + 1) % ip_list_hash_size;
-		}
-	} else {
-		while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) {
-			/* Collision in hash table */
-			hash_result = (hash_result + 1) % ip_list_hash_size;
-		}
-	}
-
-	if(hash_table[hash_result] == -1 && !(info->check_set & IPT_RECENT_SET)) {
-		/* IP not in list and not asked to SET */
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
-	}
-
-	/* Check if we need to handle the collision, do not need to on REMOVE */
-	if(orig_hash_result != hash_result && !(info->check_set & IPT_RECENT_REMOVE)) {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
-				 orig_hash_result,
-				 hash_result,
-				 r_list[hash_table[orig_hash_result]].addr,
-				 addr);
-#endif
-
-		/* We had a collision.
-		 * orig_hash_result is where we started, hash_result is where we ended up.
-		 * So, swap them because we are likely to see the same guy again sooner */
-#ifdef DEBUG
-		if(debug) {
-		  printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table[orig_hash_result]);
-		  printk(KERN_INFO RECENT_NAME ": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
-				r_list[hash_table[orig_hash_result]].hash_entry);
-		}
-#endif
-
-		r_list[hash_table[orig_hash_result]].hash_entry = hash_result;
-
-
-		temp = hash_table[orig_hash_result];
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[hash_result] = %d\n",hash_table[hash_result]);
-#endif
-		hash_table[orig_hash_result] = hash_table[hash_result];
-		hash_table[hash_result] = temp;
-		temp = hash_result;
-		hash_result = orig_hash_result;
-		orig_hash_result = temp;
-		time_info[r_list[hash_table[orig_hash_result]].time_pos].position = hash_table[orig_hash_result];
-		if(hash_table[hash_result] != -1) {
-			r_list[hash_table[hash_result]].hash_entry = hash_result;
-			time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-		}
-
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision handled.\n");
-#endif
-	}
-
-	if(hash_table[hash_result] == -1) {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): New table entry. (hr: %d,ha: %u)\n",
-				 hash_result, addr);
-#endif
-
-		/* New item found and IPT_RECENT_SET, so we need to add it */
-		location = time_info[curr_table->time_pos].position;
-		hash_table[r_list[location].hash_entry] = -1;
-		hash_table[hash_result] = location;
-		memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-		r_list[location].time_pos = curr_table->time_pos;
-		r_list[location].addr = addr;
-		r_list[location].ttl = ttl;
-		r_list[location].last_seen = now;
-		r_list[location].oldest_pkt = 1;
-		r_list[location].last_pkts[0] = now;
-		r_list[location].hash_entry = hash_result;
-		time_info[curr_table->time_pos].time = r_list[location].last_seen;
-		curr_table->time_pos = (curr_table->time_pos + 1) % ip_list_tot;
-
-		ans = !info->invert;
-	} else {
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": match(): Existing table entry. (hr: %d,ha: %u)\n",
-				 hash_result,
-				 addr);
-#endif
-
-		/* Existing item found */
-		location = hash_table[hash_result];
-		/* We have a match on address, now to make sure it meets all requirements for a
-		 * full match. */
-		if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) {
-			if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert;
-			if(info->seconds && !info->hit_count) {
-				if(time_before_eq(now,r_list[location].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert;
-			}
-			if(info->seconds && info->hit_count) {
-				for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
-					if(r_list[location].last_pkts[pkt_count] == 0) break;
-					if(time_before_eq(now,r_list[location].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++;
-				}
-				if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
-			}
-			if(info->hit_count && !info->seconds) {
-				for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
-					if(r_list[location].last_pkts[pkt_count] == 0) break;
-					hits_found++;
-				}
-				if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
-			}
-		}
-#ifdef DEBUG
-		if(debug) {
-			if(ans)
-				printk(KERN_INFO RECENT_NAME ": match(): match addr: %u\n",addr);
-			else
-				printk(KERN_INFO RECENT_NAME ": match(): no match addr: %u\n",addr);
-		}
-#endif
-
-		/* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
-		 * current timestamp to the last_seen. */
-		if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": match(): SET or UPDATE; updating time info.\n");
-#endif
-			/* Have to update our time info */
-			time_loc = r_list[location].time_pos;
-			time_info[time_loc].time = now;
-			time_info[time_loc].position = location;
-			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) {
-				time_temp = time_info[time_loc].time;
-				time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
-				time_info[(time_loc+1)%ip_list_tot].time = time_temp;
-				time_temp = time_info[time_loc].position;
-				time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
-				time_info[(time_loc+1)%ip_list_tot].position = time_temp;
-				r_list[time_info[time_loc].position].time_pos = time_loc;
-				r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
-				time_loc = (time_loc+1) % ip_list_tot;
-			}
-			r_list[location].time_pos = time_loc;
-			r_list[location].ttl = ttl;
-			r_list[location].last_pkts[r_list[location].oldest_pkt] = now;
-			r_list[location].oldest_pkt = ++r_list[location].oldest_pkt % ip_pkt_list_tot;
-			r_list[location].last_seen = now;
-		}
-		/* If we have been asked to remove the entry from the list, just set it to 0 */
-		if(info->check_set & IPT_RECENT_REMOVE) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result,hash_result);
-#endif
-			/* Check if this is part of a collision chain */
-			while(hash_table[(orig_hash_result+1) % ip_list_hash_size] != -1) {
-				orig_hash_result++;
-				if(hash_func(r_list[hash_table[orig_hash_result]].addr,ip_list_hash_size) == hash_result) {
-					/* Found collision chain, how deep does this rabbit hole go? */
-#ifdef DEBUG
-					if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; found collision chain.\n");
-#endif
-					end_collision_chain = orig_hash_result;
-				}
-			}
-			if(end_collision_chain != -1) {
-#ifdef DEBUG
-				if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; part of collision chain, moving to end.\n");
-#endif
-				/* Part of a collision chain, swap it with the end of the chain
-				 * before removing. */
-				r_list[hash_table[end_collision_chain]].hash_entry = hash_result;
-				temp = hash_table[end_collision_chain];
-				hash_table[end_collision_chain] = hash_table[hash_result];
-				hash_table[hash_result] = temp;
-				time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-				hash_result = end_collision_chain;
-				r_list[hash_table[hash_result]].hash_entry = hash_result;
-				time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
-			}
-			location = hash_table[hash_result];
-			hash_table[r_list[location].hash_entry] = -1;
-			time_loc = r_list[location].time_pos;
-			time_info[time_loc].time = 0;
-			time_info[time_loc].position = location;
-			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) {
-				time_temp = time_info[time_loc].time;
-				time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
-				time_info[(time_loc+1)%ip_list_tot].time = time_temp;
-				time_temp = time_info[time_loc].position;
-				time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
-				time_info[(time_loc+1)%ip_list_tot].position = time_temp;
-				r_list[time_info[time_loc].position].time_pos = time_loc;
-				r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
-				time_loc = (time_loc+1) % ip_list_tot;
+	if (info->check_set & IPT_RECENT_SET)
+		ret ^= 1;
+	else if (info->check_set & IPT_RECENT_REMOVE) {
+		recent_entry_remove(t, e);
+		ret ^= 1;
+	} else if (info->check_set & (IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) {
+		unsigned long t = jiffies - info->seconds * HZ;
+		unsigned int i, hits = 0;
+
+		for (i = 0; i < e->nstamps; i++) {
+			if (info->seconds && time_after(t, e->stamps[i]))
+				continue;
+			if (++hits >= info->hit_count) {
+				ret ^= 1;
+				break;
 			}
-			r_list[location].time_pos = time_loc;
-			r_list[location].last_seen = 0;
-			r_list[location].addr = 0;
-			r_list[location].ttl = 0;
-			memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
-			r_list[location].oldest_pkt = 0;
-			ans = !info->invert;
 		}
-		spin_unlock_bh(&curr_table->list_lock);
-		return ans;
 	}
 
-	spin_unlock_bh(&curr_table->list_lock);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": match() left.\n");
-#endif
-	return ans;
+	if (info->check_set & IPT_RECENT_SET ||
+	    (info->check_set & IPT_RECENT_UPDATE && ret)) {
+		recent_entry_update(t, e);
+		e->ttl = ttl;
+	}
+out:
+	spin_unlock_bh(&recent_lock);
+	return ret;
 }
 
-/* This function is to verify that the rule given during the userspace iptables
- * command is correct.
- * If the command is valid then we check if the table name referred to by the
- * rule exists, if not it is created.
- */
 static int
-checkentry(const char *tablename,
-           const void *ip,
-	   const struct xt_match *match,
-           void *matchinfo,
-           unsigned int matchsize,
-           unsigned int hook_mask)
+ipt_recent_checkentry(const char *tablename, const void *ip,
+		      const struct xt_match *match, void *matchinfo,
+		      unsigned int matchsize, unsigned int hook_mask)
 {
-	int flag = 0, c;
-	unsigned long *hold;
 	const struct ipt_recent_info *info = matchinfo;
-	struct recent_ip_tables *curr_table, *find_table, *last_table;
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() entered.\n");
-#endif
-
-	/* seconds and hit_count only valid for CHECK/UPDATE */
-	if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; }
-	if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; }
-	if(info->check_set & IPT_RECENT_CHECK) flag++;
-	if(info->check_set & IPT_RECENT_UPDATE) flag++;
-
-	/* One and only one of these should ever be set */
-	if(flag != 1) return 0;
+	struct recent_table *t;
+	unsigned i;
+	int ret = 0;
 
-	/* Name must be set to something */
-	if(!info->name || !info->name[0]) return 0;
+	if (hweight8(info->check_set &
+		     (IPT_RECENT_SET | IPT_RECENT_REMOVE |
+		      IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) != 1)
+		return 0;
+	if (info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE) &&
+	    (info->seconds || info->hit_count))
+		return 0;
+	if (info->name[0] == '\0' ||
+	    strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN)
+		return 0;
 
-	/* Things look good, create a list for this if it does not exist */
-	/* Lock the linked list while we play with it */
 	spin_lock_bh(&recent_lock);
-
-	/* Look for an entry with this name already created */
-	/* Finds the end of the list and the entry before the end if current name does not exist */
-	find_table = r_tables;
-	while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
-
-	/* If a table already exists just increment the count on that table and return */
-	if(find_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), incrementing count.\n",info->name);
-#endif
-		find_table->count++;
-		spin_unlock_bh(&recent_lock);
-		return 1;
+	t = recent_table_lookup(info->name);
+	if (t != NULL) {
+		t->refcnt++;
+		ret = 1;
+		goto out;
 	}
 
-	spin_unlock_bh(&recent_lock);
-
-	/* Table with this name not found */
-	/* Allocate memory for new linked list item */
-
-#ifdef DEBUG
-	if(debug) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: no table found (%s)\n",info->name);
-		printk(KERN_INFO RECENT_NAME ": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables));
+	t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
+		    GFP_ATOMIC);
+	if (t == NULL)
+		goto out;
+	strcpy(t->name, info->name);
+	INIT_LIST_HEAD(&t->lru_list);
+	for (i = 0; i < ip_list_hash_size; i++)
+		INIT_LIST_HEAD(&t->iphash[i]);
+#ifdef CONFIG_PROC_FS
+	t->proc = create_proc_entry(t->name, ip_list_perms, proc_dir);
+	if (t->proc == NULL) {
+		kfree(t);
+		goto out;
 	}
+	t->proc->proc_fops = &recent_fops;
+	t->proc->data      = t;
 #endif
+	list_add_tail(&t->list, &tables);
+	ret = 1;
+out:
+	spin_unlock_bh(&recent_lock);
+	return ret;
+}
 
-	curr_table = vmalloc(sizeof(struct recent_ip_tables));
-	if(curr_table == NULL) return 0;
-
-	spin_lock_init(&curr_table->list_lock);
-	curr_table->next = NULL;
-	curr_table->count = 1;
-	curr_table->time_pos = 0;
-	strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN);
-	curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0';
-
-	/* Allocate memory for this table and the list of packets in each entry. */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for table (%s).\n",
-			sizeof(struct recent_ip_list)*ip_list_tot,
-			info->name);
-#endif
-
-	curr_table->table = vmalloc(sizeof(struct recent_ip_list)*ip_list_tot);
-	if(curr_table->table == NULL) { vfree(curr_table); return 0; }
-	memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for pkt_list.\n",
-			sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
-#endif
-
-	hold = vmalloc(sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: After pkt_list allocation.\n");
-#endif
-	if(hold == NULL) { 
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for pkt_list.\n");
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-	for(c = 0; c < ip_list_tot; c++) {
-		curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot;
-	}
+static void
+ipt_recent_destroy(const struct xt_match *match, void *matchinfo,
+		   unsigned int matchsize)
+{
+	const struct ipt_recent_info *info = matchinfo;
+	struct recent_table *t;
 
-	/* Allocate memory for the hash table */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for hash_table.\n",
-			sizeof(int)*ip_list_hash_size);
+	spin_lock_bh(&recent_lock);
+	t = recent_table_lookup(info->name);
+	if (--t->refcnt == 0) {
+		list_del(&t->list);
+		recent_table_flush(t);
+#ifdef CONFIG_PROC_FS
+		remove_proc_entry(t->name, proc_dir);
 #endif
-
-	curr_table->hash_table = vmalloc(sizeof(int)*ip_list_hash_size);
-	if(!curr_table->hash_table) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for hash_table.\n");
-		vfree(hold);
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-
-	for(c = 0; c < ip_list_hash_size; c++) {
-		curr_table->hash_table[c] = -1;
+		kfree(t);
 	}
+	spin_unlock_bh(&recent_lock);
+}
 
-	/* Allocate memory for the time info */
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for time_info.\n",
-			sizeof(struct time_info_list)*ip_list_tot);
-#endif
+#ifdef CONFIG_PROC_FS
+struct recent_iter_state {
+	struct recent_table	*table;
+	unsigned int		bucket;
+};
 
-	curr_table->time_info = vmalloc(sizeof(struct time_info_list)*ip_list_tot);
-	if(!curr_table->time_info) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for time_info.\n");
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table); 
-		vfree(curr_table);
-		return 0;
-	}
-	for(c = 0; c < ip_list_tot; c++) {
-		curr_table->time_info[c].position = c;
-		curr_table->time_info[c].time = 0;
-	}
+static void *recent_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct recent_iter_state *st = seq->private;
+	struct recent_table *t = st->table;
+	struct recent_entry *e;
+	loff_t p = *pos;
 
-	/* Put the new table in place */
 	spin_lock_bh(&recent_lock);
-	find_table = r_tables;
-	while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
-
-	/* If a table already exists just increment the count on that table and return */
-	if(find_table) { 
-		find_table->count++;	
-		spin_unlock_bh(&recent_lock);
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), created by other process.\n",info->name);
-#endif
-		vfree(curr_table->time_info);
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table);
-		vfree(curr_table);
-		return 1;
-	}
-	if(!last_table) r_tables = curr_table; else last_table->next = curr_table;
 
-	spin_unlock_bh(&recent_lock);
-
-#ifdef CONFIG_PROC_FS
-	/* Create our proc 'status' entry. */
-	curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
-	if (!curr_table->status_proc) {
-		printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
-		/* Destroy the created table */
-		spin_lock_bh(&recent_lock);
-		last_table = NULL;
-		curr_table = r_tables;
-		if(!curr_table) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, no tables.\n");
-#endif
-			spin_unlock_bh(&recent_lock);
-			return 0;
-		}
-		while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
-		if(!curr_table) {
-#ifdef DEBUG
-			if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, table already destroyed.\n");
-#endif
-			spin_unlock_bh(&recent_lock);
-			return 0;
+	for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) {
+		list_for_each_entry(e, &t->iphash[st->bucket], list) {
+			if (p-- == 0)
+				return e;
 		}
-		if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
-		spin_unlock_bh(&recent_lock);
-		vfree(curr_table->time_info);
-		vfree(curr_table->hash_table);
-		vfree(hold);
-		vfree(curr_table->table);
-		vfree(curr_table);
-		return 0;
 	}
-	
-	curr_table->status_proc->owner = THIS_MODULE;
-	curr_table->status_proc->data = curr_table;
-	wmb();
-	curr_table->status_proc->read_proc = ip_recent_get_info;
-	curr_table->status_proc->write_proc = ip_recent_ctrl;
-#endif /* CONFIG_PROC_FS */
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() left.\n");
-#endif
+	return NULL;
+}
 
-	return 1;
+static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct recent_iter_state *st = seq->private;
+	struct recent_table *t = st->table;
+	struct recent_entry *e = v;
+	struct list_head *head = e->list.next;
+
+	while (head == &t->iphash[st->bucket]) {
+		if (++st->bucket >= ip_list_hash_size)
+			return NULL;
+		head = t->iphash[st->bucket].next;
+	}
+	(*pos)++;
+	return list_entry(head, struct recent_entry, list);
 }
 
-/* This function is called in the event that a rule matching this module is
- * removed.
- * When this happens we need to check if there are no other rules matching
- * the table given.  If that is the case then we remove the table and clean
- * up its memory.
- */
-static void
-destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
+static void recent_seq_stop(struct seq_file *s, void *v)
 {
-	const struct ipt_recent_info *info = matchinfo;
-	struct recent_ip_tables *curr_table, *last_table;
+	spin_unlock_bh(&recent_lock);
+}
 
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() entered.\n");
-#endif
+static int recent_seq_show(struct seq_file *seq, void *v)
+{
+	struct recent_entry *e = v;
+	unsigned int i;
+
+	i = (e->index - 1) % ip_pkt_list_tot;
+	seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u",
+		   NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index);
+	for (i = 0; i < e->nstamps; i++)
+		seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]);
+	seq_printf(seq, "\n");
+	return 0;
+}
 
-	if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return;
+static struct seq_operations recent_seq_ops = {
+	.start		= recent_seq_start,
+	.next		= recent_seq_next,
+	.stop		= recent_seq_stop,
+	.show		= recent_seq_show,
+};
 
-	/* Lock the linked list while we play with it */
-	spin_lock_bh(&recent_lock);
+static int recent_seq_open(struct inode *inode, struct file *file)
+{
+	struct proc_dir_entry *pde = PDE(inode);
+	struct seq_file *seq;
+	struct recent_iter_state *st;
+	int ret;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL)
+		return -ENOMEM;
+	ret = seq_open(file, &recent_seq_ops);
+	if (ret)
+		kfree(st);
+	st->table    = pde->data;
+	seq          = file->private_data;
+	seq->private = st;
+	return ret;
+}
 
-	/* Look for an entry with this name already created */
-	/* Finds the end of the list and the entry before the end if current name does not exist */
-	last_table = NULL;
-	curr_table = r_tables;
-	if(!curr_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() No tables found, leaving.\n");
-#endif
+static ssize_t recent_proc_write(struct file *file, const char __user *input,
+				 size_t size, loff_t *loff)
+{
+	struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+	struct recent_table *t = pde->data;
+	struct recent_entry *e;
+	char buf[sizeof("+255.255.255.255")], *c = buf;
+	u_int32_t addr;
+	int add;
+
+	if (size > sizeof(buf))
+		size = sizeof(buf);
+	if (copy_from_user(buf, input, size))
+		return -EFAULT;
+	while (isspace(*c))
+		c++;
+
+	if (size - (c - buf) < 5)
+		return c - buf;
+	if (!strncmp(c, "clear", 5)) {
+		c += 5;
+		spin_lock_bh(&recent_lock);
+		recent_table_flush(t);
 		spin_unlock_bh(&recent_lock);
-		return;
+		return c - buf;
 	}
-	while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
 
-	/* If a table does not exist then do nothing and return */
-	if(!curr_table) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table not found, leaving.\n");
-#endif
-		spin_unlock_bh(&recent_lock);
-		return;
+	switch (*c) {
+	case '-':
+		add = 0;
+		c++;
+		break;
+	case '+':
+		c++;
+	default:
+		add = 1;
+		break;
 	}
+	addr = in_aton(c);
 
-	curr_table->count--;
-
-	/* If count is still non-zero then there are still rules referenceing it so we do nothing */
-	if(curr_table->count) { 
-#ifdef DEBUG
-		if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, non-zero count, leaving.\n");
-#endif
-		spin_unlock_bh(&recent_lock);
-		return;
+	spin_lock_bh(&recent_lock);
+	e = recent_entry_lookup(t, addr, 0);
+	if (e == NULL) {
+		if (add)
+			recent_entry_init(t, addr, 0);
+	} else {
+		if (add)
+			recent_entry_update(t, e);
+		else
+			recent_entry_remove(t, e);
 	}
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, zero count, removing.\n");
-#endif
-
-	/* Count must be zero so we remove this table from the list */
-	if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
-
 	spin_unlock_bh(&recent_lock);
+	return size;
+}
 
-	/* lock to make sure any late-runners still using this after we removed it from
-	 * the list finish up then remove everything */
-	spin_lock_bh(&curr_table->list_lock);
-	spin_unlock_bh(&curr_table->list_lock);
-
-#ifdef CONFIG_PROC_FS
-	if(curr_table->status_proc) remove_proc_entry(curr_table->name,proc_net_ipt_recent);
+static struct file_operations recent_fops = {
+	.open		= recent_seq_open,
+	.read		= seq_read,
+	.write		= recent_proc_write,
+	.release	= seq_release_private,
+	.owner		= THIS_MODULE,
+};
 #endif /* CONFIG_PROC_FS */
-	vfree(curr_table->table[0].last_pkts);
-	vfree(curr_table->table);
-	vfree(curr_table->hash_table);
-	vfree(curr_table->time_info);
-	vfree(curr_table);
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": destroy() left.\n");
-#endif
 
-	return;
-}
-
-/* This is the structure we pass to ipt_register to register our
- * module with iptables.
- */
 static struct ipt_match recent_match = {
 	.name		= "recent",
-	.match		= match,
+	.match		= ipt_recent_match,
 	.matchsize	= sizeof(struct ipt_recent_info),
-	.checkentry	= checkentry,
-	.destroy	= destroy,
-	.me		= THIS_MODULE
+	.checkentry	= ipt_recent_checkentry,
+	.destroy	= ipt_recent_destroy,
+	.me		= THIS_MODULE,
 };
 
-/* Kernel module initialization. */
 static int __init ipt_recent_init(void)
 {
-	int err, count;
+	int err;
 
-	printk(version);
-#ifdef CONFIG_PROC_FS
-	proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net);
-	if(!proc_net_ipt_recent) return -ENOMEM;
-#endif
-
-	if(ip_list_hash_size && ip_list_hash_size <= ip_list_tot) {
-	  printk(KERN_WARNING RECENT_NAME ": ip_list_hash_size too small, resetting to default.\n");
-	  ip_list_hash_size = 0;
-	}
-
-	if(!ip_list_hash_size) {
-		ip_list_hash_size = ip_list_tot*3;
-		count = 2*2;
-		while(ip_list_hash_size > count) count = count*2;
-		ip_list_hash_size = count;
-	}
-
-#ifdef DEBUG
-	if(debug) printk(KERN_INFO RECENT_NAME ": ip_list_hash_size: %d\n",ip_list_hash_size);
-#endif
+	if (!ip_list_tot || !ip_pkt_list_tot)
+		return -EINVAL;
+	ip_list_hash_size = 1 << fls(ip_list_tot);
 
 	err = ipt_register_match(&recent_match);
+#ifdef CONFIG_PROC_FS
 	if (err)
-		remove_proc_entry("ipt_recent", proc_net);
+		return err;
+	proc_dir = proc_mkdir("ipt_recent", proc_net);
+	if (proc_dir == NULL) {
+		ipt_unregister_match(&recent_match);
+		err = -ENOMEM;
+	}
+#endif
 	return err;
 }
 
-/* Kernel module destruction. */
-static void __exit ipt_recent_fini(void)
+static void __exit ipt_recent_exit(void)
 {
+	BUG_ON(!list_empty(&tables));
 	ipt_unregister_match(&recent_match);
-
-	remove_proc_entry("ipt_recent",proc_net);
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("ipt_recent", proc_net);
+#endif
 }
 
-/* Register our module with the kernel. */
 module_init(ipt_recent_init);
-module_exit(ipt_recent_fini);
+module_exit(ipt_recent_exit);

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-17  6:26                               ` Patrick McHardy
@ 2006-05-17  6:59                                 ` David S. Miller
  2006-05-17  7:19                                   ` Patrick McHardy
  2006-05-17  7:09                                 ` David S. Miller
                                                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 32+ messages in thread
From: David S. Miller @ 2006-05-17  6:59 UTC (permalink / raw)
  To: kaber
  Cc: sfrost, azez, willy, gcoady.lk, laforge, netfilter-devel,
	linux-kernel, marcelo

From: Patrick McHardy <kaber@trash.net>
Date: Wed, 17 May 2006 08:26:03 +0200

> Stephen Frost wrote:
> > Looking at this again...  The ttl isn't copied into 'ttl' unless the
> > check_set has TTL turned on.  This means that the overwritting was fine,
> > if you accept that you can only ever match on TTL, or never match on it.
> > That doesn't seem right to me.  The TTL in the table should always be
> > kept up-to-date and the only question is if the current rule requires it
> > for a match or not.
> 
> 
> OK, updated patch attached. The TTL is now always kept up-to-date.

Looks nice.

Is there any reasonable reason to allow ip_pkt_list_tot to ever be
larger than say 255?  If we can accept that limit, we can shrink
the recent_entry considerably by packing the index and nstamps
into a single word next to ttl.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-17  6:26                               ` Patrick McHardy
  2006-05-17  6:59                                 ` David S. Miller
@ 2006-05-17  7:09                                 ` David S. Miller
  2006-05-17  7:13                                   ` Roland Dreier
  2006-05-17  7:19                                   ` Patrick McHardy
  2006-05-17 13:14                                 ` Stephen Frost
  2006-06-01 13:43                                 ` Andrew James Wade
  3 siblings, 2 replies; 32+ messages in thread
From: David S. Miller @ 2006-05-17  7:09 UTC (permalink / raw)
  To: kaber
  Cc: sfrost, azez, willy, gcoady.lk, laforge, netfilter-devel,
	linux-kernel, marcelo

From: Patrick McHardy <kaber@trash.net>
Date: Wed, 17 May 2006 08:26:03 +0200

> +	if (info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE) &&
> +	    (info->seconds || info->hit_count))
> +		return 0;

I'm feeling particularly dense today... but what is the relative
precedence of '&' vs '&&'?

I've been told that if you have to look up C operator precedence,
don't bother and add parenthesis instead :)

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-17  7:09                                 ` David S. Miller
@ 2006-05-17  7:13                                   ` Roland Dreier
  2006-05-17  7:19                                   ` Patrick McHardy
  1 sibling, 0 replies; 32+ messages in thread
From: Roland Dreier @ 2006-05-17  7:13 UTC (permalink / raw)
  To: David S. Miller
  Cc: kaber, sfrost, azez, willy, gcoady.lk, laforge, netfilter-devel,
	linux-kernel, marcelo

    David> I'm feeling particularly dense today... but what is the
    David> relative precedence of '&' vs '&&'?

& binds tighter than &&.  "man operator" can be your friend...

    David> I've been told that if you have to look up C operator
    David> precedence, don't bother and add parenthesis instead :) -

Probably a good rule though.

 - R.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-17  6:59                                 ` David S. Miller
@ 2006-05-17  7:19                                   ` Patrick McHardy
  2006-05-17 10:55                                     ` Stephen Frost
  0 siblings, 1 reply; 32+ messages in thread
From: Patrick McHardy @ 2006-05-17  7:19 UTC (permalink / raw)
  To: David S. Miller
  Cc: sfrost, azez, willy, gcoady.lk, laforge, netfilter-devel,
	linux-kernel, marcelo

David S. Miller wrote:
> From: Patrick McHardy <kaber@trash.net>
> Date: Wed, 17 May 2006 08:26:03 +0200
> 
>>OK, updated patch attached. The TTL is now always kept up-to-date.
> 
> 
> Looks nice.
> 
> Is there any reasonable reason to allow ip_pkt_list_tot to ever be
> larger than say 255?  If we can accept that limit, we can shrink
> the recent_entry considerably by packing the index and nstamps
> into a single word next to ttl.


My primary goal was full compatibility, I have no idea about real-life
usage though. Maybe Stephen can answer this.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-17  7:09                                 ` David S. Miller
  2006-05-17  7:13                                   ` Roland Dreier
@ 2006-05-17  7:19                                   ` Patrick McHardy
  1 sibling, 0 replies; 32+ messages in thread
From: Patrick McHardy @ 2006-05-17  7:19 UTC (permalink / raw)
  To: David S. Miller
  Cc: sfrost, azez, willy, gcoady.lk, laforge, netfilter-devel,
	linux-kernel, marcelo

David S. Miller wrote:
> From: Patrick McHardy <kaber@trash.net>
> Date: Wed, 17 May 2006 08:26:03 +0200
> 
> 
>>+	if (info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE) &&
>>+	    (info->seconds || info->hit_count))
>>+		return 0;
> 
> 
> I'm feeling particularly dense today... but what is the relative
> precedence of '&' vs '&&'?
> 
> I've been told that if you have to look up C operator precedence,
> don't bother and add parenthesis instead :)


Bitwise AND has precedence, but I have no problems adding an extra
set of parenthesis around it :)

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-17  7:19                                   ` Patrick McHardy
@ 2006-05-17 10:55                                     ` Stephen Frost
  0 siblings, 0 replies; 32+ messages in thread
From: Stephen Frost @ 2006-05-17 10:55 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: David S. Miller, azez, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 661 bytes --]

* Patrick McHardy (kaber@trash.net) wrote:
> David S. Miller wrote:
> > Is there any reasonable reason to allow ip_pkt_list_tot to ever be
> > larger than say 255?  If we can accept that limit, we can shrink
> > the recent_entry considerably by packing the index and nstamps
> > into a single word next to ttl.
> 
> My primary goal was full compatibility, I have no idea about real-life
> usage though. Maybe Stephen can answer this.

I don't recall ever seeing > 255 usage.  It's been pretty rare for it to
be changed from the default at all from what I've seen.  Making the
limit be 255 seems perfectly reasonable to me.

	Thanks,

		Stephen

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-17  6:26                               ` Patrick McHardy
  2006-05-17  6:59                                 ` David S. Miller
  2006-05-17  7:09                                 ` David S. Miller
@ 2006-05-17 13:14                                 ` Stephen Frost
  2006-06-01 13:43                                 ` Andrew James Wade
  3 siblings, 0 replies; 32+ messages in thread
From: Stephen Frost @ 2006-05-17 13:14 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Amin Azez, David S. Miller, willy, gcoady.lk, laforge,
	netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 1374 bytes --]

* Patrick McHardy (kaber@trash.net) wrote:
> OK, updated patch attached. The TTL is now always kept up-to-date.

Yup, that looks good.  Unfortunately, it looks like the lru_list isn't
being kept track of correctly now.  Perhaps I'm reading it wrong but it
*looks* like recent_entry_init() is only initializing the lru_list for
the local entry but doesn't ever add it to the main table lru_list.  My
guess is you were expecting that to be done by recent_entry_update() but
it's never the case that recent_entry_update() is called directly after
recent_entry_init() due to the 'goto out' (my line 199).  Therefore I'm
afraid that a new entry is never added to the lru_list with the current
setup and if nothing is ever updated you'll end up in a bad situation.

I think you can just drop lines 198 & 199 and modify recent_entry_init()
to not put the initial stamp in.  This way, for a new entry to the list,
recent_entry_init() is called still on 195, the return value is updated
just like it would be for an existing entry, and recent_entry_update()
is called to handle adding the latest stamp and updating the lru_list.

Looking at list.h, I *think* that will work (wasn't sure if
list_move_tail() would be upset about the state of the e->lru_list
coming from INIT_LIST_HEAD but I think the __list_del will effectively
be a no-op and so it'll be fine).

	Thanks,

		Stephen

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-05-17  6:26                               ` Patrick McHardy
                                                   ` (2 preceding siblings ...)
  2006-05-17 13:14                                 ` Stephen Frost
@ 2006-06-01 13:43                                 ` Andrew James Wade
  2006-06-01 14:53                                   ` Patrick McHardy
  3 siblings, 1 reply; 32+ messages in thread
From: Andrew James Wade @ 2006-06-01 13:43 UTC (permalink / raw)
  To: Patrick McHardy, Andrew Morton
  Cc: Stephen Frost, Amin Azez, David S. Miller, willy, gcoady.lk,
	laforge, netfilter-devel, linux-kernel, marcelo

[-- Attachment #1: Type: text/plain, Size: 3962 bytes --]

Hello Mr. McHardy,

The BUG below appears to be related to your ipt_recent rewrite. I
haven't tracked it down further yet. I've attached the (toy) firewall
script that's triggering the bug.

Regards,
Andrew Wade

[from 2.6.17-rc5-mm2]
BUG: sleeping function called from invalid context at mm/slab.c:2863
in_atomic():1, irqs_disabled():0
 [<c01495dd>] __kmalloc+0x5c/0xb8
 [<c01784c8>] proc_create+0x99/0x108
 [<c0178595>] create_proc_entry+0x5e/0x9e
 [<c037533c>] ipt_recent_checkentry+0xfb/0x157
 [<c0374755>] translate_table+0x333/0x5c1
 [<c0374acd>] do_ipt_set_ctl+0xea/0x44f
 [<c02c0b0b>] ide_execute_command+0x9c/0xcd
 [<c02c4d0e>] dma_timer_expiry+0x0/0x5d
 [<c02c0b1a>] ide_execute_command+0xab/0xcd
 [<c03471d6>] nf_sockopt+0xa6/0xd6
 [<c0347223>] nf_setsockopt+0x1d/0x22
 [<c0353b40>] ip_setsockopt+0xa48/0xa64
 [<c018e37a>] reiserfs_dirty_inode+0x6d/0x76
 [<c01698c0>] __mark_inode_dirty+0x15a/0x17a
 [<c013061f>] do_generic_mapping_read+0x5c7/0x5d1
 [<c021d7a8>] prio_tree_insert+0x150/0x207
 [<c021e465>] rb_insert_color+0x8f/0xb2
 [<c021d5a1>] prio_tree_remove+0x96/0xa5
 [<c01485f3>] __cache_free+0x1a6/0x2fc
 [<c01487e5>] kmem_cache_free+0x4d/0x52
 [<c021d7a8>] prio_tree_insert+0x150/0x207
 [<c0106b48>] kernel_fpu_begin+0x12/0x44
 [<c021ffb0>] mmx_clear_page+0x69/0x81
 [<c01339de>] get_page_from_freelist+0x2a2/0x328
 [<c012edee>] find_get_page+0x3f/0x56
 [<c0133ac7>] __alloc_pages+0x63/0x28e
 [<c014032a>] __page_set_anon_rmap+0x2b/0x2f
 [<c013c04a>] __handle_mm_fault+0x14f/0x741
 [<c013c0a8>] __handle_mm_fault+0x1ad/0x741
 [<c0330da1>] sys_setsockopt+0x5b/0x7b
 [<c03323e2>] sys_socketcall+0x148/0x188
 [<c010f36b>] do_page_fault+0x0/0x520
 [<c0385a29>] sysenter_past_esp+0x56/0x79
BUG: unable to handle kernel NULL pointer dereference at virtual address 000000d4
 printing eip:
c03754c1
*pde = 00000000
Oops: 0000 [#1]
PREEMPT 
last sysfs file: /block/hda/hda1/size
CPU:    0
EIP:    0060:[<c03754c1>]    Not tainted VLI
EFLAGS: 00010246   (2.6.17-rc5-mm2 #1) 
EIP is at ipt_recent_destroy+0x1e/0x8e
eax: 00000000   ebx: 00000000   ecx: cea9088a   edx: 00000000
esi: c0411680   edi: cea907f0   ebp: d3085660   esp: cf308ca4
ds: 007b   es: 007b   ss: 0068
Process iptables (pid: 3669, threadinfo=cf308000 task=d33b7050)
Stack: cea90860 c0374bde c0411740 cea90880 000000d4 cf2c6000 08056a18 d30859f0 
       000000b0 e1ddc000 cea90000 000007f0 00000070 dfffb080 00000000 c01485f3 
       dfff6ac8 746c6966 00007265 00000000 00000000 00000000 00000000 00000000 
Call Trace:
 [<c0374bde>] do_ipt_set_ctl+0x1fb/0x44f
 [<c01485f3>] __cache_free+0x1a6/0x2fc
 [<c03471d6>] nf_sockopt+0xa6/0xd6
 [<c0347223>] nf_setsockopt+0x1d/0x22
 [<c0353b40>] ip_setsockopt+0xa48/0xa64
 [<c0160c79>] __d_lookup+0xa7/0x13c
 [<c0160cf1>] __d_lookup+0x11f/0x13c
 [<c011866a>] current_fs_time+0x3e/0x58
 [<c01618ef>] touch_atime+0x67/0x9f
 [<c013061f>] do_generic_mapping_read+0x5c7/0x5d1
 [<c021d7a8>] prio_tree_insert+0x150/0x207
 [<c021e465>] rb_insert_color+0x8f/0xb2
 [<c021d5a1>] prio_tree_remove+0x96/0xa5
 [<c01485f3>] __cache_free+0x1a6/0x2fc
 [<c01487e5>] kmem_cache_free+0x4d/0x52
 [<c021d7a8>] prio_tree_insert+0x150/0x207
 [<c0106b48>] kernel_fpu_begin+0x12/0x44
 [<c021ffb0>] mmx_clear_page+0x69/0x81
 [<c01339de>] get_page_from_freelist+0x2a2/0x328
 [<c012edee>] find_get_page+0x3f/0x56
 [<c0133ac7>] __alloc_pages+0x63/0x28e
 [<c014032a>] __page_set_anon_rmap+0x2b/0x2f
 [<c013c04a>] __handle_mm_fault+0x14f/0x741
 [<c013c0a8>] __handle_mm_fault+0x1ad/0x741
 [<c0330da1>] sys_setsockopt+0x5b/0x7b
 [<c03323e2>] sys_socketcall+0x148/0x188
 [<c010f36b>] do_page_fault+0x0/0x520
 [<c0385a29>] sysenter_past_esp+0x56/0x79
Code: 3b 3d 78 6a 4e c0 72 df 5b 5e 5f 5d c3 53 8b 5c 24 0c e8 cd 34 da ff b8 01 00 00 00 e8 1f b1 d9 ff 8d 43 0a e8 2f fb ff ff 89 c3 <8b> 80 d4 00 00 00 48 85 c0 89 83 d4 00 00 00 75 4f 8b 53 04 39 
EIP: [<c03754c1>] ipt_recent_destroy+0x1e/0x8e SS:ESP 0068:cf308ca4
 <0>Kernel panic - not syncing: Fatal exception in interrupt

[-- Attachment #2: config-2.6.17-rc5-mm2 --]
[-- Type: text/plain, Size: 35837 bytes --]

#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.17-rc5-mm2
# Thu Jun  1 08:02:06 2006
#
CONFIG_X86_32=y
CONFIG_GENERIC_TIME=y
CONFIG_SEMAPHORE_SLEEPERS=y
CONFIG_X86=y
CONFIG_MMU=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_ARCH_MAY_HAVE_PC_FDC=y
CONFIG_DMI=y
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"

#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32

#
# General setup
#
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
CONFIG_SWAP=y
CONFIG_SWAP_PREFETCH=y
CONFIG_SYSVIPC=y
CONFIG_POSIX_MQUEUE=y
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_TASKSTATS is not set
CONFIG_SYSCTL=y
# CONFIG_UTS_NS is not set
# CONFIG_AUDIT is not set
# CONFIG_IKCONFIG is not set
# CONFIG_RELAY is not set
CONFIG_INITRAMFS_SOURCE=""
CONFIG_KLIBC_ERRLIST=y
CONFIG_KLIBC_ZLIB=y
CONFIG_UID16=y
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_RT_MUTEXES=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_SHMEM=y
CONFIG_SLAB=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
# CONFIG_SLOB is not set

#
# Loadable module support
#
# CONFIG_MODULES is not set

#
# Block layer
#
# CONFIG_LBD is not set
# CONFIG_BLK_DEV_IO_TRACE is not set
# CONFIG_LSF is not set

#
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_DEADLINE=y
CONFIG_IOSCHED_CFQ=y
CONFIG_DEFAULT_AS=y
# CONFIG_DEFAULT_DEADLINE is not set
# CONFIG_DEFAULT_CFQ is not set
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="anticipatory"

#
# Processor type and features
#
# CONFIG_SMP is not set
CONFIG_X86_PC=y
# CONFIG_X86_ELAN is not set
# CONFIG_X86_VOYAGER is not set
# CONFIG_X86_NUMAQ is not set
# CONFIG_X86_SUMMIT is not set
# CONFIG_X86_BIGSMP is not set
# CONFIG_X86_VISWS is not set
# CONFIG_X86_GENERICARCH is not set
# CONFIG_X86_ES7000 is not set
# CONFIG_M386 is not set
# CONFIG_M486 is not set
# CONFIG_M586 is not set
# CONFIG_M586TSC is not set
# CONFIG_M586MMX is not set
# CONFIG_M686 is not set
# CONFIG_MPENTIUMII is not set
# CONFIG_MPENTIUMIII is not set
# CONFIG_MPENTIUMM is not set
# CONFIG_MPENTIUM4 is not set
# CONFIG_MK6 is not set
CONFIG_MK7=y
# CONFIG_MK8 is not set
# CONFIG_MCRUSOE is not set
# CONFIG_MEFFICEON is not set
# CONFIG_MWINCHIPC6 is not set
# CONFIG_MWINCHIP2 is not set
# CONFIG_MWINCHIP3D is not set
# CONFIG_MGEODEGX1 is not set
# CONFIG_MGEODE_LX is not set
# CONFIG_MCYRIXIII is not set
# CONFIG_MVIAC3_2 is not set
# CONFIG_X86_GENERIC is not set
CONFIG_X86_CMPXCHG=y
CONFIG_X86_XADD=y
CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_X86_WP_WORKS_OK=y
CONFIG_X86_INVLPG=y
CONFIG_X86_BSWAP=y
CONFIG_X86_POPAD_OK=y
CONFIG_X86_CMPXCHG64=y
CONFIG_X86_GOOD_APIC=y
CONFIG_X86_INTEL_USERCOPY=y
CONFIG_X86_USE_PPRO_CHECKSUM=y
CONFIG_X86_USE_3DNOW=y
CONFIG_X86_TSC=y
CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
# CONFIG_PREEMPT_NONE is not set
# CONFIG_PREEMPT_VOLUNTARY is not set
CONFIG_PREEMPT=y
CONFIG_PREEMPT_BKL=y
CONFIG_X86_UP_APIC=y
CONFIG_X86_UP_IOAPIC=y
CONFIG_X86_LOCAL_APIC=y
CONFIG_X86_IO_APIC=y
CONFIG_X86_MCE=y
CONFIG_X86_MCE_NONFATAL=y
# CONFIG_X86_MCE_P4THERMAL is not set
CONFIG_VM86=y
# CONFIG_TOSHIBA is not set
# CONFIG_I8K is not set
# CONFIG_X86_REBOOTFIXUPS is not set
# CONFIG_MICROCODE is not set
CONFIG_X86_MSR=y
CONFIG_X86_CPUID=y

#
# Firmware Drivers
#
# CONFIG_EDD is not set
# CONFIG_DELL_RBU is not set
# CONFIG_DCDBAS is not set
CONFIG_NOHIGHMEM=y
# CONFIG_HIGHMEM4G is not set
# CONFIG_HIGHMEM64G is not set
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_ARCH_FLATMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
CONFIG_ARCH_ALIGNED_ZONE_BOUNDARIES=y
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
CONFIG_SPARSEMEM_STATIC=y
CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_UNALIGNED_ZONE_BOUNDARIES is not set
CONFIG_ADAPTIVE_READAHEAD=y
CONFIG_DEBUG_READAHEAD=y
CONFIG_READAHEAD_SMOOTH_AGING=y
# CONFIG_MATH_EMULATION is not set
CONFIG_MTRR=y
# CONFIG_EFI is not set
# CONFIG_REGPARM is not set
CONFIG_SECCOMP=y
# CONFIG_HZ_100 is not set
CONFIG_HZ_250=y
# CONFIG_HZ_1000 is not set
CONFIG_HZ=250
# CONFIG_KEXEC is not set
CONFIG_PHYSICAL_START=0x100000
# CONFIG_RESOURCES_32BIT is not set
CONFIG_COMPAT_VDSO=y

#
# Power management options (ACPI, APM)
#
CONFIG_PM=y
CONFIG_PM_LEGACY=y
# CONFIG_PM_DEBUG is not set
# CONFIG_SOFTWARE_SUSPEND is not set

#
# ACPI (Advanced Configuration and Power Interface) Support
#
CONFIG_ACPI=y
# CONFIG_ACPI_SLEEP is not set
# CONFIG_ACPI_AC is not set
# CONFIG_ACPI_BATTERY is not set
CONFIG_ACPI_BUTTON=y
CONFIG_ACPI_VIDEO=y
# CONFIG_ACPI_HOTKEY is not set
# CONFIG_ACPI_FAN is not set
CONFIG_ACPI_DOCK=y
CONFIG_ACPI_PROCESSOR=y
CONFIG_ACPI_THERMAL=y
# CONFIG_ACPI_ASUS is not set
# CONFIG_ACPI_ATLAS is not set
# CONFIG_ACPI_IBM is not set
# CONFIG_ACPI_TOSHIBA is not set
CONFIG_ACPI_SONY=y
CONFIG_ACPI_BLACKLIST_YEAR=0
# CONFIG_ACPI_DEBUG is not set
CONFIG_ACPI_EC=y
CONFIG_ACPI_POWER=y
CONFIG_ACPI_SYSTEM=y
CONFIG_X86_PM_TIMER=y
# CONFIG_ACPI_CONTAINER is not set

#
# APM (Advanced Power Management) BIOS Support
#
# CONFIG_APM is not set

#
# CPU Frequency scaling
#
# CONFIG_CPU_FREQ is not set

#
# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
#
CONFIG_PCI=y
# CONFIG_PCI_GOBIOS is not set
# CONFIG_PCI_GOMMCONFIG is not set
# CONFIG_PCI_GODIRECT is not set
CONFIG_PCI_GOANY=y
CONFIG_PCI_BIOS=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
# CONFIG_PCIEPORTBUS is not set
# CONFIG_PCI_MSI is not set
# CONFIG_PCI_DEBUG is not set
CONFIG_ISA_DMA_API=y
# CONFIG_ISA is not set
# CONFIG_MCA is not set
# CONFIG_SCx200 is not set

#
# PCCARD (PCMCIA/CardBus) support
#
# CONFIG_PCCARD is not set

#
# PCI Hotplug Support
#
# CONFIG_HOTPLUG_PCI is not set

#
# Executable file formats
#
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_AOUT is not set
CONFIG_BINFMT_MISC=y

#
# Networking
#
CONFIG_NET=y

#
# Networking options
#
# CONFIG_NETDEBUG is not set
CONFIG_PACKET=y
CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
CONFIG_XFRM=y
# CONFIG_XFRM_USER is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
CONFIG_IP_FIB_HASH=y
# CONFIG_IP_PNP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_ARPD is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_INET_XFRM_MODE_TUNNEL=y
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_BIC=y

#
# IP: Virtual Server Configuration
#
# CONFIG_IP_VS is not set
# CONFIG_IPV6 is not set
# CONFIG_INET6_XFRM_TUNNEL is not set
# CONFIG_INET6_TUNNEL is not set
# CONFIG_NETWORK_SECMARK is not set
CONFIG_NETFILTER=y
# CONFIG_NETFILTER_DEBUG is not set

#
# Core Netfilter Configuration
#
CONFIG_NETFILTER_NETLINK=y
CONFIG_NETFILTER_NETLINK_QUEUE=y
# CONFIG_NETFILTER_NETLINK_LOG is not set
# CONFIG_NF_CONNTRACK is not set
CONFIG_NETFILTER_XTABLES=y
# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
# CONFIG_NETFILTER_XT_TARGET_MARK is not set
# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
# CONFIG_NETFILTER_XT_MATCH_ESP is not set
# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
# CONFIG_NETFILTER_XT_MATCH_MAC is not set
# CONFIG_NETFILTER_XT_MATCH_MARK is not set
# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
# CONFIG_NETFILTER_XT_MATCH_REALM is not set
# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
# CONFIG_NETFILTER_XT_MATCH_STRING is not set
# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set

#
# IP: Netfilter Configuration
#
# CONFIG_IP_NF_CONNTRACK is not set
# CONFIG_IP_NF_QUEUE is not set
CONFIG_IP_NF_IPTABLES=y
# CONFIG_IP_NF_MATCH_IPRANGE is not set
# CONFIG_IP_NF_MATCH_TOS is not set
CONFIG_IP_NF_MATCH_RECENT=y
# CONFIG_IP_NF_MATCH_ECN is not set
# CONFIG_IP_NF_MATCH_DSCP is not set
# CONFIG_IP_NF_MATCH_AH is not set
# CONFIG_IP_NF_MATCH_TTL is not set
# CONFIG_IP_NF_MATCH_OWNER is not set
# CONFIG_IP_NF_MATCH_ADDRTYPE is not set
# CONFIG_IP_NF_MATCH_HASHLIMIT is not set
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_REJECT=y
# CONFIG_IP_NF_TARGET_LOG is not set
# CONFIG_IP_NF_TARGET_ULOG is not set
CONFIG_IP_NF_TARGET_TCPMSS=y
# CONFIG_IP_NF_MANGLE is not set
# CONFIG_IP_NF_RAW is not set
# CONFIG_IP_NF_ARPTABLES is not set

#
# DCCP Configuration (EXPERIMENTAL)
#
# CONFIG_IP_DCCP is not set

#
# SCTP Configuration (EXPERIMENTAL)
#
# CONFIG_IP_SCTP is not set

#
# TIPC Configuration (EXPERIMENTAL)
#
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set

#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set

#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_IEEE80211 is not set

#
# Device Drivers
#

#
# Generic Driver Options
#
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
# CONFIG_FW_LOADER is not set
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_SYS_HYPERVISOR is not set

#
# Connector - unified userspace <-> kernelspace linker
#
# CONFIG_CONNECTOR is not set

#
# Memory Technology Devices (MTD)
#
# CONFIG_MTD is not set

#
# Parallel port support
#
# CONFIG_PARPORT is not set

#
# Plug and Play support
#
CONFIG_PNP=y
# CONFIG_PNP_DEBUG is not set

#
# Protocols
#
CONFIG_PNPACPI=y

#
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
# CONFIG_BLK_DEV_DAC960 is not set
# CONFIG_BLK_DEV_UMEM is not set
# CONFIG_BLK_DEV_COW_COMMON is not set
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_CRYPTOLOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_SX8 is not set
# CONFIG_BLK_DEV_UB is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set

#
# ATA/ATAPI/MFM/RLL support
#
CONFIG_IDE=y
CONFIG_BLK_DEV_IDE=y

#
# Please see Documentation/ide.txt for help/info on IDE drives
#
# CONFIG_BLK_DEV_IDE_SATA is not set
# CONFIG_BLK_DEV_HD_IDE is not set
CONFIG_BLK_DEV_IDEDISK=y
CONFIG_IDEDISK_MULTI_MODE=y
CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDETAPE is not set
# CONFIG_BLK_DEV_IDEFLOPPY is not set
# CONFIG_BLK_DEV_IDESCSI is not set
# CONFIG_IDE_TASK_IOCTL is not set

#
# IDE chipset support/bugfixes
#
CONFIG_IDE_GENERIC=y
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_IDEPNP is not set
CONFIG_BLK_DEV_IDEPCI=y
CONFIG_IDEPCI_SHARE_IRQ=y
# CONFIG_BLK_DEV_OFFBOARD is not set
CONFIG_BLK_DEV_GENERIC=y
# CONFIG_BLK_DEV_OPTI621 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
CONFIG_BLK_DEV_IDEDMA_PCI=y
# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
CONFIG_IDEDMA_PCI_AUTO=y
# CONFIG_IDEDMA_ONLYDISK is not set
# CONFIG_BLK_DEV_AEC62XX is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD74XX is not set
# CONFIG_BLK_DEV_ATIIXP is not set
# CONFIG_BLK_DEV_CMD64X is not set
# CONFIG_BLK_DEV_TRIFLEX is not set
# CONFIG_BLK_DEV_CY82C693 is not set
# CONFIG_BLK_DEV_CS5520 is not set
# CONFIG_BLK_DEV_CS5530 is not set
# CONFIG_BLK_DEV_CS5535 is not set
# CONFIG_BLK_DEV_HPT34X is not set
# CONFIG_BLK_DEV_HPT366 is not set
# CONFIG_BLK_DEV_SC1200 is not set
# CONFIG_BLK_DEV_PIIX is not set
# CONFIG_BLK_DEV_IT821X is not set
# CONFIG_BLK_DEV_NS87415 is not set
# CONFIG_BLK_DEV_PDC202XX_OLD is not set
# CONFIG_BLK_DEV_PDC202XX_NEW is not set
# CONFIG_BLK_DEV_SVWKS is not set
# CONFIG_BLK_DEV_SIIMAGE is not set
# CONFIG_BLK_DEV_SIS5513 is not set
# CONFIG_BLK_DEV_SLC90E66 is not set
# CONFIG_BLK_DEV_TRM290 is not set
CONFIG_BLK_DEV_VIA82CXXX=y
# CONFIG_IDE_ARM is not set
CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_IDEDMA_IVB is not set
CONFIG_IDEDMA_AUTO=y
# CONFIG_BLK_DEV_HD is not set

#
# SCSI device support
#
# CONFIG_RAID_ATTRS is not set
CONFIG_SCSI=y
# CONFIG_SCSI_TGT is not set
# CONFIG_SCSI_PROC_FS is not set

#
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=y
# CONFIG_CHR_DEV_ST is not set
# CONFIG_CHR_DEV_OSST is not set
# CONFIG_BLK_DEV_SR is not set
# CONFIG_CHR_DEV_SG is not set
# CONFIG_CHR_DEV_SCH is not set

#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
# CONFIG_SCSI_MULTI_LUN is not set
# CONFIG_SCSI_CONSTANTS is not set
# CONFIG_SCSI_LOGGING is not set

#
# SCSI Transports
#
# CONFIG_SCSI_SPI_ATTRS is not set
# CONFIG_SCSI_FC_ATTRS is not set
# CONFIG_SCSI_ISCSI_ATTRS is not set
# CONFIG_SCSI_SAS_ATTRS is not set
# CONFIG_SCSI_SAS_DOMAIN_ATTRS is not set

#
# SCSI low-level drivers
#
# CONFIG_ISCSI_TCP is not set
# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_3W_9XXX is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AACRAID is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_AIC79XX is not set
# CONFIG_SCSI_AIC94XX is not set
# CONFIG_SCSI_DPT_I2O is not set
# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_ARCMSR is not set
# CONFIG_MEGARAID_NEWGEN is not set
# CONFIG_MEGARAID_LEGACY is not set
# CONFIG_MEGARAID_SAS is not set
# CONFIG_SCSI_SATA is not set
# CONFIG_SCSI_HPTIOP is not set
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_IPS is not set
# CONFIG_SCSI_INITIO is not set
# CONFIG_SCSI_INIA100 is not set
# CONFIG_SCSI_STEX is not set
# CONFIG_SCSI_SYM53C8XX_2 is not set
# CONFIG_SCSI_IPR is not set
# CONFIG_SCSI_QLOGIC_1280 is not set
# CONFIG_SCSI_QLA_FC is not set
# CONFIG_SCSI_LPFC is not set
# CONFIG_SCSI_DC395x is not set
# CONFIG_SCSI_DC390T is not set
# CONFIG_SCSI_NSP32 is not set
# CONFIG_SCSI_DEBUG is not set
# CONFIG_SCSI_SRP is not set

#
# Multi-device support (RAID and LVM)
#
# CONFIG_MD is not set

#
# Fusion MPT device support
#
# CONFIG_FUSION is not set
# CONFIG_FUSION_SPI is not set
# CONFIG_FUSION_FC is not set
# CONFIG_FUSION_SAS is not set

#
# IEEE 1394 (FireWire) support
#
# CONFIG_IEEE1394 is not set

#
# I2O device support
#
# CONFIG_I2O is not set

#
# Network device support
#
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_NET_SB1000 is not set

#
# ARCnet devices
#
# CONFIG_ARCNET is not set

#
# PHY device support
#
# CONFIG_PHYLIB is not set

#
# Ethernet (10 or 100Mbit)
#
CONFIG_NET_ETHERNET=y
CONFIG_MII=y
# CONFIG_HAPPYMEAL is not set
# CONFIG_SUNGEM is not set
# CONFIG_CASSINI is not set
# CONFIG_NET_VENDOR_3COM is not set

#
# Tulip family network device support
#
# CONFIG_NET_TULIP is not set
# CONFIG_HP100 is not set
CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
# CONFIG_AMD8111_ETH is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_B44 is not set
# CONFIG_FORCEDETH is not set
# CONFIG_DGRS is not set
# CONFIG_EEPRO100 is not set
# CONFIG_E100 is not set
# CONFIG_FEALNX is not set
# CONFIG_NATSEMI is not set
# CONFIG_NE2K_PCI is not set
# CONFIG_8139CP is not set
CONFIG_8139TOO=y
# CONFIG_8139TOO_PIO is not set
# CONFIG_8139TOO_TUNE_TWISTER is not set
# CONFIG_8139TOO_8129 is not set
# CONFIG_8139_OLD_RX_RESET is not set
# CONFIG_SIS900 is not set
# CONFIG_EPIC100 is not set
# CONFIG_SUNDANCE is not set
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set

#
# Ethernet (1000 Mbit)
#
# CONFIG_ACENIC is not set
# CONFIG_DL2K is not set
# CONFIG_E1000 is not set
# CONFIG_NS83820 is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_R8169 is not set
# CONFIG_SIS190 is not set
# CONFIG_SKGE is not set
# CONFIG_SKY2 is not set
# CONFIG_SK98LIN is not set
# CONFIG_VIA_VELOCITY is not set
# CONFIG_TIGON3 is not set
# CONFIG_BNX2 is not set

#
# Ethernet (10000 Mbit)
#
# CONFIG_CHELSIO_T1 is not set
# CONFIG_IXGB is not set
# CONFIG_S2IO is not set
# CONFIG_MYRI10GE is not set

#
# Token Ring devices
#
# CONFIG_TR is not set

#
# Wireless LAN (non-hamradio)
#
# CONFIG_NET_RADIO is not set

#
# Wan interfaces
#
# CONFIG_WAN is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NET_FC is not set
# CONFIG_SHAPER is not set
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set

#
# ISDN subsystem
#
# CONFIG_ISDN is not set

#
# Telephony Support
#
# CONFIG_PHONE is not set

#
# Input device support
#
CONFIG_INPUT=y
# CONFIG_INPUT_FF_EFFECTS is not set

#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_TSDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set

#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_SERIO_I8042=y
# CONFIG_SERIO_SERPORT is not set
# CONFIG_SERIO_CT82C710 is not set
# CONFIG_SERIO_PCIPS2 is not set
CONFIG_SERIO_LIBPS2=y
# CONFIG_SERIO_RAW is not set
# CONFIG_GAMEPORT is not set

#
# Character devices
#
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
# CONFIG_SERIAL_NONSTANDARD is not set

#
# Serial drivers
#
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_PCI=y
CONFIG_SERIAL_8250_PNP=y
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
# CONFIG_SERIAL_8250_EXTENDED is not set

#
# Non-8250 serial port support
#
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_SERIAL_JSM is not set
CONFIG_UNIX98_PTYS=y
# CONFIG_LEGACY_PTYS is not set

#
# IPMI
#
# CONFIG_IPMI_HANDLER is not set

#
# Watchdog Cards
#
# CONFIG_WATCHDOG is not set
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_INTEL=y
CONFIG_HW_RANDOM_AMD=y
CONFIG_HW_RANDOM_GEODE=y
CONFIG_HW_RANDOM_VIA=y
# CONFIG_NVRAM is not set
CONFIG_RTC=y
# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
# CONFIG_APPLICOM is not set
# CONFIG_SONYPI is not set

#
# Ftape, the floppy tape device driver
#
# CONFIG_FTAPE is not set
CONFIG_AGP=y
# CONFIG_AGP_ALI is not set
# CONFIG_AGP_ATI is not set
# CONFIG_AGP_AMD is not set
# CONFIG_AGP_AMD64 is not set
# CONFIG_AGP_INTEL is not set
# CONFIG_AGP_NVIDIA is not set
# CONFIG_AGP_SIS is not set
# CONFIG_AGP_SWORKS is not set
CONFIG_AGP_VIA=y
# CONFIG_AGP_EFFICEON is not set
CONFIG_DRM=y
# CONFIG_DRM_TDFX is not set
# CONFIG_DRM_R128 is not set
CONFIG_DRM_RADEON=y
# CONFIG_DRM_MGA is not set
# CONFIG_DRM_SIS is not set
# CONFIG_DRM_VIA is not set
# CONFIG_DRM_SAVAGE is not set
# CONFIG_MWAVE is not set
# CONFIG_CS5535_GPIO is not set
# CONFIG_RAW_DRIVER is not set
CONFIG_HPET=y
# CONFIG_HPET_RTC_IRQ is not set
CONFIG_HPET_MMAP=y
# CONFIG_HANGCHECK_TIMER is not set

#
# TPM devices
#
# CONFIG_TCG_TPM is not set
# CONFIG_TELCLOCK is not set

#
# I2C support
#
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y

#
# I2C Algorithms
#
CONFIG_I2C_ALGOBIT=y
# CONFIG_I2C_ALGOPCF is not set
# CONFIG_I2C_ALGOPCA is not set

#
# I2C Hardware Bus support
#
# CONFIG_I2C_ALI1535 is not set
# CONFIG_I2C_ALI1563 is not set
# CONFIG_I2C_ALI15X3 is not set
# CONFIG_I2C_AMD756 is not set
# CONFIG_I2C_AMD8111 is not set
# CONFIG_I2C_I801 is not set
# CONFIG_I2C_I810 is not set
# CONFIG_I2C_PIIX4 is not set
# CONFIG_I2C_NFORCE2 is not set
# CONFIG_I2C_PARPORT_LIGHT is not set
# CONFIG_I2C_PROSAVAGE is not set
# CONFIG_I2C_SAVAGE4 is not set
# CONFIG_SCx200_ACB is not set
# CONFIG_I2C_SIS5595 is not set
# CONFIG_I2C_SIS630 is not set
# CONFIG_I2C_SIS96X is not set
# CONFIG_I2C_VIA is not set
CONFIG_I2C_VIAPRO=y
# CONFIG_I2C_VOODOO3 is not set
# CONFIG_I2C_PCA_ISA is not set
# CONFIG_I2C_OCORES is not set

#
# Miscellaneous I2C Chip support
#
# CONFIG_SENSORS_DS1337 is not set
# CONFIG_SENSORS_DS1374 is not set
# CONFIG_SENSORS_EEPROM is not set
# CONFIG_SENSORS_PCF8574 is not set
# CONFIG_SENSORS_PCA9539 is not set
# CONFIG_SENSORS_PCF8591 is not set
# CONFIG_SENSORS_MAX6875 is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_I2C_DEBUG_BUS is not set
# CONFIG_I2C_DEBUG_CHIP is not set

#
# SPI support
#
# CONFIG_SPI is not set
# CONFIG_SPI_MASTER is not set

#
# Dallas's 1-wire bus
#

#
# Hardware Monitoring support
#
CONFIG_HWMON=y
CONFIG_HWMON_VID=y
# CONFIG_SENSORS_ADM1021 is not set
# CONFIG_SENSORS_ADM1025 is not set
# CONFIG_SENSORS_ADM1026 is not set
# CONFIG_SENSORS_ADM1031 is not set
# CONFIG_SENSORS_ADM9240 is not set
CONFIG_SENSORS_ASB100=y
# CONFIG_SENSORS_ATXP1 is not set
# CONFIG_SENSORS_DS1621 is not set
# CONFIG_SENSORS_F71805F is not set
# CONFIG_SENSORS_FSCHER is not set
# CONFIG_SENSORS_FSCPOS is not set
# CONFIG_SENSORS_GL518SM is not set
# CONFIG_SENSORS_GL520SM is not set
# CONFIG_SENSORS_IT87 is not set
# CONFIG_SENSORS_LM63 is not set
# CONFIG_SENSORS_LM75 is not set
# CONFIG_SENSORS_LM77 is not set
# CONFIG_SENSORS_LM78 is not set
# CONFIG_SENSORS_LM80 is not set
# CONFIG_SENSORS_LM83 is not set
# CONFIG_SENSORS_LM85 is not set
# CONFIG_SENSORS_LM87 is not set
# CONFIG_SENSORS_LM90 is not set
# CONFIG_SENSORS_LM92 is not set
# CONFIG_SENSORS_MAX1619 is not set
# CONFIG_SENSORS_PC87360 is not set
# CONFIG_SENSORS_SIS5595 is not set
# CONFIG_SENSORS_SMSC47M1 is not set
# CONFIG_SENSORS_SMSC47M192 is not set
# CONFIG_SENSORS_SMSC47B397 is not set
# CONFIG_SENSORS_VIA686A is not set
# CONFIG_SENSORS_VT8231 is not set
# CONFIG_SENSORS_W83781D is not set
# CONFIG_SENSORS_W83791D is not set
# CONFIG_SENSORS_W83792D is not set
# CONFIG_SENSORS_W83L785TS is not set
# CONFIG_SENSORS_W83627HF is not set
# CONFIG_SENSORS_W83627EHF is not set
# CONFIG_SENSORS_HDAPS is not set
# CONFIG_HWMON_DEBUG_CHIP is not set

#
# Misc devices
#
# CONFIG_IBM_ASM is not set

#
# Multimedia devices
#
# CONFIG_VIDEO_DEV is not set
CONFIG_VIDEO_V4L2=y

#
# Digital Video Broadcasting Devices
#
# CONFIG_DVB is not set
# CONFIG_USB_DABUSB is not set

#
# Graphics support
#
CONFIG_FIRMWARE_EDID=y
CONFIG_FB=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_MACMODES is not set
# CONFIG_FB_BACKLIGHT is not set
CONFIG_FB_MODE_HELPERS=y
# CONFIG_FB_TILEBLITTING is not set
# CONFIG_FB_CIRRUS is not set
# CONFIG_FB_PM2 is not set
# CONFIG_FB_CYBER2000 is not set
# CONFIG_FB_ARC is not set
# CONFIG_FB_ASILIANT is not set
# CONFIG_FB_IMSTT is not set
# CONFIG_FB_VGA16 is not set
# CONFIG_FB_VESA is not set
# CONFIG_FB_HGA is not set
# CONFIG_FB_S1D13XXX is not set
# CONFIG_FB_NVIDIA is not set
# CONFIG_FB_RIVA is not set
# CONFIG_FB_I810 is not set
# CONFIG_FB_INTEL is not set
# CONFIG_FB_MATROX is not set
CONFIG_FB_RADEON=y
CONFIG_FB_RADEON_I2C=y
# CONFIG_FB_RADEON_DEBUG is not set
# CONFIG_FB_ATY128 is not set
# CONFIG_FB_ATY is not set
# CONFIG_FB_SAVAGE is not set
# CONFIG_FB_SIS is not set
# CONFIG_FB_NEOMAGIC is not set
# CONFIG_FB_KYRO is not set
# CONFIG_FB_3DFX is not set
# CONFIG_FB_VOODOO1 is not set
# CONFIG_FB_CYBLA is not set
# CONFIG_FB_TRIDENT is not set
# CONFIG_FB_GEODE is not set
# CONFIG_FB_VIRTUAL is not set

#
# Console display driver support
#
CONFIG_VGA_CONSOLE=y
# CONFIG_VGACON_SOFT_SCROLLBACK is not set
# CONFIG_VIDEO_SELECT is not set
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FONTS=y
# CONFIG_FONT_8x8 is not set
CONFIG_FONT_8x16=y
# CONFIG_FONT_6x11 is not set
# CONFIG_FONT_7x14 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_ACORN_8x8 is not set
# CONFIG_FONT_MINI_4x6 is not set
# CONFIG_FONT_SUN8x16 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_10x18 is not set

#
# Logo configuration
#
# CONFIG_LOGO is not set
# CONFIG_BACKLIGHT_LCD_SUPPORT is not set

#
# Sound
#
CONFIG_SOUND=y

#
# Advanced Linux Sound Architecture
#
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
CONFIG_SND_HWDEP=y
CONFIG_SND_RAWMIDI=y
CONFIG_SND_SEQUENCER=y
# CONFIG_SND_SEQ_DUMMY is not set
CONFIG_SND_OSSEMUL=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
CONFIG_SND_PCM_OSS_PLUGINS=y
CONFIG_SND_SEQUENCER_OSS=y
CONFIG_SND_RTCTIMER=y
CONFIG_SND_SEQ_RTCTIMER_DEFAULT=y
# CONFIG_SND_DYNAMIC_MINORS is not set
# CONFIG_SND_SUPPORT_OLD_API is not set
CONFIG_SND_VERBOSE_PROCFS=y
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set

#
# Generic devices
#
CONFIG_SND_MPU401_UART=y
CONFIG_SND_OPL3_LIB=y
# CONFIG_SND_DUMMY is not set
# CONFIG_SND_VIRMIDI is not set
# CONFIG_SND_MTPAV is not set
# CONFIG_SND_SERIAL_U16550 is not set
# CONFIG_SND_MPU401 is not set

#
# PCI devices
#
# CONFIG_SND_AD1889 is not set
# CONFIG_SND_ALS300 is not set
# CONFIG_SND_ALS4000 is not set
# CONFIG_SND_ALI5451 is not set
# CONFIG_SND_ATIIXP is not set
# CONFIG_SND_ATIIXP_MODEM is not set
# CONFIG_SND_AU8810 is not set
# CONFIG_SND_AU8820 is not set
# CONFIG_SND_AU8830 is not set
# CONFIG_SND_AZT3328 is not set
# CONFIG_SND_BT87X is not set
# CONFIG_SND_CA0106 is not set
CONFIG_SND_CMIPCI=y
# CONFIG_SND_CS4281 is not set
# CONFIG_SND_CS46XX is not set
# CONFIG_SND_CS5535AUDIO is not set
# CONFIG_SND_EMU10K1 is not set
# CONFIG_SND_EMU10K1X is not set
# CONFIG_SND_ENS1370 is not set
# CONFIG_SND_ENS1371 is not set
# CONFIG_SND_ES1938 is not set
# CONFIG_SND_ES1968 is not set
# CONFIG_SND_FM801 is not set
# CONFIG_SND_HDA_INTEL is not set
# CONFIG_SND_HDSP is not set
# CONFIG_SND_HDSPM is not set
# CONFIG_SND_ICE1712 is not set
# CONFIG_SND_ICE1724 is not set
# CONFIG_SND_INTEL8X0 is not set
# CONFIG_SND_INTEL8X0M is not set
# CONFIG_SND_KORG1212 is not set
# CONFIG_SND_MAESTRO3 is not set
# CONFIG_SND_MIXART is not set
# CONFIG_SND_NM256 is not set
# CONFIG_SND_PCXHR is not set
# CONFIG_SND_RME32 is not set
# CONFIG_SND_RME96 is not set
# CONFIG_SND_RME9652 is not set
# CONFIG_SND_SONICVIBES is not set
# CONFIG_SND_TRIDENT is not set
# CONFIG_SND_VIA82XX is not set
# CONFIG_SND_VIA82XX_MODEM is not set
# CONFIG_SND_VX222 is not set
# CONFIG_SND_YMFPCI is not set

#
# USB devices
#
# CONFIG_SND_USB_AUDIO is not set
# CONFIG_SND_USB_USX2Y is not set

#
# Open Sound System
#
# CONFIG_SOUND_PRIME is not set

#
# USB support
#
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
CONFIG_USB_ARCH_HAS_EHCI=y
CONFIG_USB=y
# CONFIG_USB_DEBUG is not set

#
# Miscellaneous USB options
#
CONFIG_USB_DEVICEFS=y
# CONFIG_USB_BANDWIDTH is not set
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_OTG is not set

#
# USB Host Controller Drivers
#
CONFIG_USB_EHCI_HCD=y
# CONFIG_USB_EHCI_SPLIT_ISO is not set
# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_OHCI_HCD is not set
CONFIG_USB_UHCI_HCD=y
# CONFIG_USB_SL811_HCD is not set

#
# USB Device Class drivers
#
# CONFIG_USB_ACM is not set
CONFIG_USB_PRINTER=y

#
# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
#

#
# may also be needed; see USB_STORAGE Help for more information
#
CONFIG_USB_STORAGE=y
# CONFIG_USB_STORAGE_DEBUG is not set
# CONFIG_USB_STORAGE_DATAFAB is not set
# CONFIG_USB_STORAGE_FREECOM is not set
# CONFIG_USB_STORAGE_ISD200 is not set
# CONFIG_USB_STORAGE_DPCM is not set
# CONFIG_USB_STORAGE_USBAT is not set
# CONFIG_USB_STORAGE_SDDR09 is not set
# CONFIG_USB_STORAGE_SDDR55 is not set
# CONFIG_USB_STORAGE_JUMPSHOT is not set
# CONFIG_USB_STORAGE_ALAUDA is not set
# CONFIG_USB_LIBUSUAL is not set

#
# USB Input Devices
#
CONFIG_USB_HID=y
CONFIG_USB_HIDINPUT=y
# CONFIG_USB_HIDINPUT_POWERBOOK is not set
# CONFIG_USB_HIDDEV is not set
# CONFIG_USB_AIPTEK is not set
# CONFIG_USB_WACOM is not set
# CONFIG_USB_ACECAD is not set
# CONFIG_USB_KBTAB is not set
# CONFIG_USB_POWERMATE is not set
# CONFIG_USB_TOUCHSCREEN is not set
# CONFIG_USB_YEALINK is not set
# CONFIG_USB_XPAD is not set
# CONFIG_USB_ATI_REMOTE is not set
# CONFIG_USB_ATI_REMOTE2 is not set
# CONFIG_USB_KEYSPAN_REMOTE is not set
# CONFIG_USB_APPLETOUCH is not set

#
# USB Imaging devices
#
# CONFIG_USB_MDC800 is not set
# CONFIG_USB_MICROTEK is not set

#
# USB Network Adapters
#
# CONFIG_USB_CATC is not set
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
# CONFIG_USB_USBNET is not set
# CONFIG_USB_MON is not set

#
# USB port drivers
#

#
# USB Serial Converter support
#
# CONFIG_USB_SERIAL is not set

#
# USB Miscellaneous drivers
#
# CONFIG_USB_EMI62 is not set
# CONFIG_USB_EMI26 is not set
# CONFIG_USB_AUERSWALD is not set
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_LEGOTOWER is not set
# CONFIG_USB_LCD is not set
# CONFIG_USB_LED is not set
# CONFIG_USB_CYTHERM is not set
# CONFIG_USB_GOTEMP is not set
# CONFIG_USB_PHIDGETKIT is not set
# CONFIG_USB_PHIDGETSERVO is not set
# CONFIG_USB_IDMOUSE is not set
# CONFIG_USB_APPLEDISPLAY is not set
# CONFIG_USB_SISUSBVGA is not set
# CONFIG_USB_LD is not set
# CONFIG_USB_TEST is not set

#
# USB DSL modem support
#

#
# USB Gadget Support
#
# CONFIG_USB_GADGET is not set

#
# MMC/SD Card support
#
# CONFIG_MMC is not set

#
# LED devices
#
# CONFIG_NEW_LEDS is not set

#
# LED drivers
#

#
# LED Triggers
#

#
# InfiniBand support
#
# CONFIG_INFINIBAND is not set

#
# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
#
# CONFIG_EDAC is not set

#
# Real Time Clock
#
# CONFIG_RTC_CLASS is not set

#
# DMA Engine support
#
# CONFIG_DMA_ENGINE is not set

#
# DMA Clients
#

#
# DMA Devices
#

#
# File systems
#
# CONFIG_EXT2_FS is not set
# CONFIG_EXT3_FS is not set
CONFIG_REISER4_FS=y
# CONFIG_REISER4_DEBUG is not set
CONFIG_REISERFS_FS=y
# CONFIG_REISERFS_CHECK is not set
# CONFIG_REISERFS_PROC_INFO is not set
# CONFIG_REISERFS_FS_XATTR is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_ROMFS_FS is not set
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
CONFIG_DNOTIFY=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_FUSE_FS is not set

#
# CD-ROM/DVD Filesystems
#
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
# CONFIG_ZISOFS is not set
CONFIG_UDF_FS=y
CONFIG_UDF_NLS=y

#
# DOS/FAT/NT Filesystems
#
CONFIG_FAT_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
# CONFIG_NTFS_FS is not set

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
# CONFIG_PROC_KCORE is not set
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_HUGETLBFS is not set
# CONFIG_HUGETLB_PAGE is not set
CONFIG_RAMFS=y
# CONFIG_CONFIGFS_FS is not set

#
# Miscellaneous filesystems
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set

#
# Network File Systems
#
# CONFIG_NFS_FS is not set
# CONFIG_NFSD is not set
# CONFIG_SMB_FS is not set
CONFIG_CIFS=y
# CONFIG_CIFS_STATS is not set
# CONFIG_CIFS_WEAK_PW_HASH is not set
# CONFIG_CIFS_XATTR is not set
# CONFIG_CIFS_DEBUG2 is not set
# CONFIG_CIFS_EXPERIMENTAL is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set
# CONFIG_9P_FS is not set

#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y

#
# Native Language Support
#
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
CONFIG_NLS_CODEPAGE_437=y
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
# CONFIG_NLS_CODEPAGE_850 is not set
# CONFIG_NLS_CODEPAGE_852 is not set
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_861 is not set
# CONFIG_NLS_CODEPAGE_862 is not set
# CONFIG_NLS_CODEPAGE_863 is not set
# CONFIG_NLS_CODEPAGE_864 is not set
# CONFIG_NLS_CODEPAGE_865 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_936 is not set
# CONFIG_NLS_CODEPAGE_950 is not set
# CONFIG_NLS_CODEPAGE_932 is not set
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_6 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
CONFIG_NLS_UTF8=y

#
# Distributed Lock Manager
#

#
# Instrumentation Support
#
# CONFIG_PROFILING is not set

#
# Kernel hacking
#
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
# CONFIG_PRINTK_TIME is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_UNUSED_SYMBOLS=y
# CONFIG_DEBUG_SHIRQ is not set
CONFIG_DEBUG_KERNEL=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_DETECT_SOFTLOCKUP=y
# CONFIG_SCHEDSTATS is not set
CONFIG_DEBUG_SLAB=y
CONFIG_DEBUG_SLAB_LEAK=y
CONFIG_DEBUG_PREEMPT=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_MUTEX_ALLOC=y
CONFIG_DEBUG_MUTEX_DEADLOCKS=y
CONFIG_DEBUG_RT_MUTEXES=y
CONFIG_DEBUG_PI_LIST=y
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_PROVE_SPIN_LOCKING is not set
# CONFIG_PROVE_RW_LOCKING is not set
# CONFIG_PROVE_MUTEX_LOCKING is not set
# CONFIG_PROVE_RWSEM_LOCKING is not set
CONFIG_DEBUG_SPINLOCK_SLEEP=y
CONFIG_DEBUG_LOCKING_API_SELFTESTS=y
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_PAGE_OWNER is not set
CONFIG_DEBUG_FS=y
# CONFIG_DEBUG_VM is not set
# CONFIG_FRAME_POINTER is not set
# CONFIG_UNWIND_INFO is not set
CONFIG_FORCED_INLINING=y
# CONFIG_DEBUG_SYNCHRO_TEST is not set
# CONFIG_RCU_TORTURE_TEST is not set
# CONFIG_PROFILE_LIKELY is not set
# CONFIG_WANT_EXTRA_DEBUG_INFORMATION is not set
# CONFIG_KGDB is not set
CONFIG_EARLY_PRINTK=y
CONFIG_DEBUG_STACKOVERFLOW=y
# CONFIG_DEBUG_STACK_USAGE is not set
CONFIG_STACK_BACKTRACE_COLS=1
# CONFIG_DEBUG_PAGEALLOC is not set
CONFIG_DEBUG_RODATA=y
CONFIG_4KSTACKS=y
CONFIG_X86_FIND_SMP_CONFIG=y
CONFIG_X86_MPPARSE=y
CONFIG_DOUBLEFAULT=y

#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set

#
# Cryptographic options
#
# CONFIG_CRYPTO is not set

#
# Hardware crypto devices
#

#
# Library routines
#
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
CONFIG_CRC32=y
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_PLIST=y
CONFIG_GENERIC_HARDIRQS=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_X86_BIOS_REBOOT=y
CONFIG_KTIME_SCALAR=y

[-- Attachment #3: tarpit --]
[-- Type: application/x-shellscript, Size: 1889 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-06-01 13:43                                 ` Andrew James Wade
@ 2006-06-01 14:53                                   ` Patrick McHardy
  2006-06-02 21:32                                     ` Andrew James Wade
  0 siblings, 1 reply; 32+ messages in thread
From: Patrick McHardy @ 2006-06-01 14:53 UTC (permalink / raw)
  To: ajwade
  Cc: Andrew Morton, Stephen Frost, Amin Azez, David S. Miller,
	netfilter-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 303 bytes --]

[CC-list trimmed]

Andrew James Wade wrote:
> Hello Mr. McHardy,
> 
> The BUG below appears to be related to your ipt_recent rewrite. I
> haven't tracked it down further yet. I've attached the (toy) firewall
> script that's triggering the bug.

Yes, that was my fault. These two patches should fix it.


[-- Attachment #2: 01.diff --]
[-- Type: text/x-patch, Size: 2421 bytes --]

[NETFILTER]: recent match: fix "sleeping function called from invalid context"

create_proc_entry must not be called with locks held. Use a mutex
instead to protect data only changed in user context.

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit 6812aa38ac35d6e819058d2273a6a7de091a2604
tree 140fea396f29415d7981fcfbc5980f5c0b2d91a7
parent a0447a3000b5d4e926928493def9d62aae3b87ed
author Patrick McHardy <kaber@trash.net> Thu, 01 Jun 2006 16:40:39 +0200
committer Patrick McHardy <kaber@trash.net> Thu, 01 Jun 2006 16:40:39 +0200

 net/ipv4/netfilter/ipt_recent.c |   15 ++++++++++-----
 1 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
index 9686c4d..9b09e48 100644
--- a/net/ipv4/netfilter/ipt_recent.c
+++ b/net/ipv4/netfilter/ipt_recent.c
@@ -69,6 +69,7 @@ #endif
 
 static LIST_HEAD(tables);
 static DEFINE_SPINLOCK(recent_lock);
+static DEFINE_MUTEX(recent_mutex);
 
 #ifdef CONFIG_PROC_FS
 static struct proc_dir_entry	*proc_dir;
@@ -249,7 +250,7 @@ ipt_recent_checkentry(const char *tablen
 	    strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN)
 		return 0;
 
-	spin_lock_bh(&recent_lock);
+	mutex_lock(&recent_mutex);
 	t = recent_table_lookup(info->name);
 	if (t != NULL) {
 		t->refcnt++;
@@ -258,7 +259,7 @@ ipt_recent_checkentry(const char *tablen
 	}
 
 	t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
-		    GFP_ATOMIC);
+		    GFP_KERNEL);
 	if (t == NULL)
 		goto out;
 	strcpy(t->name, info->name);
@@ -274,10 +275,12 @@ #ifdef CONFIG_PROC_FS
 	t->proc->proc_fops = &recent_fops;
 	t->proc->data      = t;
 #endif
+	spin_lock_bh(&recent_lock);
 	list_add_tail(&t->list, &tables);
+	spin_unlock_bh(&recent_lock);
 	ret = 1;
 out:
-	spin_unlock_bh(&recent_lock);
+	mutex_unlock(&recent_mutex);
 	return ret;
 }
 
@@ -288,17 +291,19 @@ ipt_recent_destroy(const struct xt_match
 	const struct ipt_recent_info *info = matchinfo;
 	struct recent_table *t;
 
-	spin_lock_bh(&recent_lock);
+	mutex_lock(&recent_mutex);
 	t = recent_table_lookup(info->name);
 	if (--t->refcnt == 0) {
+		spin_lock_bh(&recent_lock);
 		list_del(&t->list);
+		spin_unlock_bh(&recent_lock);
 		recent_table_flush(t);
 #ifdef CONFIG_PROC_FS
 		remove_proc_entry(t->name, proc_dir);
 #endif
 		kfree(t);
 	}
-	spin_unlock_bh(&recent_lock);
+	mutex_unlock(&recent_mutex);
 }
 
 #ifdef CONFIG_PROC_FS

[-- Attachment #3: 02.diff --]
[-- Type: text/x-patch, Size: 915 bytes --]

[NETFILTER]: recent match: missing refcnt initialization

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit 10263005af5814396b8263c1c2a4367d49548e13
tree a73003fe82e7b4546359d86f684b90b78a6aa504
parent 6812aa38ac35d6e819058d2273a6a7de091a2604
author Patrick McHardy <kaber@trash.net> Thu, 01 Jun 2006 16:49:58 +0200
committer Patrick McHardy <kaber@trash.net> Thu, 01 Jun 2006 16:49:58 +0200

 net/ipv4/netfilter/ipt_recent.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
index 9b09e48..61a2139 100644
--- a/net/ipv4/netfilter/ipt_recent.c
+++ b/net/ipv4/netfilter/ipt_recent.c
@@ -262,6 +262,7 @@ ipt_recent_checkentry(const char *tablen
 		    GFP_KERNEL);
 	if (t == NULL)
 		goto out;
+	t->refcnt = 1;
 	strcpy(t->name, info->name);
 	INIT_LIST_HEAD(&t->lru_list);
 	for (i = 0; i < ip_list_hash_size; i++)

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH] fix mem-leak in netfilter
  2006-06-01 14:53                                   ` Patrick McHardy
@ 2006-06-02 21:32                                     ` Andrew James Wade
  0 siblings, 0 replies; 32+ messages in thread
From: Andrew James Wade @ 2006-06-02 21:32 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Andrew Morton, Stephen Frost, Amin Azez, David S. Miller,
	netfilter-devel, linux-kernel

On Thursday 01 June 2006 10:53, Patrick McHardy wrote:
> [CC-list trimmed]
> 
> Andrew James Wade wrote:
> > Hello Mr. McHardy,
> > 
> > The BUG below appears to be related to your ipt_recent rewrite. I
> > haven't tracked it down further yet. I've attached the (toy) firewall
> > script that's triggering the bug.
> 
> Yes, that was my fault. These two patches should fix it.
> 
> 
That fixed it, thanks.

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2006-06-02 21:32 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-05-07  2:26 [PATCH] fix mem-leak in netfilter Jesper Juhl
2006-05-07  9:36 ` Willy Tarreau
2006-05-07 22:42   ` Grant Coady
2006-05-08  5:07     ` Willy Tarreau
2006-05-08  5:43       ` David S. Miller
2006-05-08  8:36         ` Amin Azez
2006-05-08  9:08           ` Juergen Kreileder
2006-05-12  7:40         ` Patrick McHardy
2006-05-12 11:09           ` Jesper Juhl
2006-05-12 11:33             ` Patrick McHardy
2006-05-12 12:13               ` Jesper Juhl
2006-05-12 12:40                 ` Willy Tarreau
2006-05-12 12:49                   ` Patrick McHardy
     [not found]               ` <446490BB.10801@ufomechanic.net>
2006-05-15  8:25                 ` Patrick McHardy
2006-05-15 14:28                   ` Stephen Frost
2006-05-15 18:49                     ` Patrick McHardy
2006-05-15 19:27                       ` Stephen Frost
2006-05-15 20:09                         ` Patrick McHardy
2006-05-15 20:41                           ` Stephen Frost
2006-05-15 20:45                             ` Patrick McHardy
2006-05-15 21:03                             ` Stephen Frost
2006-05-17  6:26                               ` Patrick McHardy
2006-05-17  6:59                                 ` David S. Miller
2006-05-17  7:19                                   ` Patrick McHardy
2006-05-17 10:55                                     ` Stephen Frost
2006-05-17  7:09                                 ` David S. Miller
2006-05-17  7:13                                   ` Roland Dreier
2006-05-17  7:19                                   ` Patrick McHardy
2006-05-17 13:14                                 ` Stephen Frost
2006-06-01 13:43                                 ` Andrew James Wade
2006-06-01 14:53                                   ` Patrick McHardy
2006-06-02 21:32                                     ` Andrew James Wade

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).