aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv4/netfilter/ipt_recent.c1268
1 files changed, 377 insertions, 891 deletions
diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
index b847ee409efb..9686c4d2057d 100644
--- a/net/ipv4/netfilter/ipt_recent.c
+++ b/net/ipv4/netfilter/ipt_recent.c
@@ -1,1007 +1,493 @@
1/* Kernel module to check if the source address has been seen recently. */ 1/*
2/* Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org */ 2 * Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
3/* Author: Stephen Frost <sfrost@snowman.net> */ 3 *
4/* Project Page: http://snowman.net/projects/ipt_recent/ */ 4 * This program is free software; you can redistribute it and/or modify
5/* This software is distributed under the terms of the GPL, Version 2 */ 5 * it under the terms of the GNU General Public License version 2 as
6/* This copyright does not cover user programs that use kernel services 6 * published by the Free Software Foundation.
7 * by normal system calls. */ 7 *
8 8 * This is a replacement of the old ipt_recent module, which carried the
9#include <linux/module.h> 9 * following copyright notice:
10#include <linux/skbuff.h> 10 *
11 * Author: Stephen Frost <sfrost@snowman.net>
12 * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org
13 */
14#include <linux/init.h>
15#include <linux/moduleparam.h>
11#include <linux/proc_fs.h> 16#include <linux/proc_fs.h>
12#include <linux/spinlock.h> 17#include <linux/seq_file.h>
13#include <linux/interrupt.h> 18#include <linux/string.h>
14#include <asm/uaccess.h>
15#include <linux/ctype.h> 19#include <linux/ctype.h>
16#include <linux/ip.h> 20#include <linux/list.h>
17#include <linux/vmalloc.h> 21#include <linux/random.h>
18#include <linux/moduleparam.h> 22#include <linux/jhash.h>
23#include <linux/bitops.h>
24#include <linux/skbuff.h>
25#include <linux/inet.h>
19 26
20#include <linux/netfilter_ipv4/ip_tables.h> 27#include <linux/netfilter_ipv4/ip_tables.h>
21#include <linux/netfilter_ipv4/ipt_recent.h> 28#include <linux/netfilter_ipv4/ipt_recent.h>
22 29
23#undef DEBUG 30MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
24#define HASH_LOG 9 31MODULE_DESCRIPTION("IP tables recently seen matching module");
32MODULE_LICENSE("GPL");
25 33
26/* Defaults, these can be overridden on the module command-line. */
27static unsigned int ip_list_tot = 100; 34static unsigned int ip_list_tot = 100;
28static unsigned int ip_pkt_list_tot = 20; 35static unsigned int ip_pkt_list_tot = 20;
29static unsigned int ip_list_hash_size = 0; 36static unsigned int ip_list_hash_size = 0;
30static unsigned int ip_list_perms = 0644; 37static unsigned int ip_list_perms = 0644;
31#ifdef DEBUG
32static int debug = 1;
33#endif
34
35static char version[] =
36KERN_INFO RECENT_NAME " " RECENT_VER ": Stephen Frost <sfrost@snowman.net>. http://snowman.net/projects/ipt_recent/\n";
37
38MODULE_AUTHOR("Stephen Frost <sfrost@snowman.net>");
39MODULE_DESCRIPTION("IP tables recently seen matching module " RECENT_VER);
40MODULE_LICENSE("GPL");
41module_param(ip_list_tot, uint, 0400); 38module_param(ip_list_tot, uint, 0400);
42module_param(ip_pkt_list_tot, uint, 0400); 39module_param(ip_pkt_list_tot, uint, 0400);
43module_param(ip_list_hash_size, uint, 0400); 40module_param(ip_list_hash_size, uint, 0400);
44module_param(ip_list_perms, uint, 0400); 41module_param(ip_list_perms, uint, 0400);
45#ifdef DEBUG 42MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
46module_param(debug, bool, 0600); 43MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember (max. 255)");
47MODULE_PARM_DESC(debug,"enable debugging output"); 44MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
48#endif 45MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files");
49MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list"); 46
50MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember"); 47
51MODULE_PARM_DESC(ip_list_hash_size,"size of hash table used to look up IPs"); 48struct recent_entry {
52MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_recent/* files"); 49 struct list_head list;
53 50 struct list_head lru_list;
54/* Structure of our list of recently seen addresses. */ 51 u_int32_t addr;
55struct recent_ip_list { 52 u_int8_t ttl;
56 u_int32_t addr; 53 u_int8_t index;
57 u_int8_t ttl; 54 u_int16_t nstamps;
58 unsigned long last_seen; 55 unsigned long stamps[0];
59 unsigned long *last_pkts;
60 u_int32_t oldest_pkt;
61 u_int32_t hash_entry;
62 u_int32_t time_pos;
63};
64
65struct time_info_list {
66 u_int32_t position;
67 u_int32_t time;
68}; 56};
69 57
70/* Structure of our linked list of tables of recent lists. */ 58struct recent_table {
71struct recent_ip_tables { 59 struct list_head list;
72 char name[IPT_RECENT_NAME_LEN]; 60 char name[IPT_RECENT_NAME_LEN];
73 int count;
74 int time_pos;
75 struct recent_ip_list *table;
76 struct recent_ip_tables *next;
77 spinlock_t list_lock;
78 int *hash_table;
79 struct time_info_list *time_info;
80#ifdef CONFIG_PROC_FS 61#ifdef CONFIG_PROC_FS
81 struct proc_dir_entry *status_proc; 62 struct proc_dir_entry *proc;
82#endif /* CONFIG_PROC_FS */ 63#endif
64 unsigned int refcnt;
65 unsigned int entries;
66 struct list_head lru_list;
67 struct list_head iphash[0];
83}; 68};
84 69
85/* Our current list of addresses we have recently seen. 70static LIST_HEAD(tables);
86 * Only added to on a --set, and only updated on --set || --update
87 */
88static struct recent_ip_tables *r_tables = NULL;
89
90/* We protect r_list with this spinlock so two processors are not modifying
91 * the list at the same time.
92 */
93static DEFINE_SPINLOCK(recent_lock); 71static DEFINE_SPINLOCK(recent_lock);
94 72
95#ifdef CONFIG_PROC_FS 73#ifdef CONFIG_PROC_FS
96/* Our /proc/net/ipt_recent entry */ 74static struct proc_dir_entry *proc_dir;
97static struct proc_dir_entry *proc_net_ipt_recent = NULL; 75static struct file_operations recent_fops;
98#endif
99
100/* Function declaration for later. */
101static int
102match(const struct sk_buff *skb,
103 const struct net_device *in,
104 const struct net_device *out,
105 const struct xt_match *match,
106 const void *matchinfo,
107 int offset,
108 unsigned int protoff,
109 int *hotdrop);
110
111/* Function to hash a given address into the hash table of table_size size */
112static int hash_func(unsigned int addr, int table_size)
113{
114 int result = 0;
115 unsigned int value = addr;
116 do { result ^= value; } while((value >>= HASH_LOG));
117
118#ifdef DEBUG
119 if(debug) printk(KERN_INFO RECENT_NAME ": %d = hash_func(%u,%d)\n",
120 result & (table_size - 1),
121 addr,
122 table_size);
123#endif 76#endif
124 77
125 return(result & (table_size - 1)); 78static u_int32_t hash_rnd;
126} 79static int hash_rnd_initted;
127 80
128#ifdef CONFIG_PROC_FS 81static unsigned int recent_entry_hash(u_int32_t addr)
129/* This is the function which produces the output for our /proc output
130 * interface which lists each IP address, the last seen time and the
131 * other recent times the address was seen.
132 */
133
134static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
135{ 82{
136 int len = 0, count, last_len = 0, pkt_count; 83 if (!hash_rnd_initted) {
137 off_t pos = 0; 84 get_random_bytes(&hash_rnd, 4);
138 off_t begin = 0; 85 hash_rnd_initted = 1;
139 struct recent_ip_tables *curr_table;
140
141 curr_table = (struct recent_ip_tables*) data;
142
143 spin_lock_bh(&curr_table->list_lock);
144 for(count = 0; count < ip_list_tot; count++) {
145 if(!curr_table->table[count].addr) continue;
146 last_len = len;
147 len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].addr));
148 len += sprintf(buffer+len,"ttl: %u ",curr_table->table[count].ttl);
149 len += sprintf(buffer+len,"last_seen: %lu ",curr_table->table[count].last_seen);
150 len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[count].oldest_pkt);
151 len += sprintf(buffer+len,"last_pkts: %lu",curr_table->table[count].last_pkts[0]);
152 for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) {
153 if(!curr_table->table[count].last_pkts[pkt_count]) break;
154 len += sprintf(buffer+len,", %lu",curr_table->table[count].last_pkts[pkt_count]);
155 }
156 len += sprintf(buffer+len,"\n");
157 pos = begin + len;
158 if(pos < offset) { len = 0; begin = pos; }
159 if(pos > offset + length) { len = last_len; break; }
160 } 86 }
161 87 return jhash_1word(addr, hash_rnd) & (ip_list_hash_size - 1);
162 *start = buffer + (offset - begin);
163 len -= (offset - begin);
164 if(len > length) len = length;
165
166 spin_unlock_bh(&curr_table->list_lock);
167 return len;
168} 88}
169 89
170/* ip_recent_ctrl provides an interface for users to modify the table 90static struct recent_entry *
171 * directly. This allows adding entries, removing entries, and 91recent_entry_lookup(const struct recent_table *table, u_int32_t addr, u_int8_t ttl)
172 * flushing the entire table.
173 * This is done by opening up the appropriate table for writing and
174 * sending one of:
175 * xx.xx.xx.xx -- Add entry to table with current time
176 * +xx.xx.xx.xx -- Add entry to table with current time
177 * -xx.xx.xx.xx -- Remove entry from table
178 * clear -- Flush table, remove all entries
179 */
180
181static int ip_recent_ctrl(struct file *file, const char __user *input, unsigned long size, void *data)
182{ 92{
183 static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff }; 93 struct recent_entry *e;
184 u_int32_t val; 94 unsigned int h;
185 int base, used = 0; 95
186 char c, *cp; 96 h = recent_entry_hash(addr);
187 union iaddr { 97 list_for_each_entry(e, &table->iphash[h], list)
188 uint8_t bytes[4]; 98 if (e->addr == addr && (ttl == e->ttl || !ttl || !e->ttl))
189 uint32_t word; 99 return e;
190 } res; 100 return NULL;
191 uint8_t *pp = res.bytes; 101}
192 int digit;
193
194 char buffer[20];
195 int len, check_set = 0, count;
196 u_int32_t addr = 0;
197 struct sk_buff *skb;
198 struct ipt_recent_info *info;
199 struct recent_ip_tables *curr_table;
200
201 curr_table = (struct recent_ip_tables*) data;
202
203 if(size > 20) len = 20; else len = size;
204
205 if(copy_from_user(buffer,input,len)) return -EFAULT;
206
207 if(len < 20) buffer[len] = '\0';
208
209#ifdef DEBUG
210 if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl len: %d, input: `%.20s'\n",len,buffer);
211#endif
212 102
213 cp = buffer; 103static void recent_entry_remove(struct recent_table *t, struct recent_entry *e)
214 while(isspace(*cp)) { cp++; used++; if(used >= len-5) return used; } 104{
105 list_del(&e->list);
106 list_del(&e->lru_list);
107 kfree(e);
108 t->entries--;
109}
215 110
216 /* Check if we are asked to flush the entire table */ 111static struct recent_entry *
217 if(!memcmp(cp,"clear",5)) { 112recent_entry_init(struct recent_table *t, u_int32_t addr, u_int8_t ttl)
218 used += 5; 113{
219 spin_lock_bh(&curr_table->list_lock); 114 struct recent_entry *e;
220 curr_table->time_pos = 0;
221 for(count = 0; count < ip_list_hash_size; count++) {
222 curr_table->hash_table[count] = -1;
223 }
224 for(count = 0; count < ip_list_tot; count++) {
225 curr_table->table[count].last_seen = 0;
226 curr_table->table[count].addr = 0;
227 curr_table->table[count].ttl = 0;
228 memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
229 curr_table->table[count].oldest_pkt = 0;
230 curr_table->table[count].time_pos = 0;
231 curr_table->time_info[count].position = count;
232 curr_table->time_info[count].time = 0;
233 }
234 spin_unlock_bh(&curr_table->list_lock);
235 return used;
236 }
237 115
238 check_set = IPT_RECENT_SET; 116 if (t->entries >= ip_list_tot) {
239 switch(*cp) { 117 e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
240 case '+': check_set = IPT_RECENT_SET; cp++; used++; break; 118 recent_entry_remove(t, e);
241 case '-': check_set = IPT_RECENT_REMOVE; cp++; used++; break;
242 default: if(!isdigit(*cp)) return (used+1); break;
243 } 119 }
120 e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot,
121 GFP_ATOMIC);
122 if (e == NULL)
123 return NULL;
124 e->addr = addr;
125 e->ttl = ttl;
126 e->stamps[0] = jiffies;
127 e->nstamps = 1;
128 e->index = 1;
129 list_add_tail(&e->list, &t->iphash[recent_entry_hash(addr)]);
130 list_add_tail(&e->lru_list, &t->lru_list);
131 t->entries++;
132 return e;
133}
244 134
245#ifdef DEBUG 135static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
246 if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp,check_set); 136{
247#endif 137 e->stamps[e->index++] = jiffies;
248 /* Get addr (effectively inet_aton()) */ 138 if (e->index > e->nstamps)
249 /* Shamelessly stolen from libc, a function in the kernel for doing 139 e->nstamps = e->index;
250 * this would, of course, be greatly preferred, but our options appear 140 e->index %= ip_pkt_list_tot;
251 * to be rather limited, so we will just do it ourselves here. 141 list_move_tail(&e->lru_list, &t->lru_list);
252 */ 142}
253 res.word = 0;
254
255 c = *cp;
256 for(;;) {
257 if(!isdigit(c)) return used;
258 val = 0; base = 10; digit = 0;
259 if(c == '0') {
260 c = *++cp;
261 if(c == 'x' || c == 'X') base = 16, c = *++cp;
262 else { base = 8; digit = 1; }
263 }
264 for(;;) {
265 if(isascii(c) && isdigit(c)) {
266 if(base == 8 && (c == '8' || c == '0')) return used;
267 val = (val * base) + (c - '0');
268 c = *++cp;
269 digit = 1;
270 } else if(base == 16 && isascii(c) && isxdigit(c)) {
271 val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A'));
272 c = *++cp;
273 digit = 1;
274 } else break;
275 }
276 if(c == '.') {
277 if(pp > res.bytes + 2 || val > 0xff) return used;
278 *pp++ = val;
279 c = *++cp;
280 } else break;
281 }
282 used = cp - buffer;
283 if(c != '\0' && (!isascii(c) || !isspace(c))) return used;
284 if(c == '\n') used++;
285 if(!digit) return used;
286 143
287 if(val > max[pp - res.bytes]) return used; 144static struct recent_table *recent_table_lookup(const char *name)
288 addr = res.word | htonl(val); 145{
146 struct recent_table *t;
289 147
290 if(!addr && check_set == IPT_RECENT_SET) return used; 148 list_for_each_entry(t, &tables, list)
149 if (!strcmp(t->name, name))
150 return t;
151 return NULL;
152}
291 153
292#ifdef DEBUG 154static void recent_table_flush(struct recent_table *t)
293 if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used); 155{
294#endif 156 struct recent_entry *e, *next;
157 unsigned int i;
295 158
296 /* Set up and just call match */ 159 for (i = 0; i < ip_list_hash_size; i++) {
297 info = kmalloc(sizeof(struct ipt_recent_info),GFP_KERNEL); 160 list_for_each_entry_safe(e, next, &t->iphash[i], list)
298 if(!info) { return -ENOMEM; } 161 recent_entry_remove(t, e);
299 info->seconds = 0;
300 info->hit_count = 0;
301 info->check_set = check_set;
302 info->invert = 0;
303 info->side = IPT_RECENT_SOURCE;
304 strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN);
305 info->name[IPT_RECENT_NAME_LEN-1] = '\0';
306
307 skb = kmalloc(sizeof(struct sk_buff),GFP_KERNEL);
308 if (!skb) {
309 used = -ENOMEM;
310 goto out_free_info;
311 }
312 skb->nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL);
313 if (!skb->nh.iph) {
314 used = -ENOMEM;
315 goto out_free_skb;
316 } 162 }
317
318 skb->nh.iph->saddr = addr;
319 skb->nh.iph->daddr = 0;
320 /* Clear ttl since we have no way of knowing it */
321 skb->nh.iph->ttl = 0;
322 match(skb,NULL,NULL,NULL,info,0,0,NULL);
323
324 kfree(skb->nh.iph);
325out_free_skb:
326 kfree(skb);
327out_free_info:
328 kfree(info);
329
330#ifdef DEBUG
331 if(debug) printk(KERN_INFO RECENT_NAME ": Leaving ip_recent_ctrl addr: %u used: %d\n",addr,used);
332#endif
333 return used;
334} 163}
335 164
336#endif /* CONFIG_PROC_FS */
337
338/* 'match' is our primary function, called by the kernel whenever a rule is
339 * hit with our module as an option to it.
340 * What this function does depends on what was specifically asked of it by
341 * the user:
342 * --set -- Add or update last seen time of the source address of the packet
343 * -- matchinfo->check_set == IPT_RECENT_SET
344 * --rcheck -- Just check if the source address is in the list
345 * -- matchinfo->check_set == IPT_RECENT_CHECK
346 * --update -- If the source address is in the list, update last_seen
347 * -- matchinfo->check_set == IPT_RECENT_UPDATE
348 * --remove -- If the source address is in the list, remove it
349 * -- matchinfo->check_set == IPT_RECENT_REMOVE
350 * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
351 * -- matchinfo->seconds
352 * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
353 * -- matchinfo->hit_count
354 * --seconds and --hitcount can be combined
355 */
356static int 165static int
357match(const struct sk_buff *skb, 166ipt_recent_match(const struct sk_buff *skb,
358 const struct net_device *in, 167 const struct net_device *in, const struct net_device *out,
359 const struct net_device *out, 168 const struct xt_match *match, const void *matchinfo,
360 const struct xt_match *match, 169 int offset, unsigned int protoff, int *hotdrop)
361 const void *matchinfo,
362 int offset,
363 unsigned int protoff,
364 int *hotdrop)
365{ 170{
366 int pkt_count, hits_found, ans;
367 unsigned long now;
368 const struct ipt_recent_info *info = matchinfo; 171 const struct ipt_recent_info *info = matchinfo;
369 u_int32_t addr = 0, time_temp; 172 struct recent_table *t;
370 u_int8_t ttl = skb->nh.iph->ttl; 173 struct recent_entry *e;
371 int *hash_table; 174 u_int32_t addr;
372 int orig_hash_result, hash_result, temp, location = 0, time_loc, end_collision_chain = -1; 175 u_int8_t ttl;
373 struct time_info_list *time_info; 176 int ret = info->invert;
374 struct recent_ip_tables *curr_table;
375 struct recent_ip_tables *last_table;
376 struct recent_ip_list *r_list;
377
378#ifdef DEBUG
379 if(debug) printk(KERN_INFO RECENT_NAME ": match() called\n");
380#endif
381
382 /* Default is false ^ info->invert */
383 ans = info->invert;
384 177
385#ifdef DEBUG 178 if (info->side == IPT_RECENT_DEST)
386 if(debug) printk(KERN_INFO RECENT_NAME ": match(): name = '%s'\n",info->name); 179 addr = skb->nh.iph->daddr;
387#endif 180 else
181 addr = skb->nh.iph->saddr;
388 182
389 /* if out != NULL then routing has been done and TTL changed. 183 ttl = skb->nh.iph->ttl;
390 * We change it back here internally for match what came in before routing. */ 184 /* use TTL as seen before forwarding */
391 if(out) ttl++; 185 if (out && !skb->sk)
186 ttl++;
392 187
393 /* Find the right table */
394 spin_lock_bh(&recent_lock); 188 spin_lock_bh(&recent_lock);
395 curr_table = r_tables; 189 t = recent_table_lookup(info->name);
396 while( (last_table = curr_table) && strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (curr_table = curr_table->next) ); 190 e = recent_entry_lookup(t, addr,
397 191 info->check_set & IPT_RECENT_TTL ? ttl : 0);
398#ifdef DEBUG 192 if (e == NULL) {
399 if(debug) printk(KERN_INFO RECENT_NAME ": match(): table found('%s')\n",info->name); 193 if (!(info->check_set & IPT_RECENT_SET))
400#endif 194 goto out;
401 195 e = recent_entry_init(t, addr, ttl);
402 spin_unlock_bh(&recent_lock); 196 if (e == NULL)
403 197 *hotdrop = 1;
404 /* Table with this name not found, match impossible */ 198 ret ^= 1;
405 if(!curr_table) { return ans; } 199 goto out;
406
407 /* Make sure no one is changing the list while we work with it */
408 spin_lock_bh(&curr_table->list_lock);
409
410 r_list = curr_table->table;
411 if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr;
412
413 if(!addr) {
414#ifdef DEBUG
415 if(debug) printk(KERN_INFO RECENT_NAME ": match() address (%u) invalid, leaving.\n",addr);
416#endif
417 spin_unlock_bh(&curr_table->list_lock);
418 return ans;
419 } 200 }
420 201
421#ifdef DEBUG 202 if (info->check_set & IPT_RECENT_SET)
422 if(debug) printk(KERN_INFO RECENT_NAME ": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr,ttl,skb->nh.iph->ttl); 203 ret ^= 1;
423#endif 204 else if (info->check_set & IPT_RECENT_REMOVE) {
424 205 recent_entry_remove(t, e);
425 /* Get jiffies now in case they changed while we were waiting for a lock */ 206 ret ^= 1;
426 now = jiffies; 207 } else if (info->check_set & (IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) {
427 hash_table = curr_table->hash_table; 208 unsigned long t = jiffies - info->seconds * HZ;
428 time_info = curr_table->time_info; 209 unsigned int i, hits = 0;
429 210
430 orig_hash_result = hash_result = hash_func(addr,ip_list_hash_size); 211 for (i = 0; i < e->nstamps; i++) {
431 /* Hash entry at this result used */ 212 if (info->seconds && time_after(t, e->stamps[i]))
432 /* Check for TTL match if requested. If TTL is zero then a match would never 213 continue;
433 * happen, so match regardless of existing TTL in that case. Zero means the 214 if (++hits >= info->hit_count) {
434 * entry was added via the /proc interface anyway, so we will just use the 215 ret ^= 1;
435 * first TTL we get for that IP address. */ 216 break;
436 if(info->check_set & IPT_RECENT_TTL) {
437 while(hash_table[hash_result] != -1 && !(r_list[hash_table[hash_result]].addr == addr &&
438 (!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) {
439 /* Collision in hash table */
440 hash_result = (hash_result + 1) % ip_list_hash_size;
441 }
442 } else {
443 while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) {
444 /* Collision in hash table */
445 hash_result = (hash_result + 1) % ip_list_hash_size;
446 }
447 }
448
449 if(hash_table[hash_result] == -1 && !(info->check_set & IPT_RECENT_SET)) {
450 /* IP not in list and not asked to SET */
451 spin_unlock_bh(&curr_table->list_lock);
452 return ans;
453 }
454
455 /* Check if we need to handle the collision, do not need to on REMOVE */
456 if(orig_hash_result != hash_result && !(info->check_set & IPT_RECENT_REMOVE)) {
457#ifdef DEBUG
458 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
459 orig_hash_result,
460 hash_result,
461 r_list[hash_table[orig_hash_result]].addr,
462 addr);
463#endif
464
465 /* We had a collision.
466 * orig_hash_result is where we started, hash_result is where we ended up.
467 * So, swap them because we are likely to see the same guy again sooner */
468#ifdef DEBUG
469 if(debug) {
470 printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table[orig_hash_result]);
471 printk(KERN_INFO RECENT_NAME ": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
472 r_list[hash_table[orig_hash_result]].hash_entry);
473 }
474#endif
475
476 r_list[hash_table[orig_hash_result]].hash_entry = hash_result;
477
478
479 temp = hash_table[orig_hash_result];
480#ifdef DEBUG
481 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[hash_result] = %d\n",hash_table[hash_result]);
482#endif
483 hash_table[orig_hash_result] = hash_table[hash_result];
484 hash_table[hash_result] = temp;
485 temp = hash_result;
486 hash_result = orig_hash_result;
487 orig_hash_result = temp;
488 time_info[r_list[hash_table[orig_hash_result]].time_pos].position = hash_table[orig_hash_result];
489 if(hash_table[hash_result] != -1) {
490 r_list[hash_table[hash_result]].hash_entry = hash_result;
491 time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
492 }
493
494#ifdef DEBUG
495 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision handled.\n");
496#endif
497 }
498
499 if(hash_table[hash_result] == -1) {
500#ifdef DEBUG
501 if(debug) printk(KERN_INFO RECENT_NAME ": match(): New table entry. (hr: %d,ha: %u)\n",
502 hash_result, addr);
503#endif
504
505 /* New item found and IPT_RECENT_SET, so we need to add it */
506 location = time_info[curr_table->time_pos].position;
507 hash_table[r_list[location].hash_entry] = -1;
508 hash_table[hash_result] = location;
509 memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
510 r_list[location].time_pos = curr_table->time_pos;
511 r_list[location].addr = addr;
512 r_list[location].ttl = ttl;
513 r_list[location].last_seen = now;
514 r_list[location].oldest_pkt = 1;
515 r_list[location].last_pkts[0] = now;
516 r_list[location].hash_entry = hash_result;
517 time_info[curr_table->time_pos].time = r_list[location].last_seen;
518 curr_table->time_pos = (curr_table->time_pos + 1) % ip_list_tot;
519
520 ans = !info->invert;
521 } else {
522#ifdef DEBUG
523 if(debug) printk(KERN_INFO RECENT_NAME ": match(): Existing table entry. (hr: %d,ha: %u)\n",
524 hash_result,
525 addr);
526#endif
527
528 /* Existing item found */
529 location = hash_table[hash_result];
530 /* We have a match on address, now to make sure it meets all requirements for a
531 * full match. */
532 if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) {
533 if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert;
534 if(info->seconds && !info->hit_count) {
535 if(time_before_eq(now,r_list[location].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert;
536 }
537 if(info->seconds && info->hit_count) {
538 for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
539 if(r_list[location].last_pkts[pkt_count] == 0) break;
540 if(time_before_eq(now,r_list[location].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++;
541 }
542 if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
543 }
544 if(info->hit_count && !info->seconds) {
545 for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
546 if(r_list[location].last_pkts[pkt_count] == 0) break;
547 hits_found++;
548 }
549 if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
550 }
551 }
552#ifdef DEBUG
553 if(debug) {
554 if(ans)
555 printk(KERN_INFO RECENT_NAME ": match(): match addr: %u\n",addr);
556 else
557 printk(KERN_INFO RECENT_NAME ": match(): no match addr: %u\n",addr);
558 }
559#endif
560
561 /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
562 * current timestamp to the last_seen. */
563 if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) {
564#ifdef DEBUG
565 if(debug) printk(KERN_INFO RECENT_NAME ": match(): SET or UPDATE; updating time info.\n");
566#endif
567 /* Have to update our time info */
568 time_loc = r_list[location].time_pos;
569 time_info[time_loc].time = now;
570 time_info[time_loc].position = location;
571 while((time_info[(time_loc+1) % ip_list_tot].time < time_info[time_loc].time) && ((time_loc+1) % ip_list_tot) != curr_table->time_pos) {
572 time_temp = time_info[time_loc].time;
573 time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
574 time_info[(time_loc+1)%ip_list_tot].time = time_temp;
575 time_temp = time_info[time_loc].position;
576 time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
577 time_info[(time_loc+1)%ip_list_tot].position = time_temp;
578 r_list[time_info[time_loc].position].time_pos = time_loc;
579 r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
580 time_loc = (time_loc+1) % ip_list_tot;
581 }
582 r_list[location].time_pos = time_loc;
583 r_list[location].ttl = ttl;
584 r_list[location].last_pkts[r_list[location].oldest_pkt] = now;
585 r_list[location].oldest_pkt = ++r_list[location].oldest_pkt % ip_pkt_list_tot;
586 r_list[location].last_seen = now;
587 }
588 /* If we have been asked to remove the entry from the list, just set it to 0 */
589 if(info->check_set & IPT_RECENT_REMOVE) {
590#ifdef DEBUG
591 if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result,hash_result);
592#endif
593 /* Check if this is part of a collision chain */
594 while(hash_table[(orig_hash_result+1) % ip_list_hash_size] != -1) {
595 orig_hash_result++;
596 if(hash_func(r_list[hash_table[orig_hash_result]].addr,ip_list_hash_size) == hash_result) {
597 /* Found collision chain, how deep does this rabbit hole go? */
598#ifdef DEBUG
599 if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; found collision chain.\n");
600#endif
601 end_collision_chain = orig_hash_result;
602 }
603 }
604 if(end_collision_chain != -1) {
605#ifdef DEBUG
606 if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; part of collision chain, moving to end.\n");
607#endif
608 /* Part of a collision chain, swap it with the end of the chain
609 * before removing. */
610 r_list[hash_table[end_collision_chain]].hash_entry = hash_result;
611 temp = hash_table[end_collision_chain];
612 hash_table[end_collision_chain] = hash_table[hash_result];
613 hash_table[hash_result] = temp;
614 time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
615 hash_result = end_collision_chain;
616 r_list[hash_table[hash_result]].hash_entry = hash_result;
617 time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
618 }
619 location = hash_table[hash_result];
620 hash_table[r_list[location].hash_entry] = -1;
621 time_loc = r_list[location].time_pos;
622 time_info[time_loc].time = 0;
623 time_info[time_loc].position = location;
624 while((time_info[(time_loc+1) % ip_list_tot].time < time_info[time_loc].time) && ((time_loc+1) % ip_list_tot) != curr_table->time_pos) {
625 time_temp = time_info[time_loc].time;
626 time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
627 time_info[(time_loc+1)%ip_list_tot].time = time_temp;
628 time_temp = time_info[time_loc].position;
629 time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
630 time_info[(time_loc+1)%ip_list_tot].position = time_temp;
631 r_list[time_info[time_loc].position].time_pos = time_loc;
632 r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
633 time_loc = (time_loc+1) % ip_list_tot;
634 } 217 }
635 r_list[location].time_pos = time_loc;
636 r_list[location].last_seen = 0;
637 r_list[location].addr = 0;
638 r_list[location].ttl = 0;
639 memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long));
640 r_list[location].oldest_pkt = 0;
641 ans = !info->invert;
642 } 218 }
643 spin_unlock_bh(&curr_table->list_lock);
644 return ans;
645 } 219 }
646 220
647 spin_unlock_bh(&curr_table->list_lock); 221 if (info->check_set & IPT_RECENT_SET ||
648#ifdef DEBUG 222 (info->check_set & IPT_RECENT_UPDATE && ret)) {
649 if(debug) printk(KERN_INFO RECENT_NAME ": match() left.\n"); 223 recent_entry_update(t, e);
650#endif 224 e->ttl = ttl;
651 return ans; 225 }
226out:
227 spin_unlock_bh(&recent_lock);
228 return ret;
652} 229}
653 230
654/* This function is to verify that the rule given during the userspace iptables
655 * command is correct.
656 * If the command is valid then we check if the table name referred to by the
657 * rule exists, if not it is created.
658 */
659static int 231static int
660checkentry(const char *tablename, 232ipt_recent_checkentry(const char *tablename, const void *ip,
661 const void *ip, 233 const struct xt_match *match, void *matchinfo,
662 const struct xt_match *match, 234 unsigned int matchsize, unsigned int hook_mask)
663 void *matchinfo,
664 unsigned int matchsize,
665 unsigned int hook_mask)
666{ 235{
667 int flag = 0, c;
668 unsigned long *hold;
669 const struct ipt_recent_info *info = matchinfo; 236 const struct ipt_recent_info *info = matchinfo;
670 struct recent_ip_tables *curr_table, *find_table, *last_table; 237 struct recent_table *t;
671 238 unsigned i;
672#ifdef DEBUG 239 int ret = 0;
673 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() entered.\n");
674#endif
675
676 /* seconds and hit_count only valid for CHECK/UPDATE */
677 if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; }
678 if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; }
679 if(info->check_set & IPT_RECENT_CHECK) flag++;
680 if(info->check_set & IPT_RECENT_UPDATE) flag++;
681
682 /* One and only one of these should ever be set */
683 if(flag != 1) return 0;
684 240
685 /* Name must be set to something */ 241 if (hweight8(info->check_set &
686 if(!info->name || !info->name[0]) return 0; 242 (IPT_RECENT_SET | IPT_RECENT_REMOVE |
243 IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) != 1)
244 return 0;
245 if ((info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE)) &&
246 (info->seconds || info->hit_count))
247 return 0;
248 if (info->name[0] == '\0' ||
249 strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN)
250 return 0;
687 251
688 /* Things look good, create a list for this if it does not exist */
689 /* Lock the linked list while we play with it */
690 spin_lock_bh(&recent_lock); 252 spin_lock_bh(&recent_lock);
691 253 t = recent_table_lookup(info->name);
692 /* Look for an entry with this name already created */ 254 if (t != NULL) {
693 /* Finds the end of the list and the entry before the end if current name does not exist */ 255 t->refcnt++;
694 find_table = r_tables; 256 ret = 1;
695 while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) ); 257 goto out;
696
697 /* If a table already exists just increment the count on that table and return */
698 if(find_table) {
699#ifdef DEBUG
700 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), incrementing count.\n",info->name);
701#endif
702 find_table->count++;
703 spin_unlock_bh(&recent_lock);
704 return 1;
705 } 258 }
706 259
707 spin_unlock_bh(&recent_lock); 260 t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
708 261 GFP_ATOMIC);
709 /* Table with this name not found */ 262 if (t == NULL)
710 /* Allocate memory for new linked list item */ 263 goto out;
711 264 strcpy(t->name, info->name);
712#ifdef DEBUG 265 INIT_LIST_HEAD(&t->lru_list);
713 if(debug) { 266 for (i = 0; i < ip_list_hash_size; i++)
714 printk(KERN_INFO RECENT_NAME ": checkentry: no table found (%s)\n",info->name); 267 INIT_LIST_HEAD(&t->iphash[i]);
715 printk(KERN_INFO RECENT_NAME ": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables)); 268#ifdef CONFIG_PROC_FS
269 t->proc = create_proc_entry(t->name, ip_list_perms, proc_dir);
270 if (t->proc == NULL) {
271 kfree(t);
272 goto out;
716 } 273 }
274 t->proc->proc_fops = &recent_fops;
275 t->proc->data = t;
717#endif 276#endif
277 list_add_tail(&t->list, &tables);
278 ret = 1;
279out:
280 spin_unlock_bh(&recent_lock);
281 return ret;
282}
718 283
719 curr_table = vmalloc(sizeof(struct recent_ip_tables)); 284static void
720 if(curr_table == NULL) return 0; 285ipt_recent_destroy(const struct xt_match *match, void *matchinfo,
721 286 unsigned int matchsize)
722 spin_lock_init(&curr_table->list_lock); 287{
723 curr_table->next = NULL; 288 const struct ipt_recent_info *info = matchinfo;
724 curr_table->count = 1; 289 struct recent_table *t;
725 curr_table->time_pos = 0;
726 strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN);
727 curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0';
728
729 /* Allocate memory for this table and the list of packets in each entry. */
730#ifdef DEBUG
731 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for table (%s).\n",
732 sizeof(struct recent_ip_list)*ip_list_tot,
733 info->name);
734#endif
735
736 curr_table->table = vmalloc(sizeof(struct recent_ip_list)*ip_list_tot);
737 if(curr_table->table == NULL) { vfree(curr_table); return 0; }
738 memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot);
739#ifdef DEBUG
740 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for pkt_list.\n",
741 sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
742#endif
743
744 hold = vmalloc(sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot);
745#ifdef DEBUG
746 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: After pkt_list allocation.\n");
747#endif
748 if(hold == NULL) {
749 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for pkt_list.\n");
750 vfree(curr_table->table);
751 vfree(curr_table);
752 return 0;
753 }
754 for(c = 0; c < ip_list_tot; c++) {
755 curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot;
756 }
757 290
758 /* Allocate memory for the hash table */ 291 spin_lock_bh(&recent_lock);
759#ifdef DEBUG 292 t = recent_table_lookup(info->name);
760 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for hash_table.\n", 293 if (--t->refcnt == 0) {
761 sizeof(int)*ip_list_hash_size); 294 list_del(&t->list);
295 recent_table_flush(t);
296#ifdef CONFIG_PROC_FS
297 remove_proc_entry(t->name, proc_dir);
762#endif 298#endif
763 299 kfree(t);
764 curr_table->hash_table = vmalloc(sizeof(int)*ip_list_hash_size);
765 if(!curr_table->hash_table) {
766 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for hash_table.\n");
767 vfree(hold);
768 vfree(curr_table->table);
769 vfree(curr_table);
770 return 0;
771 }
772
773 for(c = 0; c < ip_list_hash_size; c++) {
774 curr_table->hash_table[c] = -1;
775 } 300 }
301 spin_unlock_bh(&recent_lock);
302}
776 303
777 /* Allocate memory for the time info */ 304#ifdef CONFIG_PROC_FS
778#ifdef DEBUG 305struct recent_iter_state {
779 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for time_info.\n", 306 struct recent_table *table;
780 sizeof(struct time_info_list)*ip_list_tot); 307 unsigned int bucket;
781#endif 308};
782 309
783 curr_table->time_info = vmalloc(sizeof(struct time_info_list)*ip_list_tot); 310static void *recent_seq_start(struct seq_file *seq, loff_t *pos)
784 if(!curr_table->time_info) { 311{
785 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for time_info.\n"); 312 struct recent_iter_state *st = seq->private;
786 vfree(curr_table->hash_table); 313 struct recent_table *t = st->table;
787 vfree(hold); 314 struct recent_entry *e;
788 vfree(curr_table->table); 315 loff_t p = *pos;
789 vfree(curr_table);
790 return 0;
791 }
792 for(c = 0; c < ip_list_tot; c++) {
793 curr_table->time_info[c].position = c;
794 curr_table->time_info[c].time = 0;
795 }
796 316
797 /* Put the new table in place */
798 spin_lock_bh(&recent_lock); 317 spin_lock_bh(&recent_lock);
799 find_table = r_tables;
800 while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
801
802 /* If a table already exists just increment the count on that table and return */
803 if(find_table) {
804 find_table->count++;
805 spin_unlock_bh(&recent_lock);
806#ifdef DEBUG
807 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), created by other process.\n",info->name);
808#endif
809 vfree(curr_table->time_info);
810 vfree(curr_table->hash_table);
811 vfree(hold);
812 vfree(curr_table->table);
813 vfree(curr_table);
814 return 1;
815 }
816 if(!last_table) r_tables = curr_table; else last_table->next = curr_table;
817 318
818 spin_unlock_bh(&recent_lock); 319 for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) {
819 320 list_for_each_entry(e, &t->iphash[st->bucket], list) {
820#ifdef CONFIG_PROC_FS 321 if (p-- == 0)
821 /* Create our proc 'status' entry. */ 322 return e;
822 curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
823 if (!curr_table->status_proc) {
824 vfree(hold);
825 printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
826 /* Destroy the created table */
827 spin_lock_bh(&recent_lock);
828 last_table = NULL;
829 curr_table = r_tables;
830 if(!curr_table) {
831#ifdef DEBUG
832 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, no tables.\n");
833#endif
834 spin_unlock_bh(&recent_lock);
835 return 0;
836 }
837 while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
838 if(!curr_table) {
839#ifdef DEBUG
840 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, table already destroyed.\n");
841#endif
842 spin_unlock_bh(&recent_lock);
843 return 0;
844 } 323 }
845 if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
846 spin_unlock_bh(&recent_lock);
847 vfree(curr_table->time_info);
848 vfree(curr_table->hash_table);
849 vfree(curr_table->table);
850 vfree(curr_table);
851 return 0;
852 } 324 }
853 325 return NULL;
854 curr_table->status_proc->owner = THIS_MODULE; 326}
855 curr_table->status_proc->data = curr_table;
856 wmb();
857 curr_table->status_proc->read_proc = ip_recent_get_info;
858 curr_table->status_proc->write_proc = ip_recent_ctrl;
859#endif /* CONFIG_PROC_FS */
860
861#ifdef DEBUG
862 if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() left.\n");
863#endif
864 327
865 return 1; 328static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos)
329{
330 struct recent_iter_state *st = seq->private;
331 struct recent_table *t = st->table;
332 struct recent_entry *e = v;
333 struct list_head *head = e->list.next;
334
335 while (head == &t->iphash[st->bucket]) {
336 if (++st->bucket >= ip_list_hash_size)
337 return NULL;
338 head = t->iphash[st->bucket].next;
339 }
340 (*pos)++;
341 return list_entry(head, struct recent_entry, list);
866} 342}
867 343
868/* This function is called in the event that a rule matching this module is 344static void recent_seq_stop(struct seq_file *s, void *v)
869 * removed.
870 * When this happens we need to check if there are no other rules matching
871 * the table given. If that is the case then we remove the table and clean
872 * up its memory.
873 */
874static void
875destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
876{ 345{
877 const struct ipt_recent_info *info = matchinfo; 346 spin_unlock_bh(&recent_lock);
878 struct recent_ip_tables *curr_table, *last_table; 347}
879 348
880#ifdef DEBUG 349static int recent_seq_show(struct seq_file *seq, void *v)
881 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() entered.\n"); 350{
882#endif 351 struct recent_entry *e = v;
352 unsigned int i;
353
354 i = (e->index - 1) % ip_pkt_list_tot;
355 seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u",
356 NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index);
357 for (i = 0; i < e->nstamps; i++)
358 seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]);
359 seq_printf(seq, "\n");
360 return 0;
361}
883 362
884 if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return; 363static struct seq_operations recent_seq_ops = {
364 .start = recent_seq_start,
365 .next = recent_seq_next,
366 .stop = recent_seq_stop,
367 .show = recent_seq_show,
368};
885 369
886 /* Lock the linked list while we play with it */ 370static int recent_seq_open(struct inode *inode, struct file *file)
887 spin_lock_bh(&recent_lock); 371{
372 struct proc_dir_entry *pde = PDE(inode);
373 struct seq_file *seq;
374 struct recent_iter_state *st;
375 int ret;
376
377 st = kzalloc(sizeof(*st), GFP_KERNEL);
378 if (st == NULL)
379 return -ENOMEM;
380 ret = seq_open(file, &recent_seq_ops);
381 if (ret)
382 kfree(st);
383 st->table = pde->data;
384 seq = file->private_data;
385 seq->private = st;
386 return ret;
387}
888 388
889 /* Look for an entry with this name already created */ 389static ssize_t recent_proc_write(struct file *file, const char __user *input,
890 /* Finds the end of the list and the entry before the end if current name does not exist */ 390 size_t size, loff_t *loff)
891 last_table = NULL; 391{
892 curr_table = r_tables; 392 struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
893 if(!curr_table) { 393 struct recent_table *t = pde->data;
894#ifdef DEBUG 394 struct recent_entry *e;
895 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() No tables found, leaving.\n"); 395 char buf[sizeof("+255.255.255.255")], *c = buf;
896#endif 396 u_int32_t addr;
397 int add;
398
399 if (size > sizeof(buf))
400 size = sizeof(buf);
401 if (copy_from_user(buf, input, size))
402 return -EFAULT;
403 while (isspace(*c))
404 c++;
405
406 if (size - (c - buf) < 5)
407 return c - buf;
408 if (!strncmp(c, "clear", 5)) {
409 c += 5;
410 spin_lock_bh(&recent_lock);
411 recent_table_flush(t);
897 spin_unlock_bh(&recent_lock); 412 spin_unlock_bh(&recent_lock);
898 return; 413 return c - buf;
899 } 414 }
900 while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
901 415
902 /* If a table does not exist then do nothing and return */ 416 switch (*c) {
903 if(!curr_table) { 417 case '-':
904#ifdef DEBUG 418 add = 0;
905 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table not found, leaving.\n"); 419 c++;
906#endif 420 break;
907 spin_unlock_bh(&recent_lock); 421 case '+':
908 return; 422 c++;
423 default:
424 add = 1;
425 break;
909 } 426 }
427 addr = in_aton(c);
910 428
911 curr_table->count--; 429 spin_lock_bh(&recent_lock);
912 430 e = recent_entry_lookup(t, addr, 0);
913 /* If count is still non-zero then there are still rules referenceing it so we do nothing */ 431 if (e == NULL) {
914 if(curr_table->count) { 432 if (add)
915#ifdef DEBUG 433 recent_entry_init(t, addr, 0);
916 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, non-zero count, leaving.\n"); 434 } else {
917#endif 435 if (add)
918 spin_unlock_bh(&recent_lock); 436 recent_entry_update(t, e);
919 return; 437 else
438 recent_entry_remove(t, e);
920 } 439 }
921
922#ifdef DEBUG
923 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, zero count, removing.\n");
924#endif
925
926 /* Count must be zero so we remove this table from the list */
927 if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
928
929 spin_unlock_bh(&recent_lock); 440 spin_unlock_bh(&recent_lock);
441 return size;
442}
930 443
931 /* lock to make sure any late-runners still using this after we removed it from 444static struct file_operations recent_fops = {
932 * the list finish up then remove everything */ 445 .open = recent_seq_open,
933 spin_lock_bh(&curr_table->list_lock); 446 .read = seq_read,
934 spin_unlock_bh(&curr_table->list_lock); 447 .write = recent_proc_write,
935 448 .release = seq_release_private,
936#ifdef CONFIG_PROC_FS 449 .owner = THIS_MODULE,
937 if(curr_table->status_proc) remove_proc_entry(curr_table->name,proc_net_ipt_recent); 450};
938#endif /* CONFIG_PROC_FS */ 451#endif /* CONFIG_PROC_FS */
939 vfree(curr_table->table[0].last_pkts);
940 vfree(curr_table->table);
941 vfree(curr_table->hash_table);
942 vfree(curr_table->time_info);
943 vfree(curr_table);
944
945#ifdef DEBUG
946 if(debug) printk(KERN_INFO RECENT_NAME ": destroy() left.\n");
947#endif
948 452
949 return;
950}
951
952/* This is the structure we pass to ipt_register to register our
953 * module with iptables.
954 */
955static struct ipt_match recent_match = { 453static struct ipt_match recent_match = {
956 .name = "recent", 454 .name = "recent",
957 .match = match, 455 .match = ipt_recent_match,
958 .matchsize = sizeof(struct ipt_recent_info), 456 .matchsize = sizeof(struct ipt_recent_info),
959 .checkentry = checkentry, 457 .checkentry = ipt_recent_checkentry,
960 .destroy = destroy, 458 .destroy = ipt_recent_destroy,
961 .me = THIS_MODULE 459 .me = THIS_MODULE,
962}; 460};
963 461
964/* Kernel module initialization. */
965static int __init ipt_recent_init(void) 462static int __init ipt_recent_init(void)
966{ 463{
967 int err, count; 464 int err;
968 465
969 printk(version); 466 if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255)
970#ifdef CONFIG_PROC_FS 467 return -EINVAL;
971 proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net); 468 ip_list_hash_size = 1 << fls(ip_list_tot);
972 if(!proc_net_ipt_recent) return -ENOMEM;
973#endif
974
975 if(ip_list_hash_size && ip_list_hash_size <= ip_list_tot) {
976 printk(KERN_WARNING RECENT_NAME ": ip_list_hash_size too small, resetting to default.\n");
977 ip_list_hash_size = 0;
978 }
979
980 if(!ip_list_hash_size) {
981 ip_list_hash_size = ip_list_tot*3;
982 count = 2*2;
983 while(ip_list_hash_size > count) count = count*2;
984 ip_list_hash_size = count;
985 }
986
987#ifdef DEBUG
988 if(debug) printk(KERN_INFO RECENT_NAME ": ip_list_hash_size: %d\n",ip_list_hash_size);
989#endif
990 469
991 err = ipt_register_match(&recent_match); 470 err = ipt_register_match(&recent_match);
471#ifdef CONFIG_PROC_FS
992 if (err) 472 if (err)
993 remove_proc_entry("ipt_recent", proc_net); 473 return err;
474 proc_dir = proc_mkdir("ipt_recent", proc_net);
475 if (proc_dir == NULL) {
476 ipt_unregister_match(&recent_match);
477 err = -ENOMEM;
478 }
479#endif
994 return err; 480 return err;
995} 481}
996 482
997/* Kernel module destruction. */ 483static void __exit ipt_recent_exit(void)
998static void __exit ipt_recent_fini(void)
999{ 484{
485 BUG_ON(!list_empty(&tables));
1000 ipt_unregister_match(&recent_match); 486 ipt_unregister_match(&recent_match);
1001 487#ifdef CONFIG_PROC_FS
1002 remove_proc_entry("ipt_recent",proc_net); 488 remove_proc_entry("ipt_recent", proc_net);
489#endif
1003} 490}
1004 491
1005/* Register our module with the kernel. */
1006module_init(ipt_recent_init); 492module_init(ipt_recent_init);
1007module_exit(ipt_recent_fini); 493module_exit(ipt_recent_exit);