📄 ipt_recent.c
字号:
/* 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>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include <linux/ctype.h>#include <linux/ip.h>#include <linux/vmalloc.h>#include <linux/moduleparam.h>#include <linux/netfilter_ipv4/ip_tables.h>#include <linux/netfilter_ipv4/ipt_recent.h>#undef DEBUG#define HASH_LOG 9/* Defaults, these can be overridden on the module command-line. */static int ip_list_tot = 100;static int ip_pkt_list_tot = 20;static int ip_list_hash_size = 0;static int ip_list_perms = 0644;#ifdef DEBUGstatic int debug = 1;#endifstatic 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, int, 0400);module_param(ip_pkt_list_tot, int, 0400);module_param(ip_list_hash_size, int, 0400);module_param(ip_list_perms, int, 0400);#ifdef DEBUGmodule_param(debug, int, 0600);MODULE_PARM_DESC(debug,"debugging level, defaults to 1");#endifMODULE_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;};/* 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;#ifdef CONFIG_PROC_FS struct proc_dir_entry *status_proc;#endif /* CONFIG_PROC_FS */};/* 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 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 intmatch(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, 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 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){ 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; } } *start = buffer + (offset - begin); len -= (offset - begin); if(len > length) len = length; spin_unlock_bh(&curr_table->list_lock); return len;}/* 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 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; } 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; }#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); if(!addr && check_set == IPT_RECENT_SET) return used;#ifdef DEBUG if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used);#endif /* 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; } 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,info,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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -