From: Amin Azez <azez@ufomechanic.net>
To: Patrick McHardy <kaber@trash.net>
Cc: netfilter-devel@lists.netfilter.org
Subject: ipt_recent patch
Date: Mon, 13 Mar 2006 17:47:05 +0000 [thread overview]
Message-ID: <4415B019.6050409@ufomechanic.net> (raw)
In-Reply-To: <440ECB1B.4070507@trash.net>
[-- Attachment #1: Type: text/plain, Size: 3869 bytes --]
This patch has some sort of versioning built in so that the new userland
features are not enabled unless built against a new header file.
If a new kernel module is called with rules by old userland, then it
refuses to accept the rule.
I don't know if you will like my CPP macros, I could expand them out?
Changes:
* /proc/net/ipt_recent/* is sorted by age, most recent first
* include Per Hedelands patch in message
<200603062203.k26M3pI5024778@tordmule.bluetail.com>
* fix problem of not moving up list entries after deleting entries which
meant that sometimes you could never fill the list
* fix problems of items with hash of 0 being erased whenever an empty
slot is used
* Add these new features:
[!] --listcount-lt count
Requires as a precondition that the number of IP entries
in the
list (subject to the optional --listtime-* specifier) is
less
than count (or not !). No other options are considered
if this
is not true.
[!] --listcount-le count
Requires as a precondition that the number of IP entries
in the
list (subject to the optional --listtime-* specifier)
is less
than or equal to count (or not !). No other options are
consid-
ered if this is not true.
[!] --listcount-eq count
Requires as a precondition that the number of IP entries
in the
list (subject to the optional --listtime-* specifier) is
equal
to (or not !) count. No other options are considered if
this is
not true.
[!] --listcount-ge count
Requires as a precondition that the number of IP entries
in the
list (subject to the optional --listtime-* specifier) is
greater
than or equal to count (or not !). No other options are
consid-
ered if this is not true.
[!] --listcount-gt count
Requires as a precondition that the number of IP entries
in the
list (subject to the optional --listtime-* specifier) is
greater
than count (or not !). No other options are considered
if this
is not true.
Only one --listcount-* option can be specified.
[!] --listtime-lt seconds
Affects the --listcount-* so that instead of counting the
number
of items in the list, it counts the number of items
that were
last updated less than seconds seconds ago.
[!] --listtime-le seconds
Affects the --listcount-* so that instead of counting the
number
of items in the list, it counts the number of items
that were
last updated less than or equal to seconds seconds ago.
[!] --listtime-ge seconds
Affects the --listcount-* so that instead of counting the
number
of items in the list, it counts the number of items
that were
last updated more than or equal to seconds seconds ago.
[!] --listtime-gt seconds
Affects the --listcount-* so that instead of counting the
number
of items in the list, it counts the number of items
that were
last updated more than seconds seconds ago.
Only one --listtime-* option can be specified. --listtime-*
options act
as select clauses for what to count. The ! negation for
--list-
time-* options merely inverts the comparison, so !
--listime-le
is the same as --listtime-gt
...
The next example accepts the packet if less than 5 ip
addresses
in the list have been updated in the last 60 seconds
# iptables -A FORWARD -m recent --listcount-lt 5
--listtime-lt
60 --set -j ACCEPT
[-- Attachment #2: iptables.recent.patch --]
[-- Type: text/x-patch, Size: 15440 bytes --]
--- extensions/Makefile.orig 2005-07-13 15:25:35.000000000 +0100
+++ extensions/Makefile 2005-07-13 15:25:54.000000000 +0100
@@ -6,8 +6,9 @@
#
PF_EXT_SLIB:=ah addrtype comment connlimit connmark conntrack dscp ecn esp hashlimit helper icmp iprange length limit mac mark multiport owner physdev pkttype realm rpc sctp standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NFQUEUE NOTRACK REDIRECT REJECT SAME SNAT TARPIT TCPMSS TOS TRACE TTL ULOG layer7
PF_EXT_SLIB+=connrate ROUTE time account quota
PF_EXT_SLIB+=condition
+PF_EXT_SLIB+=recent
PF_EXT_SLIB+=vlan
PF6_EXT_SLIB:=eui64 hl icmpv6 length limit mac mark multiport owner physdev standard tcp udp HL LOG NFQUEUE MARK TRACE
# Optionals
--- extensions/libipt_recent.man.orig 2006-03-01 12:39:29.000000000 +0000
+++ extensions/libipt_recent.man 2006-03-02 10:40:53.000000000 +0000
@@ -9,6 +9,63 @@
Specify the list to use for the commands. If no name is given then 'DEFAULT'
will be used.
.TP
+[\fB!\fR] \fB--listcount-lt \ficount\fR
+Requires as a precondition that the number of IP entries in the list
+(subject to the optional --listtime-* specifier)
+is less than count (or not !). No other options are considered if
+this is not true.
+.TP
+[\fB!\fR] \fB--listcount-le \ficount\fR
+Requires as a precondition that the number of IP entries in the list
+(subject to the optional --listtime-* specifier)
+is less than or equal to count (or not !). No other options are considered if
+this is not true.
+.TP
+[\fB!\fR] \fB--listcount-eq \ficount\fR
+Requires as a precondition that the number of IP entries in the list
+(subject to the optional --listtime-* specifier)
+is equal to (or not !) count. No other options are considered if
+this is not true.
+.TP
+[\fB!\fR] \fB--listcount-ge \ficount\fR
+Requires as a precondition that the number of IP entries in the list
+(subject to the optional --listtime-* specifier)
+is greater than or equal to count (or not !). No other options are considered if
+this is not true.
+.TP
+[\fB!\fR] \fB--listcount-gt \ficount\fR
+Requires as a precondition that the number of IP entries in the list
+(subject to the optional --listtime-* specifier)
+is greater than count (or not !). No other options are considered if
+this is not true.
+.TP
+Only one --listcount-* option can be specified.
+.TP
+[\fB!\fR] \fB--listtime-lt \fisecounds\fR
+Affects the --listcount-* so that instead of counting the number of
+items in the list, it counts the number of items that were last updated
+less than \fBseconds\fR seconds ago.
+.TP
+[\fB!\fR] \fB--listtime-le \fisecounds\fR
+Affects the --listcount-* so that instead of counting the number of
+items in the list, it counts the number of items that were last updated
+less than or equal to \fBseconds\fR seconds ago.
+.TP
+[\fB!\fR] \fB--listtime-ge \fisecounds\fR
+Affects the --listcount-* so that instead of counting the number of
+items in the list, it counts the number of items that were last updated
+more than or equal to \fBseconds\fR seconds ago.
+.TP
+[\fB!\fR] \fB--listtime-gt \fisecounds\fR
+Affects the --listcount-* so that instead of counting the number of
+items in the list, it counts the number of items that were last updated
+more than \fBseconds\fR seconds ago.
+.TP
+Only one --listtime-* option can be specified. --listtime-* options act
+as select clauses for what to count. The ! negation for --listtime-*
+options merely inverts the comparison, so \fB! --listime-le\fR is the same as
+\fB--listtime-gt\fR
+.TP
[\fB!\fR] \fB--set\fR
This will add the source address of the packet to the list. If the
source address is already in the list, this will update the existing
@@ -56,6 +113,12 @@
# iptables -A FORWARD -m recent --name badguy --rcheck --seconds 60 -j DROP
# iptables -A FORWARD -p tcp -i eth0 --dport 139 -m recent --name badguy --set -j DROP
+
+The next example accepts the packet if less than 5 ip addresses in the list have
+been updated in the last 60 seconds
+
+# iptables -A FORWARD -m recent --listcount-lt 5 --listtime-lt 60 --set -j ACCEPT
+
.P
Official website (http://snowman.net/projects/ipt_recent/) also has
some examples of usage.
--- extensions/libipt_recent.c.orig 2006-03-13 17:02:03.000000000 +0000
+++ extensions/libipt_recent.c 2006-03-13 17:19:25.000000000 +0000
@@ -23,17 +23,28 @@
/* Options for this module */
static struct option opts[] = {
- { .name = "set", .has_arg = 0, .flag = 0, .val = 201 },
- { .name = "rcheck", .has_arg = 0, .flag = 0, .val = 202 },
- { .name = "update", .has_arg = 0, .flag = 0, .val = 203 },
- { .name = "seconds", .has_arg = 1, .flag = 0, .val = 204 },
- { .name = "hitcount", .has_arg = 1, .flag = 0, .val = 205 },
- { .name = "remove", .has_arg = 0, .flag = 0, .val = 206 },
- { .name = "rttl", .has_arg = 0, .flag = 0, .val = 207 },
- { .name = "name", .has_arg = 1, .flag = 0, .val = 208 },
- { .name = "rsource", .has_arg = 0, .flag = 0, .val = 209 },
- { .name = "rdest", .has_arg = 0, .flag = 0, .val = 210 },
- { .name = 0, .has_arg = 0, .flag = 0, .val = 0 }
+#ifdef IPT_RECENT_HAS_LIST_OPS
+ { .name = "set", .has_arg = 0, .flag = 0, .val = 201 },
+ { .name = "rcheck", .has_arg = 0, .flag = 0, .val = 202 },
+ { .name = "update", .has_arg = 0, .flag = 0, .val = 203 },
+ { .name = "seconds", .has_arg = 1, .flag = 0, .val = 204 },
+ { .name = "hitcount", .has_arg = 1, .flag = 0, .val = 205 },
+ { .name = "remove", .has_arg = 0, .flag = 0, .val = 206 },
+ { .name = "rttl", .has_arg = 0, .flag = 0, .val = 207 },
+ { .name = "name", .has_arg = 1, .flag = 0, .val = 208 },
+ { .name = "rsource", .has_arg = 0, .flag = 0, .val = 209 },
+ { .name = "rdest", .has_arg = 0, .flag = 0, .val = 210 },
+ { .name = "listcount-lt", .has_arg = 1, .flag = 0, .val = 211 },
+ { .name = "listcount-le", .has_arg = 1, .flag = 0, .val = 212 },
+ { .name = "listcount-eq", .has_arg = 1, .flag = 0, .val = 213 },
+ { .name = "listcount-ge", .has_arg = 1, .flag = 0, .val = 214 },
+ { .name = "listcount-gt", .has_arg = 1, .flag = 0, .val = 215 },
+ { .name = "listtime-lt", .has_arg = 1, .flag = 0, .val = 216 },
+ { .name = "listtime-le", .has_arg = 1, .flag = 0, .val = 217 },
+ { .name = "listtime-ge", .has_arg = 1, .flag = 0, .val = 218 },
+ { .name = "listtime-gt", .has_arg = 1, .flag = 0, .val = 219 },
+#endif
+ { .name = 0, .has_arg = 0, .flag = 0, .val = 0 }
};
/* Function which prints out usage message. */
@@ -42,6 +53,20 @@
{
printf(
"recent v%s options:\n"
+#ifdef RECENT_HAS_LIST_OPS
+" PRECONDITIONS Only one precondition may be set.\n"
+"[!] --listcount-lt n Only match if there are less than n items in the table\n"
+"[!] --listcount-le n Only match if there are n items or less in the table\n"
+"[!] --listcount-eq n Only match if there are n items in the table\n"
+"[!] --listcount-ge n Only match if there are n items or more in the table\n"
+"[!] --listcount-gt n Only match if there are more than n items in the table\n"
+
+"[!] --listtimes-lt n --listcount option only applies to items less than n seconds old\n"
+"[!] --listtimes-le n --listcount option only applies to items n seconds old or less\n"
+"[!] --listtimes-ge n --listcount option only applies to items n seconds old or more\n"
+"[!] --listtimes-gt n --listcount option only applies ti items more than n seconds old\n"
+#endif
+" The precondtion may be followed by:\n"
"[!] --set Add source address to list, always matches.\n"
"[!] --rcheck Match if source address in list.\n"
"[!] --update Match if source address in list, also update last-seen time.\n"
@@ -78,6 +103,11 @@
* better be safe, than sorry */
info->name[IPT_RECENT_NAME_LEN-1] = '\0';
info->side = IPT_RECENT_SOURCE;
+/* If kernel side supports LIST_OPS but we don't set this flag it will
+ presume we are an old client side and refuse the rule */
+#ifdef IPT_RECENT_HAS_LIST_OPS
+ info->check_set |= IPT_RECENT_HAS_LIST_OPS;
+#endif
}
/* Function which parses command options; returns true if it
@@ -155,6 +185,89 @@
info->side = IPT_RECENT_DEST;
break;
+#ifdef IPT_RECENT_HAS_LIST_OPS
+ case 211: /* listcount-lt */
+ if (info->count_ops & IPT_RECENT_COUNT_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listcount-lt', `--listcount-le' "
+ "`--listcount-eq', `--listcount-ge' `listcount-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= IPT_RECENT_COUNT_LT | (invert?IPT_RECENT_COUNT_INVERT:0);
+ info->entry_count=atoi(optarg);
+ break;
+
+ case 212: /* listcount-le */
+ if (info->count_ops & IPT_RECENT_COUNT_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listcount-lt', `--listcount-le' "
+ "`--listcount-eq', `--listcount-ge' `listcount-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= IPT_RECENT_COUNT_LE | (invert?IPT_RECENT_COUNT_INVERT:0);
+ info->entry_count=atoi(optarg);
+ break;
+
+ case 213: /* listcount-eq */
+ if (info->count_ops & IPT_RECENT_COUNT_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listcount-lt', `--listcount-le' "
+ "`--listcount-eq', `--listcount-ge' `listcount-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= IPT_RECENT_COUNT_EQ | (invert?IPT_RECENT_COUNT_INVERT:0);
+ info->entry_count=atoi(optarg);
+ break;
+
+ case 214: /* listcount-ge */
+ if (info->count_ops & IPT_RECENT_COUNT_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listcount-lt', `--listcount-le' "
+ "`--listcount-eq', `--listcount-ge' `listcount-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= IPT_RECENT_COUNT_GE | (invert?IPT_RECENT_COUNT_INVERT:0);
+ info->entry_count=atoi(optarg);
+ break;
+
+ case 215: /* listcount-gt */
+ if (info->count_ops & IPT_RECENT_COUNT_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listcount-lt', `--listcount-le' "
+ "`--listcount-eq', `--listcount-ge' `listcount-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= IPT_RECENT_COUNT_GT | (invert?IPT_RECENT_COUNT_INVERT:0);
+ info->entry_count=atoi(optarg);
+ break;
+
+ case 216: /* listtime-lt */
+ if (info->count_ops & IPT_RECENT_TIME_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listtime-lt', `--listtime-le' "
+ "`--listtime-eq', `--listtime-ge' `listtime-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= invert?IPT_RECENT_TIME_GE:IPT_RECENT_TIME_LT;
+ info->entry_time=atoi(optarg);
+ break;
+
+ case 217: /* listtime-le */
+ if (info->count_ops & IPT_RECENT_TIME_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listtime-lt', `--listtime-le' "
+ "`--listtime-eq', `--listtime-ge' `listtime-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= invert?IPT_RECENT_TIME_GT:IPT_RECENT_TIME_LE;
+ info->entry_time=atoi(optarg);
+ break;
+
+ case 218: /* listtime-ge */
+ if (info->count_ops & IPT_RECENT_TIME_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listtime-lt', `--listtime-le' "
+ "`--listtime-eq', `--listtime-ge' `listtime-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= invert?IPT_RECENT_TIME_LT:IPT_RECENT_TIME_GE;
+ info->entry_time=atoi(optarg);
+ break;
+
+ case 219: /* listtime-gt */
+ if (info->count_ops & IPT_RECENT_TIME_OPS) exit_error(PARAMETER_PROBLEM,
+ "recent: only one of `--listtime-lt', `--listtime-le' "
+ "`--listtime-eq', `--listtime-ge' `listtime-gts'` "
+ " or `--eq' may be set");
+ info->count_ops |= invert?IPT_RECENT_TIME_LE:IPT_RECENT_TIME_GT;
+ info->entry_time=atoi(optarg);
+ break;
+#endif
+
default:
return 0;
}
@@ -181,10 +294,35 @@
{
struct ipt_recent_info *info = (struct ipt_recent_info *)match->data;
+#ifdef IPT_RECENT_HAS_LIST_OPS
+ printf("recent: ");
+
+ /* handle the precondition first */
+ if (info->count_ops) {
+ if (info->count_ops & IPT_RECENT_COUNT_INVERT) printf("not ");
+ switch (info->count_ops & IPT_RECENT_COUNT_OPS) {
+ case IPT_RECENT_COUNT_LT: printf("< "); break;
+ case IPT_RECENT_COUNT_LE: printf("<= "); break;
+ case IPT_RECENT_COUNT_EQ: printf("== "); break;
+ case IPT_RECENT_COUNT_GE: printf(">= "); break;
+ case IPT_RECENT_COUNT_GT: printf("> "); break;
+ default: printf(" ");
+ }
+ printf("%d ",info->entry_count);
+ if (info->count_ops & IPT_RECENT_TIME_OPS) switch (info->count_ops & IPT_RECENT_TIME_OPS) {
+ case IPT_RECENT_TIME_LT: printf("< "); break;
+ case IPT_RECENT_TIME_LE: printf("<= "); break;
+ case IPT_RECENT_TIME_GE: printf(">= "); break;
+ case IPT_RECENT_TIME_GT: printf("> "); break;
+ default: printf(" ");
+ }
+ printf("%d ",info->entry_time);
+ }
+#endif
+
if (info->invert)
fputc('!', stdout);
- printf("recent: ");
if(info->check_set & IPT_RECENT_SET) printf("SET ");
if(info->check_set & IPT_RECENT_CHECK) printf("CHECK ");
if(info->check_set & IPT_RECENT_UPDATE) printf("UPDATE ");
@@ -203,6 +341,31 @@
{
struct ipt_recent_info *info = (struct ipt_recent_info *)match->data;
+#ifdef IPT_RECENT_HAS_LIST_OPS
+ /* handle the precondition first */
+ if (info->count_ops) {
+ if (info->count_ops & IPT_RECENT_COUNT_INVERT) printf("! ");
+ switch (info->count_ops & IPT_RECENT_COUNT_OPS) {
+ case IPT_RECENT_COUNT_LT: printf("--listcount-lt "); break;
+ case IPT_RECENT_COUNT_LE: printf("--listcount-le "); break;
+ case IPT_RECENT_COUNT_EQ: printf("--listcount-eq "); break;
+ case IPT_RECENT_COUNT_GE: printf("--listcount-ge "); break;
+ case IPT_RECENT_COUNT_GT: printf("--listcount-gt "); break;
+ default: printf(" ");
+ }
+ printf("%d ",info->entry_count);
+
+ if (info->count_ops & IPT_RECENT_TIME_OPS) switch (info->count_ops & IPT_RECENT_TIME_OPS) {
+ case IPT_RECENT_TIME_LT: printf("--listtime-lt "); break;
+ case IPT_RECENT_TIME_LE: printf("--listtime-le "); break;
+ case IPT_RECENT_TIME_GE: printf("--listtime-ge "); break;
+ case IPT_RECENT_TIME_GT: printf("--listtime-gt "); break;
+ default: printf(" ");
+ }
+ printf("%d ",info->entry_time);
+ }
+#endif
+
if (info->invert)
printf("! ");
[-- Attachment #3: kernel_ipt_recent.patch --]
[-- Type: text/x-patch, Size: 19620 bytes --]
--- net/ipv4/netfilter/ipt_recent.c.nolimit 2006-02-15 16:34:20.000000000 +0000
+++ net/ipv4/netfilter/ipt_recent.c 2006-03-13 17:26:41.000000000 +0000
@@ -1,11 +1,12 @@
/* 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> */
+/* Copyright 2006 ufomechanic.net <azez@ufomechanic.net>
+ for --listcount and --listtime enhancements */
/* 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>
#include <linux/proc_fs.h>
@@ -70,7 +71,11 @@
/* Structure of our linked list of tables of recent lists. */
struct recent_ip_tables {
char name[IPT_RECENT_NAME_LEN];
+ /* number of entries in list *table */
+ int entry_count;
+ /* number of reference to this structure from iptables rules */
int count;
+ /* an index increased with each operation which maps time_info[x].position to a position in table */
int time_pos;
struct recent_ip_list *table;
struct recent_ip_tables *next;
@@ -131,7 +136,7 @@
static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
{
- int len = 0, count, last_len = 0, pkt_count;
+ int len = 0, index, time, count, last_len = 0, pkt_count;
off_t pos = 0;
off_t begin = 0;
struct recent_ip_tables *curr_table;
@@ -139,22 +144,27 @@
curr_table = (struct recent_ip_tables*) data;
spin_lock_bh(&curr_table->list_lock);
+ time=(curr_table->time_pos+ip_list_tot-1) % ip_list_tot;
for(count = 0; count < ip_list_tot; count++) {
- if(!curr_table->table[count].addr) continue;
+ /* The next slot to be re-used is the oldest slot, so start there... */
+ index=curr_table->time_info[time].position;
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(curr_table->table[index].addr) {
+ len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[index].addr));
+ len += sprintf(buffer+len,"ttl: %u ",curr_table->table[index].ttl);
+ len += sprintf(buffer+len,"last_seen: %lu ",curr_table->table[index].last_seen);
+ len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[index].oldest_pkt);
+ len += sprintf(buffer+len,"last_pkts: %lu",curr_table->table[index].last_pkts[0]);
+ for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) {
+ if(!curr_table->table[index].last_pkts[pkt_count]) break;
+ len += sprintf(buffer+len,", %lu",curr_table->table[index].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; }
+ }
+ time=(time-1+ip_list_tot) % ip_list_tot;
}
*start = buffer + (offset - begin);
@@ -216,6 +226,7 @@
used += 5;
spin_lock_bh(&curr_table->list_lock);
curr_table->time_pos = 0;
+ curr_table->entry_count = 0;
for(count = 0; count < ip_list_hash_size; count++) {
curr_table->hash_table[count] = -1;
}
@@ -297,6 +308,9 @@
info->seconds = 0;
info->hit_count = 0;
info->check_set = check_set;
+ info->entry_count = 0;
+ info->entry_time = 0;
+ info->count_ops = 0;
info->invert = 0;
info->side = IPT_RECENT_SOURCE;
strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN);
@@ -350,6 +364,16 @@
* --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
* -- matchinfo->hit_count
* --seconds and --hitcount can be combined
+ * --listcount-lt -- Options to see if how many entries in the table altogether
+ * --listcount-le
+ * --listcount-eq
+ * --listcount-ge
+ * --listcount-gt
+ * --listtime-lt -- change --listcount to refer to how many entries in the table we
+ * updated less than so many seconds ago
+ * --listtime-le
+ * --listtime-ge
+ * --listtime-gt
*/
static int
match(const struct sk_buff *skb,
@@ -403,7 +427,132 @@
/* Make sure no one is changing the list while we work with it */
spin_lock_bh(&curr_table->list_lock);
+ /* Get jiffies now in case they changed while we were waiting for a lock */
+ now = jiffies;
+
r_list = curr_table->table;
+
+ /* We seek: count of items matching time constraint.
+
+ We don't actually want to count the number of items meeting
+ the time constraints because it is a waste of time, we already
+ have an index in time order, we can easily check that there
+ are enough in the index.
+
+ location = time_info[curr_table->time_pos].position;
+ references the next slot to be used, and which therefore is the
+ oldest slot to have been used if it has been used.
+
+ start = time_info[(curr_table->time_pos + ip_list_tot -1) % ip_list_tot].position;
+ refers to the most recent slot to be used if any have been used
+
+ (or if curr_table->entry_count > 0)
+ end = time_info[(curr_table->time_pos + ip_list_tot
+ - curr_table->entry_count) % ip_list_tot ].position;
+ will always show the oldest item if entry_count > 0
+
+ SO, if we want to check that there are n items in the list that
+ are < or <= to t then we go to position (END+n-1) and compare that.
+ If it is < or <= t then so are the old items
+ If we want to check that there are n items > or >= t then we
+ check item START-n+1 and compare that, if it is < or <= t then so
+ are the items after it.
+
+ Here are the full cases we cover. We don't do time= because its not
+ very useful and difficult to optimize
+
+ time > t, count > c : itemcount > c && [start-c]>t ; there are enough, and past the limitmatches
+ time > t, count >= c : itemcount >= c && [start-c+1]>t ; there are enough and the limit matches
+ time > t, count = c : itemcount >= c && [start-c+1]>t ; there are at least enough and the limit matches
+ && (c<=itemcount || !([start-c]>t)) ; others, if any, do not match
+ time > t, count <= c : itemcount <= c || !([start-c]>t) ; either there aren't too many anyway, or the one just out of count doesn't match
+ time > t, count < c : itemcount < c || !([start-c+1]>t) ; either there aren't too many anyway, or the one just out of count doesn't match
+
+
+ time >= t, count > c : itemcount > c && [start-c]>=t ; there are enough past the limit
+ time >= t, count >= c : itemcount >=c && [start-c+1]>=t ; there are enough and the limit matches
+ time >= t, count = c : itemcount >=c && [start-c+1]>=t ; there are at least enough and the limit matches
+ && (c<=itemcount || !([start-c]>=t)) ; others, if any, do not match
+ time >= t, count <= c : itemcount <=c || !([start-c]>=t) ; either there aren't too many anyway, or the one just out of count doesn't match
+ time >= t, count < c : itemcount < c || !([start-c+1]>=t) ; either there aren't too many anyway, or the one just out of count doesn't match
+
+
+ time <= t, count > c : itemcount > c && [end+c]<=t ; Make sure that at least 1 more than c matches
+ time <= t, count >= c : itemcount >= c && [end+c-1]<=t ; Make sure that at least c items match
+ time <= t, count = c : itemcount >= c && [end+c-1]<=t ; there are at least enough and the limit matches
+ && (c<=itemcount) || !([end+c]<=t)) ; others, if any, do not match
+ time <= t, count <= c : itemcount <=c || !([end+c]<=t) ; either there aren't too many anyway, or the one out of count doesn't match
+ time <= t, count < c : itemcount < c || !([end+c-1]<=t) ; either there aren't too many anyway, or the one out of count doesn't match
+
+
+ time < t, count > c : itemcount > c && [end+c]<t ; Make sure that at least 1 more than c matches
+ time < t, count >= c : itemcount >= c && [end+c-1]<t ; Make sure that at least c items match
+ time < t, count = c : itemcount >= c && [end+c-1]<t ; there are at least enough and the limit matches
+ && (c<=itemcount) || !([end+c]<t)) ; others, if any, do not match
+ time < t, count <= c : itemcount <=c || !([end+c]<t) ; either there aren't too many anyway, or the one out of count doesn't match
+ time < t, count < c : itemcount < c || !([end+c-1]<t) ; either there aren't too many anyway, or the one out of count doesn't match
+ BTW the last rule is the same as this:
+ time < t, count < c : !(itemcount >= c && ([end+c-1]<t) ; either there aren't too many anyway, or the one out of count doesn't match
+
+
+ We can spot the pattern in thse and reduce it to one set of factorized rules.
+ This will sadden some people */
+
+ /* General parameterized count rule */
+ #define COUNT_(tablelist,timecheck,TIME_OP,COUNT_CHECK,limit) ( (tablelist->entry_count COUNT_CHECK \
+ ((!timecheck) || TIME_OP(now,tablelist->time_info[(limit+ip_list_tot)%ip_list_tot].time+(unsigned long)(timecheck)*HZ))) )
+
+ #define COUNT_GT(table,timecheck,TIME_OP,countcheck,limit,DIR_OP) COUNT_(table,timecheck,TIME_OP, > countcheck &&,limit DIR_OP countcheck)
+ #define COUNT_GE(table,timecheck,TIME_OP,countcheck,limit,DIR_OP) COUNT_(table,timecheck,TIME_OP,>= countcheck &&,limit DIR_OP countcheck - DIR_OP 1)
+ #define COUNT_EQ(table,timecheck,TIME_OP,countcheck,limit,DIR_OP) COUNT_GE(table,timecheck,TIME_OP,countcheck,limit,DIR_OP) \
+ && ! COUNT_GT(table,timecheck,TIME_OP,countcheck,limit,DIR_OP)
+ #define COUNT_LE(table,timecheck,TIME_OP,countcheck,limit,DIR_OP) COUNT_(table,timecheck,TIME_OP,<= countcheck || !,limit DIR_OP countcheck)
+ #define COUNT_LT(table,timecheck,TIME_OP,countcheck,limit,DIR_OP) COUNT_(table,timecheck,TIME_OP, < countcheck || !,limit DIR_OP countcheck - DIR_OP 1)
+
+ /* and one rule to bind them. Macros were made for this.
+ Inline functions won't be efficient here because we would have to pass a function pointer or something */
+ #define COUNT(COUNT_FUNC,TIME_OP,table,timecheck,countcheck) (\
+ (((TIME_OP)==IPT_RECENT_TIME_GT)?(COUNT_FUNC(table,timecheck,time_after, countcheck,table->time_pos-table->entry_count,+)):\
+ ((TIME_OP)==IPT_RECENT_TIME_GE)?(COUNT_FUNC(table,timecheck,time_after_eq, countcheck,table->time_pos-table->entry_count,+)):\
+ ((TIME_OP)==IPT_RECENT_TIME_LE)?(COUNT_FUNC(table,timecheck,time_before_eq, countcheck,table->time_pos-1,-)):\
+ ((TIME_OP)==IPT_RECENT_TIME_LT)?(COUNT_FUNC(table,timecheck,time_before, countcheck,table->time_pos-1,-)):\
+ ((TIME_OP)==0 )?(COUNT_FUNC(table,0, time_after, countcheck,table->time_pos-1,-)):\
+ 0) \
+ )
+
+ /* we use do..while(0) so that the acceptable cases can "break" away from the
+ exit block which saves repeating the exit block for each case */
+ if(info->count_ops) do {
+ int invert=(0!=(info->count_ops & IPT_RECENT_COUNT_INVERT));
+#ifdef DEBUG
+ if (debug) printk(KERN_INFO RECENT_NAME " match invert=%d count_ops=%x count=%d time=%d\n",invert,info->count_ops,info->entry_count,info->entry_time);
+#endif
+
+ /* of course limit and over_limit may be invalid if c<count */
+ /* For LT or LE limit=end+c-1; over_limit=limit+1; */
+ /* treat GT, GE or EQ with same limits limit=start-c+1; over_limit=limit-1; */
+ if ((info->count_ops & IPT_RECENT_COUNT_OPS)==IPT_RECENT_COUNT_LT) {
+ if (invert ^ COUNT(COUNT_LT,info->count_ops & IPT_RECENT_TIME_OPS,curr_table,info->entry_time,info->entry_count)) break;
+ } else if ((info->count_ops & IPT_RECENT_COUNT_OPS)==IPT_RECENT_COUNT_LE) {
+ if (invert ^ COUNT(COUNT_LE,info->count_ops & IPT_RECENT_TIME_OPS,curr_table,info->entry_time,info->entry_count)) break;
+ } else if ((info->count_ops & IPT_RECENT_COUNT_OPS)==IPT_RECENT_COUNT_EQ) {
+ if (invert ^ COUNT(COUNT_EQ,info->count_ops & IPT_RECENT_TIME_OPS,curr_table,info->entry_time,info->entry_count)) break;
+ } else if ((info->count_ops & IPT_RECENT_COUNT_OPS)== IPT_RECENT_COUNT_GE) {
+ if (invert ^ COUNT(COUNT_GE,info->count_ops & IPT_RECENT_TIME_OPS,curr_table,info->entry_time,info->entry_count)) break;
+ } else if ((info->count_ops & IPT_RECENT_COUNT_OPS)== IPT_RECENT_COUNT_GT) {
+ if (invert ^ COUNT(COUNT_GT,info->count_ops & IPT_RECENT_TIME_OPS,curr_table,info->entry_time,info->entry_count)) break;
+ }
+ /* If we get here then a definite comparison failed */
+ spin_unlock_bh(&curr_table->list_lock);
+ return 0;
+ } while(0);
+
+ /* this is the next slot to use.
+ If it has never been used then 0 is the oldest slot
+ If it has been used then it is the oldest slot. */
+ location = time_info[curr_table->time_pos].position;
+
+
if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr;
if(!addr) {
@@ -418,8 +567,6 @@
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;
@@ -500,7 +647,27 @@
/* 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;
+ /*
+ location is the next slot in r_list that we will use.
+ Maybe it is already used and we need to remove it from the hash.
+ If it is used then:
+ 1) hash_table[r_list[location].hash_entry]=location
+ Which is to say that anything claiming to have a hash entry
+ should also be pointed to BY that hash entry.
+ This only comes up becase by default r_list[x].hash_entry=0
+ i.e. all items claim to have hash entry 0, so if a real
+ item has hash slot 0 it gets trashed because of lies.
+ SO: If r_list[location].hash_entry]==0 we are in warning condition
+ and need to double check that hash_table reciprocates before we
+ set the hash_table entry to -1 (it may not really belong)
+ This would work;
+ if (r_list[location].hash_entry!=0 || hash_table[0]==location) hash_table[r_list[location].hash_entry] = -1;
+ but we want to combine with a counter for new entries
+ */
+ /* increase counter if this is a new item */
+ if (hash_table[r_list[location].hash_entry]!=location) curr_table->entry_count++;
+ else 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(u_int32_t));
r_list[location].time_pos = curr_table->time_pos;
@@ -587,7 +754,7 @@
#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++;
+ orig_hash_result = (orig_hash_result+1) % ip_list_hash_size;
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
@@ -616,7 +783,10 @@
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) {
+ /* close up the gap we made in time_info.
+ Perhaps we should work out which is the quickest way around the ring buffer
+ and close up the gap the quickest way. We should use memmove while we are at it. */
+ while(((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;
@@ -633,6 +803,9 @@
r_list[location].ttl = 0;
memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t));
r_list[location].oldest_pkt = 0;
+ curr_table->entry_count--;
+ /* back off next-free-slot time_pos to the free slot we just bubbled to the end */
+ curr_table->time_pos=(curr_table->time_pos+ip_list_tot-1)%ip_list_tot;
ans = !info->invert;
}
spin_unlock_bh(&curr_table->list_lock);
@@ -669,6 +842,11 @@
if (matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return 0;
+ if(! (info->check_set & IPT_RECENT_HAS_LIST_OPS)) {
+ printk(KERN_INFO RECENT_NAME ": supports LIST_OPS but user iptables does not!\n");
+ return 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; }
@@ -719,6 +897,7 @@
curr_table->next = NULL;
curr_table->count = 1;
curr_table->time_pos = 0;
+ curr_table->entry_count = 0;
strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN);
curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0';
--- include/linux/netfilter_ipv4/ipt_recent.h.nolimit 2006-02-20 10:12:06.000000000 +0000
+++ include/linux/netfilter_ipv4/ipt_recent.h 2006-03-13 17:11:08.000000000 +0000
@@ -2,13 +2,31 @@
#define _IPT_RECENT_H
#define RECENT_NAME "ipt_recent"
-#define RECENT_VER "v0.3.1"
+#define RECENT_VER "v0.3.2"
#define IPT_RECENT_CHECK 1
#define IPT_RECENT_SET 2
#define IPT_RECENT_UPDATE 4
#define IPT_RECENT_REMOVE 8
#define IPT_RECENT_TTL 16
+#define IPT_RECENT_HAS_LIST_OPS 32
+
+#ifdef IPT_RECENT_HAS_LIST_OPS
+#define IPT_RECENT_COUNT_INVERT 1 << 0
+#define IPT_RECENT_COUNT_LT 1 << 1
+#define IPT_RECENT_COUNT_EQ 1 << 2
+#define IPT_RECENT_COUNT_GT 1 << 3
+#define IPT_RECENT_COUNT_LE (IPT_RECENT_COUNT_LT | IPT_RECENT_COUNT_EQ)
+#define IPT_RECENT_COUNT_GE (IPT_RECENT_COUNT_GT | IPT_RECENT_COUNT_EQ)
+#define IPT_RECENT_COUNT_OPS (IPT_RECENT_COUNT_LT | IPT_RECENT_COUNT_EQ | IPT_RECENT_COUNT_GT)
+
+#define IPT_RECENT_TIME_LT 1 << 4
+#define IPT_RECENT_TIME_EQ 1 << 5 /* But we don't implement checks for == itself */
+#define IPT_RECENT_TIME_GT 1 << 6
+#define IPT_RECENT_TIME_LE (IPT_RECENT_TIME_LT | IPT_RECENT_TIME_EQ)
+#define IPT_RECENT_TIME_GE (IPT_RECENT_TIME_GT | IPT_RECENT_TIME_EQ)
+#define IPT_RECENT_TIME_OPS (IPT_RECENT_TIME_LT | IPT_RECENT_TIME_EQ | IPT_RECENT_TIME_GT)
+#endif
#define IPT_RECENT_SOURCE 0
#define IPT_RECENT_DEST 1
@@ -20,6 +38,11 @@
u_int32_t hit_count;
u_int8_t check_set;
u_int8_t invert;
+#ifdef IPT_RECENT_HAS_LIST_OPS
+ u_int8_t count_ops;
+ u_int32_t entry_count;
+ u_int32_t entry_time;
+#endif
char name[IPT_RECENT_NAME_LEN];
u_int8_t side;
};
next prev parent reply other threads:[~2006-03-13 17:47 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-02-17 17:37 ipt_recent fix Amin Azez
2006-02-20 16:12 ` [patch] ipt_recent Amin Azez
2006-03-04 10:00 ` Patrick McHardy
2006-03-07 15:48 ` Amin Azez
2006-03-08 12:16 ` Patrick McHardy
2006-03-13 17:47 ` Amin Azez [this message]
2006-03-22 14:26 ` ipt_recent patch Stephen Frost
2006-05-03 9:32 ` Amin Azez
2006-05-03 11:41 ` Amin Azez
2006-03-22 12:04 ` [patch] ipt_recent Amin Azez
2006-02-21 11:16 ` BUG: More ipt_recent queries Amin Azez
2006-02-21 16:28 ` Amin Azez
2006-03-04 10:13 ` Patrick McHardy
2006-03-06 3:04 ` Stephen Frost
2006-03-07 15:46 ` Amin Azez
2006-03-04 10:10 ` ipt_recent fix Patrick McHardy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4415B019.6050409@ufomechanic.net \
--to=azez@ufomechanic.net \
--cc=kaber@trash.net \
--cc=netfilter-devel@lists.netfilter.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.