My first netfilter module, seeking feedback

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello. I wrote this netfilter module for Linux 2.6, its an experiment to try and mitigate the effect of a spoofed syn attack on a server, and also to learn more about the netfilter hooks.

With this module loaded, all syn packets are distrusted. My idea was to to mitigate the effect of a spoofed syn attack by discarding all initial SYN packets from a given client address, and permit SYN packets which arrive from client addresses which were recently discarded. So, we have two lists- trusted and pending trusted. The trusted list is full of client addresses which reconnected after being discarded. This is similar to email greylisting. Both of the lists are periodically cleaned up and there are some tunable values such as the number of necessary syn reconnects to become trusted and how often to clean each list.

Looking for any feedback on my overall idea or the code implementation. My module code is attached.

Thank-yo,

--
--------------------------------------------------
| Rick J. Blundell                               |
| <rickb@xxxxxxxxxxxx>                           |
--------------------------------------------------
| Mac OS X proves that it's easier to make UNIX  |
| pretty than it is to make Windows secure.      |
--------------------------------------------------
#include <linux/module.h>
#include <linux/init.h>
#include <linux/stat.h> // for proc permissions
#include <linux/netdevice.h> // struct net_device, net_device functions
#include <linux/skbuff.h> // struct sk_buff
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/string.h>
#include <linux/time.h>
#include <net/tcp.h>
#include <linux/time.h>

                        #define TCPCB_FLAG_FIN          0x01
                        #define TCPCB_FLAG_SYN          0x02
                        #define TCPCB_FLAG_RST          0x04
                        #define TCPCB_FLAG_PSH          0x08
                        #define TCPCB_FLAG_ACK          0x10
                        #define TCPCB_FLAG_URG          0x20
                        #define TCPCB_FLAG_ECE          0x40
                        #define TCPCB_FLAG_CWR          0x80
		     #define REQUIRED_SYNS_CEIL 10
		     #define HERTZ    1000

		MODULE_AUTHOR("Rick Blundell");
		MODULE_LICENSE("RJB");
		MODULE_DESCRIPTION("netfilter syn attack evade");
		MODULE_SUPPORTED_DEVICE("none");

struct timer_list my_timer;
static struct proc_dir_entry *procentry;
static struct authorizedsrc {
        struct authorizedsrc *next;
        __be32 ip;
        int timestamp;
        int syn_count;
};

unsigned int hookfn(unsigned int hooknum,
                struct sk_buff **skb,
                const struct net_device *in,
                const struct net_device *out,
                int (*okfn)(struct sk_buff *));
static struct nf_hook_ops hook_in = {
        .list = {NULL, NULL},
        .hook = hookfn,
        .pf = PF_INET,
        .hooknum = NF_IP_LOCAL_IN
};

struct authorizedsrc *head_auth_pending=NULL;
struct authorizedsrc *head_auth=NULL;



int timeout=1;
int required_syns=5;

/* FXN DEFINITIONS */
struct authorizedsrc * remove_ip_from_list(struct authorizedsrc *head,__be32 ip);
int authoized_src(struct authorizedsrc **head,__be32 src_ip);
void authorize_src(struct authorizedsrc **head,struct authorizedsrc **head2,__be32 src_ip, int required_syns);
void free_authorized_src(struct authorizedsrc *head);
static void my_timer_func(struct authorizedsrc **head);
static struct proc_dir_entry *proc_authorizedsrcs;

int read_proc_auth_pending(char *buffer, char **start, off_t offset, int count, int *peof, void *dat);
int read_proc_auth(char *buffer, char **start, off_t offset, int count, int *peof, void *dat);
static struct proc_dir_entry *procentry_timeout;
int write_timeout_proc(struct file *file, const char __user *buffer, unsigned long count, void *data);
int read_timeout_proc(char *buffer, char **start, off_t offset, int count, int *peof, void *dat);
static struct proc_dir_entry *procentry_required_syns;
int write_required_syns_proc(struct file *file, const char __user *buffer, unsigned long count, void *data);
int read_required_syns_proc(char *buffer, char **start, off_t offset, int count, int *peof, void *dat);
__be32 getip(const char* buff);


/* init FXN! */
static int __init mod_init(void) {
        nf_register_hook(&hook_in);
        procentry_auth = create_proc_entry("auth", 0644, proc_net_netfilter_synevade);
        if (procentry_auth) {
                procentry_auth->read_proc = read_proc_auth;
                procentry_auth->data = NULL;
        }
        procentry_auth_pending = create_proc_entry("auth_pending", 0644, proc_net_netfilter);
        if (procentry_auth_pending) {
                procentry->read_proc = read_proc_auth_pending;
                procentry->data = NULL;
        }
        procentry_timeout=create_proc_entry("src_timeout", 0644, proc_net_netfilter);
        if (procentry_timeout){
                procentry_timeout->write_proc=write_timeout_proc;
                procentry_timeout->read_proc=read_timeout_proc;
                procentry_timeout->data=NULL;
        }
        procentry_required_syns=create_proc_entry("required_syns", 0644, proc_net_netfilter);
        if(procentry_required_syns){
                procentry_required_syns->write_proc=write_required_syns_proc;
                procentry_required_syns->read_proc=read_required_syns_proc;
                procentry_required_syns->data=NULL;
        }

        printk("synattack_evader loaded\n");
        init_timer(&my_timer);
        my_timer.function = my_timer_func;
        my_timer.data = (struct authorized_src *) head_auth_pending;
        my_timer.expires = jiffies + HERTZ*timeout;
        add_timer(&my_timer);
        return 0;
}


/* EXIT AND CLEANUP! */
static void __exit mod_exit(void) {
        del_timer(&my_timer);
        nf_unregister_hook(&hook_in);
        remove_proc_entry("authorized_srcs", proc_net_netfilter);
        remove_proc_entry("src_timeout", proc_net_netfilter);
        remove_proc_entry("required_syns", proc_net_netfilter);
        printk("synattack_evader unloaded\n");
}

/* ADD THEM TO KERNEL! */
module_init( mod_init );
module_exit( mod_exit );

/* RUNS On EACH PACKET */
unsigned int hookfn(unsigned int hooknum,
                struct sk_buff **skb,
                const struct net_device *in,
                const struct net_device *out,
                int (*okfn)(struct sk_buff *)) {

	/* DEFAULT POLICY IS ACCEPT THE PACKET */
	unsigned int rc = NF_ACCEPT;
	struct sk_buff *buff = *skb;
	struct hostentry *host;
	__be32 src_ip = 0;
	struct tcp_skb_cb *tcb = TCP_SKB_CB(buff);

	switch (hooknum) {
		/* INPUT TABLE PACKET */
		case NF_IP_LOCAL_IN:
		//Address of client
		src_ip=buff->nh.iph->saddr;
		//printk("source is %u.%u.%u.%u\n",NIPQUAD(src_ip));
		//CHECK IF THIS PACKET IS A SYN
		if (tcb->flags = TCPCB_FLAG_SYN) {
			//printk("syn is %u.%u.%u.%u\n",NIPQUAD(src_ip));
			//check if in authorized_src list
			if( authorized_src(&head_auth,src_ip) ){
				//allow
			}else{
				//deny and add to list
				//rc=NF_DROP;
				printk("dropping syn from %u.%u.%u.%u\n",NIPQUAD(src_ip));
				authorize_src(&head_auth_pending,&head_auth,src_ip,required_syns);
			}
		}
		break;
	}
	return rc;
}


/* Check if the source address is in the table "required" times */
int authorized_src( struct authorizedsrc **head,__be32 src_ip){
	struct authorizedsrc *tmp=*head;
	while(tmp!=NULL){
		//printk("checking if \"%u.%u.%u.%u\" = \"%u.%u.%u.%u\" \n",NIPQUAD(tmp->ip),NIPQUAD(src_ip));
		if(tmp->ip==src_ip){ 
			//printk("\tauthoized!..\n");
			return 1;
		}
		tmp=tmp->next;
	}
	//printk("\tnot authorized..\n");
	return 0;
}

/* Run cleanup on the list */
void free_authorized_src(struct authorizedsrc *head){
	struct authorizedsrc *tmp=head;
	while(head!=NULL){
		tmp=head->next;
		kfree(head);
		head=tmp;
	}
}

/* add src_ip to auth list or increment syn count if already in the list */
void authorize_src(struct authorizedsrc **head_auth_pending, struct authorizedsrc **head_auth,__be32 src_ip, int required_syns){
	//printk("in authorize_src..\n");
	if(authorized_src(head_auth_pending,src_ip)){
		//printk("checking authorized in authorize.\n");
		//already in the list.
	
		//find it in the list
		
		struct authorizedsrc *tmp;
		tmp=*head_auth_pending;
		int count=0;
		while(tmp!=NULL){
			if(src_ip == tmp->ip){ 
				//found it!
				if(tmp->syn_count >= required_syns-1){
					//add to head_auth and remove from head_auth_pending
					struct authorizedsrc *tmp;
			                if ((tmp = kmalloc(sizeof(*tmp), GFP_KERNEL)) ) {
                		        	tmp->ip=src_ip;
               			         	struct timeval tv;
                		        	do_gettimeofday(&tv);
                		        	tmp->timestamp=tv.tv_sec;
                		        	tmp->syn_count=1;
                		        	printk("added ip %u.%u.%u.%u to authed!\n",NIPQUAD(src_ip));
                		       	 	tmp->next=*head_auth;
                		        	*head_auth=tmp;
               				 }
					//remov it from head_auth_pending...
					remove_ip_from_list(head_auth_pending,src_ip);
               				return 1;
				}else{
			printk("incrementing syn_count on ip %u.%u.%u.%u\n",NIPQUAD(src_ip));
					tmp->syn_count++;
				}
				return 0;
			}
			tmp=tmp->next;
		}
		return 1;
	}else{
		//not in the list. insert it into the list
		struct authorizedsrc *tmp;
		if ((tmp = kmalloc(sizeof(*tmp), GFP_KERNEL)) ) {
			tmp->ip=src_ip;
			struct timeval tv;
			do_gettimeofday(&tv);
			tmp->timestamp=tv.tv_sec;
			tmp->syn_count=1;
			printk("added ip %u.%u.%u.%u\n",NIPQUAD(src_ip));
			tmp->next=*head_auth_pending;
			*head_auth_pending=tmp;
		}
		return 1;
	}
}
struct authorizedsrc * remove_ip_from_list(struct authorizedsrc *head,__be32 ip){
	if(head==NULL) return NULL;
	if(head->ip==ip){
		struct authorizedsrc * save;
		save=head->next;
		kfree(head);
		return save;
	}
	head->next=remove_ip_from_list(head->next,ip);
	return head;	
}
int read_proc_auth_pending(char *buff, char **start, off_t offset, int count, int *peof, void *dat) {
	int rc=0;
	struct authorizedsrc *tmp=head_auth_pending;
	while(tmp!=NULL){
		if(tmp==NULL){*peof=1;return rc;}
		rc += snprintf (buff+rc, count-rc, "%u.%u.%u.%u:%d:%d\n",NIPQUAD(tmp->ip),tmp->syn_count,tmp->timestamp);
		tmp=tmp->next;
	}
	*peof=1;
	return rc;
}
int read_proc_auth(char *buff, char **start, off_t offset, int count, int *peof, void *dat) {
	int rc=0;
	struct authorizedsrc *tmp=head_auth;
	while(tmp!=NULL){
		if(tmp==NULL){*peof=1;return rc;}
		rc += snprintf (buff+rc, count-rc, "%u.%u.%u.%u:%d:%d\n",NIPQUAD(tmp->ip),tmp->syn_count,tmp->timestamp);
		tmp=tmp->next;
	}
	*peof=1;
	return rc;
}

int read_timeout_proc(char *buff, char **start, off_t offset, int count, int *peof, void *dat) {
	int rc=0;
	rc+=snprintf(buff+rc,count-rc,"%d\n",timeout);
	*peof=1;
	return rc;
}
int write_timeout_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) {
	char *buff;
	int rc;
	buff = kmalloc(count+1, GFP_KERNEL);
	if(buff) {
		rc = copy_from_user(buff, buffer, count);
		timeout=simple_strtol(buff,NULL,0);
		kfree(buff);
	}else {  
		return -ENOMEM;
	}
	return count - rc;
}
int read_required_syns_proc(char *buff, char **start, off_t offset, int count, int *peof, void *dat) {
	int rc=0;
	rc+=snprintf(buff+rc,count-rc,"%d\n",required_syns);
	*peof=1;
	return rc;
}
int write_required_syns_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) {
	char *buff;
	int rc;
	int tmp_rs;
	buff = kmalloc(count+1, GFP_KERNEL);
	if(buff) {
		rc = copy_from_user(buff, buffer, count);
		tmp_rs=simple_strtol(buff,NULL,0);
		if(tmp_rs <= REQUIRED_SYNS_CEIL) required_syns=tmp_rs;
		kfree(buff);
	}else {  
		return -ENOMEM;
	}
	return count - rc;
}
static void my_timer_func(struct authorizedsrc **head){
	printk("reaping..\n");
//	free_authorized_src(head);
	my_timer.expires = jiffies + HERTZ*timeout;
	add_timer(&my_timer);
}


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux