`
thecloud
  • 浏览: 882480 次
文章分类
社区版块
存档分类
最新评论

Linux内核--基于Netfilter的内核级包过滤防火墙实现

 
阅读更多

测试内核版本:Linux Kernel 2.6.35----Linux Kernel 3.2.1

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7572382

更多请查看专栏http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明


知识基础:本防火墙的开发基于对Linux内核网络栈有个良好的概念,本人对网络栈的分析是基于早期版本(Linux 1.2.13),在明确了网络栈架构的前提下,上升一步分析高级版本内核中的Netfilter防火墙实现原理,然后进行模块或内核编程,开发一款基于包过滤的个人防火墙。

包过滤防火墙:包过滤防火墙是用一个软件查看所流经的数据包的包头(header),由此决定整个包的命运。它可能会决定丢弃(DROP)这个包,可能会接受(ACCEPT)这个包(让这个包通过),也可能执行其它更复杂的动作。工作于网络层,能对IP数据报进行首部检查。例如:IP源地址,目的地址,源端口和目的端口等。

本防火墙的包过滤功能如下:  

* 拒绝来自某主机或某网段的所有连接。
  * 允许来自某主机或某网段的所有连接。
  * 拒绝来自某主机或某网段的指定端口的连接。
  * 允许来自某主机或某网段的指定端口的连接。
  * 拒绝发去某主机或某网段的所有连接。
  * 允许发去某主机或某网段的所有连接。
  * 拒绝发去某主机或某网段的指定端口的连接。
  * 允许发去某主机或某网段的指定端口的连接。

Netfilter框架是Linux内核分析和过滤特定协议数据包处理框架,为其他模块动态参与网络层数据包处理提供了方便的途径。

该防火墙的总体结构如下:


本防火墙的简单功能就是检查数据包是否符合过滤的条件,如果不符合就舍弃(Drop),否则就接受(Accept),这里定义八个链表头结点

struct ip_node  ip_allowed_in_node_head;/*允许的远程主机或网络IP地址头节点*/
struct ip_node  ip_denied_in_node_head;/*拒绝的远程主机或网络IP地址头节点*/
struct ip_node  ip_allowed_out_node_head;/*允许的本地主机或网络IP地址头节点*/
struct ip_node  ip_denied_out_node_head;/*拒绝的本地主机或网络IP地址头节点*/

struct port_node port_allowed_in_node_head;/*允许的远程主机或网络传输层端口号头节点*/
struct port_node port_denied_in_node_head;/*拒绝的远程主机或网络传输层端口号头节点*/
struct port_node port_allowed_out_node_head;/*允许的本地主机或网络传输层端口号头节点*/
struct port_node port_denied_out_node_head;/*拒绝的本地主机或网络传输层端口号头节点*/

用于保存配置文件中的地址或端口信息。

定义两个钩子函数hook_func_in和hook_func_out,分别将其挂载到INET协议族的入口NF_INET_LOCAL_IN和出口NF_INET_LOCAL_OUT:

static struct nf_hook_ops my_netfilter[] =
{
	{
		.hook		=hook_func_in,
		.owner		=THIS_MODULE,
		.pf		=PF_INET,
		.hooknum	=NF_INET_LOCAL_IN,
		.priority	=100
	},
	{
		.hook		=hook_func_out,
		.owner		=THIS_MODULE,
		.pf		=PF_INET,
		.hooknum	=NF_INET_LOCAL_OUT,
		.priority	=100
	}
};


说明一下自己定义的一些宏和引用的头文件:

#ifndef MODULE
#define MODULE
#endif

#ifndef __KERNEL__
#define __KERNEL__
#endif
//#define NET_DOWN
#define MY_FIREWALL_DEBUG

#include <asm/system.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>

#include <linux/net.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/inet.h>


#include <net/ip.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/icmp.h>
#include <net/raw.h>
#include <net/checksum.h>
#include <linux/netfilter_ipv4.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/igmp.h>

#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>

#define YES 1
#define NO 0

#define IP_MAX_LEN 20
#define PORT_MAX_LEN 20

#define ALLOWED_IP_IN 0
#define DENIED_IP_IN 1
#define ALLOWED_IP_OUT 2
#define DENIED_IP_OUT 3

#define ALLOWED_PORT_IN 0
#define DENIED_PORT_IN 1
#define ALLOWED_PORT_OUT 2
#define DENIED_PORT_OUT 3

#define ALLOWED_IN_IP_CONF_FILE_DIR "/etc/my_firewall/ip_allowed_in"
#define DENIED_IN_IP_CONF_FILE_DIR "/etc/my_firewall/ip_denied_in"
#define ALLOWED_IN_PORT_CONF_FILE_DIR "/etc/my_firewall/port_allowed_in"
#define DENIED_IN_PORT_CONF_FILE_DIR "/etc/my_firewall/port_denied_in"

#define ALLOWED_OUT_IP_CONF_FILE_DIR "/etc/my_firewall/ip_allowed_out"
#define DENIED_OUT_IP_CONF_FILE_DIR "/etc/my_firewall/ip_denied_out"
#define ALLOWED_OUT_PORT_CONF_FILE_DIR "/etc/my_firewall/port_allowed_out"
#define DENIED_OUT_PORT_CONF_FILE_DIR "/etc/my_firewall/port_denied_out"

//DEFINE FOR WORK_MODE

/*不工作状态,默认*/
#define MODE_FREE 0
/*允许来自某主机或某网段的所有连接*/
#define MODE_IP_ONLY_ALLOWED_IN 1

/*拒绝来自某主机或某网段的所有连接*/
#define MODE_IP_ONLY_DENIED_IN 2

/*允许来自某主机或某网段指定端口的连接*/
#define MODE_IP_PORT_ALLOWED_IN 3

/*拒绝来自某主机或某网段的指定端口的连接*/
#define MODE_IP_PORT_DENIED_IN 4

/*允许本地主机或本地网络与其他主机或网络的所有连接*/
#define MODE_IP_ONLY_ALLOWED_OUT 5

/*拒绝本地主机或本地网络与其他主机或网络的所有连接*/
#define MODE_IP_ONLY_DENIED_OUT 6

/*允许本地主机或网络与其他主机或其他网络的指定端口的连接*/
#define MODE_IP_PORT_ALLOWED_OUT 7

/*拒绝本地主机或网络与其他主机或其他网络的指定端口的连接*/
#define MODE_IP_PORT_DENIED_OUT 8


下面是防火墙模块的初始化函数:

int init_firewall()
{
	(&ip_allowed_in_node_head)->next = NULL;
	(&ip_denied_in_node_head)->next = NULL;
	(&port_allowed_in_node_head)->next = NULL;
	(&port_denied_in_node_head)->next = NULL;

	(&ip_allowed_out_node_head)->next = NULL;
	(&ip_denied_out_node_head)->next = NULL;
	(&port_allowed_out_node_head)->next = NULL;
	(&port_denied_out_node_head)->next = NULL;

	switch(work_mode)
	{
		case MODE_IP_ONLY_ALLOWED_IN:
			open_ip_cfg_file(ALLOWED_IN_IP_CONF_FILE_DIR,ALLOWED_IP_IN);
			break;
		case MODE_IP_ONLY_DENIED_IN:
			open_ip_cfg_file(DENIED_IN_IP_CONF_FILE_DIR,DENIED_IP_IN);
			break;
		case MODE_IP_PORT_ALLOWED_IN:
			open_port_cfg_file(ALLOWED_IN_PORT_CONF_FILE_DIR,ALLOWED_PORT_IN);
			open_ip_cfg_file(ALLOWED_IN_IP_CONF_FILE_DIR,ALLOWED_IP_IN);
			break;
		case MODE_IP_PORT_DENIED_IN:
			open_port_cfg_file(DENIED_IN_PORT_CONF_FILE_DIR,DENIED_PORT_IN);
			open_ip_cfg_file(ALLOWED_IN_IP_CONF_FILE_DIR,ALLOWED_IP_IN);
			break;
		case MODE_IP_ONLY_ALLOWED_OUT:
			open_ip_cfg_file(ALLOWED_OUT_IP_CONF_FILE_DIR,ALLOWED_IP_OUT);
			break;
		case MODE_IP_ONLY_DENIED_OUT:
			open_ip_cfg_file(DENIED_OUT_IP_CONF_FILE_DIR,DENIED_IP_OUT);
			break;
		case MODE_IP_PORT_ALLOWED_OUT:
			open_port_cfg_file(ALLOWED_OUT_PORT_CONF_FILE_DIR,ALLOWED_PORT_OUT);
			open_ip_cfg_file(ALLOWED_OUT_IP_CONF_FILE_DIR,ALLOWED_IP_OUT);
			break;
		case MODE_IP_PORT_DENIED_OUT:
			open_port_cfg_file(DENIED_OUT_PORT_CONF_FILE_DIR,DENIED_PORT_OUT);
			open_ip_cfg_file(ALLOWED_OUT_IP_CONF_FILE_DIR,ALLOWED_IP_OUT);
			break;
		default:break;
	}
	
	
	
	//open_port_cfg_file(DENIED_PORT_CONF_FILE,DENIED_PORT);
	nf_register_hook(&my_netfilter[0]);
	nf_register_hook(&my_netfilter[1]);
	printk("INIT my firewall OK!\n");
	return 0;
}

先从文件读取配置文件,加载到内核,然后注册钩子操作结构my_netfilter[0],my_netfilter[1]

下图是Netfilter的IPV4下的结构

上述的两个函数挂载位置NF_INET_LOCAL_IN和NF_INET_LOCAL_OUT,分别处理从本机发出和到达本机的数据包。

下面就是挂载到这两个挂载点的钩子函数,用于数据包的检查

static unsigned int hook_func_in(unsigned int hook,
				struct sk_buff *skb,
				const struct net_device *in,
				const struct net_device *out,
				int (*okfn)(struct sk_buff *))
{
#ifdef NET_DOWN
	return NF_DROP;
#else
	struct iphdr *iph = ip_hdr(skb);
	__be32 saddr = ntohl(iph->saddr);
	
	
	__be16 sport = 0,dport = 0;
	if(trans_port(iph,skb,&sport,&dport) == NO && \
		work_mode == MODE_IP_PORT_ALLOWED_IN || \
			work_mode == MODE_IP_PORT_DENIED_IN)
		return NF_ACCEPT;

#ifdef MY_FIREWALL_DEBUG
	__be32 daddr = ntohl(iph->daddr);
	printk("saddr= %u : %u  daddr= %u : %d\n",saddr,sport,daddr,dport);
#endif

	switch(work_mode)
	{
		case MODE_FREE:
			return NF_ACCEPT;
		case MODE_IP_ONLY_ALLOWED_IN:
			if(ip_in_cfg_file(saddr,&ip_allowed_in_node_head))
				return NF_ACCEPT;
			else return NF_DROP;
			break;
		case MODE_IP_ONLY_DENIED_IN:
			if(ip_in_cfg_file(saddr,&ip_denied_in_node_head))
				return NF_DROP;
			else return NF_ACCEPT;
			break;
		case MODE_IP_PORT_ALLOWED_IN:
			if(ip_in_cfg_file(saddr,&ip_allowed_in_node_head) && \
				port_in_cfg_file(sport,&port_allowed_in_node_head))
				return NF_ACCEPT;
			else return NF_DROP;
			break;
		case MODE_IP_PORT_DENIED_IN:
			if(ip_in_cfg_file(saddr,&ip_allowed_in_node_head) && \
				!port_in_cfg_file(sport,&port_denied_in_node_head))
				return NF_ACCEPT;
			else return NF_DROP;
			break;
		default:
			return NF_DROP;
			break;
	}
#endif

}

static unsigned int hook_func_out(unsigned int hook,
				struct sk_buff *skb,
				const struct net_device *in,
				const struct net_device *out,
				int (*okfn)(struct sk_buff *))
{
#ifdef NET_DOWN
	return NF_DROP;
#else
	struct iphdr *iph = ip_hdr(skb);
	
	__be32 daddr = ntohl(iph->daddr);
	
	__be16 sport = 0,dport = 0;

	if(trans_port(iph,skb,&sport,&dport) == NO && \
		work_mode == MODE_IP_PORT_ALLOWED_OUT && \
			work_mode == MODE_IP_PORT_DENIED_OUT)
		return NF_ACCEPT;

#ifdef MY_FIREWALL_DEBUG
	__be32 saddr = ntohl(iph->saddr);
	printk("saddr= %u : %u  daddr= %u : %d\n",saddr,sport,daddr,dport);
#endif

	switch(work_mode)
	{
		case MODE_FREE:
			return NF_ACCEPT;
		case MODE_IP_ONLY_ALLOWED_OUT:
			if(ip_in_cfg_file(daddr,&ip_allowed_out_node_head))
				return NF_ACCEPT;
			else return NF_DROP;
			break;
		case MODE_IP_ONLY_DENIED_OUT:
			if(ip_in_cfg_file(daddr,&ip_denied_out_node_head))
				return NF_DROP;
			else return NF_ACCEPT;
			break;
		case MODE_IP_PORT_ALLOWED_OUT:
			if(ip_in_cfg_file(daddr,&ip_allowed_out_node_head) && \
				port_in_cfg_file(dport,&port_allowed_out_node_head))
				return NF_ACCEPT;
			else return NF_DROP;
			break;
		case MODE_IP_PORT_DENIED_OUT:
			if(ip_in_cfg_file(daddr,&ip_allowed_out_node_head) && \
				!port_in_cfg_file(dport,&port_denied_out_node_head))
				return NF_ACCEPT;
			else return NF_DROP;
			break;
		default:
			return NF_DROP;
			break;
	}
#endif

}

下面是打开文件并读取信息的函数,这里以打开IP地址的配置文件为例

static int open_ip_cfg_file(char * file_dir,int flag)
{
	struct file * filp = NULL;
	
	char str[IP_MAX_LEN];
	int i = 0;

	if((filp = filp_open(file_dir,O_RDONLY,0)) < 0) 
		return NO;

	mm_segment_t fs;
	fs = get_fs();
	set_fs(KERNEL_DS);


	struct ip_node * work = NULL;

	while((filp->f_op->read(filp,&str[i],1,&filp->f_pos)) == 1)
	{
		if(str[i] == '\n')//next line
		{
			str[i] = '\0';//the end of a string
			i = 0;
#ifdef MY_FIREWALL_DEBUG
			printk("%s\n",str);
#endif
			work = (struct ip_node *)kmalloc(sizeof(ip_allowed_in_node_head),GFP_ATOMIC);
		
			
			if( ip_to_unsigned(str,&(work->ip_start),&(work->ip_end)) == 0 )
				return NO;

			switch(flag)
			{
				case ALLOWED_IP_IN:
					work->next = (&ip_allowed_in_node_head)->next;
					(&ip_allowed_in_node_head)->next = work;//head insert
					break;
				case DENIED_IP_IN:
					work->next = (&ip_denied_in_node_head)->next;
					(&ip_denied_in_node_head)->next = work;//head insert
					break;
				case ALLOWED_IP_OUT:
					work->next = (&ip_allowed_out_node_head)->next;
					(&ip_allowed_out_node_head)->next = work;//head insert
					break;
				case DENIED_IP_OUT:
					work->next = (&ip_denied_out_node_head)->next;
					(&ip_denied_out_node_head)->next = work;//head insert
					break;
				default:break;
			}
				
			filp->f_op->read(filp,&str[0],1,&filp->f_pos);//eat the '\r'
		}
		

		if(i > IP_MAX_LEN) return NO;
		i++;
		
	}
	return YES;
}

这里配置文件中不仅支持具体的IP地址,还支持IP地址网段,例如

192.168.1.1

192.168.1.*

192.168.*.*

192.*.*.*

下面是处理函数

/************************************************
str:The IP Address like 192.168.1.1
start:The pointer to the start IP
end:The pointer to the end IP
***********************************************/
static int ip_to_unsigned(const char * str,unsigned int * start,unsigned int * end)
{
	char cache[4][4];
	/*split the IP address*/
	int i;
	int k = 0;
	int j = 0;
	for(i = 0;str[i] != '\0';i++)
	{
		cache[j][k] = str[i];
		if(str[i] == '.')
		{
			cache[j][k] = '\0';
			k = 0;
			j++;
			
		}
		else k++;
		if(j > 3) return NO;
	}
	cache[3][k] = '\0';

	short int a[4];
	for(i = 0;i < 4;i++)
	{
		if(cache[i][0] != '*')
		{
			a[i] = (short)simple_strtol(cache[i],NULL,0);
			if(a[i] < 0 || a[i] > 255) return NO;
		}
		else
		{
			break;
		}
	}

	switch(i)
	{
		case 4:/*Specific IP Address eg.  192.168.1.1   */
			*start = *end = (a[0]<<24) + (a[1]<<16) + (a[2]<<8 )+a[3];
			break;
		case 3:/*  eg. 192.168.1.*   */
			*start = (a[0]<<24) + (a[1]<<16) + (a[2]<<8);
			*end = *start + (1<<8) - 1;
			break;
		case 2:/*  eg. 192.168.*.*   */
			*start = (a[0]<<24) + (a[1]<<16);
			*end = *start + (1<<16) - 1;
			break;
		case 1:/*  eg. 192.*.*.*    */
			*start = (a[0]<<24);
			*end = *start + (1<<24) - 1;
			break;
		default:
			*start = 0;
			*end = (1<<32) - 1;
			break;
	}

	return 	YES;
}

模块的移除函数

void remove_firewall()
{
	free_ip_list(&ip_allowed_in_node_head);
	free_ip_list(&ip_denied_in_node_head);

	free_ip_list(&ip_allowed_out_node_head);
	free_ip_list(&ip_denied_out_node_head);

	free_port_list(&port_allowed_in_node_head);
	free_port_list(&port_denied_in_node_head);

	free_port_list(&port_allowed_out_node_head);
	free_port_list(&port_denied_out_node_head);

	nf_unregister_hook(&my_netfilter[0]);
	nf_unregister_hook(&my_netfilter[1]);
	printk("CLEAN up my firewall OK!\n");
}

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("yming0221@gmail.com");
该防火墙支持命令参数设置,根据参数设置防火墙的工作模式,只需定义和声明

static int work_mode = 0;//default mode
module_param(work_mode,int,S_IRUGO);

目前测试,防火墙正常工作。

可以看到数据包能到达网络层,但由于防火墙启用的相应检查规则,浏览器等应用层软件无法联网,数据包被丢弃。




分享到:
评论

相关推荐

    Linux内核--基于Netfilter的内核级包过滤防火墙实现.doc

    本人对网络栈的分析是基于早期版本(Linux 1.2.13),在明确了网络栈架构的前提下,上升一步分析高级版本内核中的Netfilter防火墙实现原理,然后进行模块或内核编程,开发一款基于包过滤的个人防火墙。 包过滤防火墙...

    Linux内核Netfilter包过滤防火墙的设计与实现.pdf

    Linux内核Netfilter包过滤防火墙的设计与实现.pdf

    内核防火墙netfilter

    能(包过滤,地址伪装,透明代理),又避免了商业防火墙那高的惊人的价格。如果 你用的是某款国产防火墙,那么十有八九你实际在受到ipchains(有些甚至是2.0系列 中ipfwadm)的保护:-).在未来的2.4内核中,被称为...

    基于Netfilter的包过滤状态防火墙(C语言)

    在内核基于 NETFILTER 实现包过滤、NAT 等功能 设计思路 1、Node + MySQL Web 控制面板使用用户程序提供的命令行管理语法实现便利的的 B/S 架构 UI 2、用户态程序 用户程序向下使用内核驱动模块提供的接口,向上为...

    基于Linux的包过滤防火墙设计与实现

    在Linux下,基于netfilter框架设计的包过滤防火墙。该防火墙分为用户空间和内核空间两个部分的内容。

    linux下iptables防火墙学习

    Linux中的防火墙是由netfilter/iptables网络构架实现的包过滤防火墙,可以实现大部分硬件防火墙的功能,所以可以在企业的应用方案中可以作为硬件防火墙的替代品。 如果Linux的内核是在2.4以上的版本,都可以使用...

    Linux防火墙

     本书是linux防火墙权威指南,包括如何使用linux netfilter/iptables来实现防火墙的主题。本书的主题既包括如何安装和设置防火墙的基础知识,也包括针对黑客攻击的防范。本身最有价值的部分莫过于对iptables、包...

    基于Linux 的防火墙技术研究

    关键词:防火墙 Linux Netfilter 包过滤 中图分类号:TN393 文献标识码:A Study of the technology of firewall based on Linux SONG Wen-gong1 Tang Jin2 (1.Central South University, Changsha 510630,China; 2....

    最全的iptable防火墙详解

    防火墙在做信息包过滤决定时,有一套遵循和组成的规则,这些规则存储在专用的信 息包过滤表中,而这些表集成在 Linux 内核中。在信息包过滤表中,规则被分组放在我们所谓的链(chain)中。而netfilter/iptables IP ...

    Linux防火墙.pdf

     1.2 使用iptables进行包过滤 2  1.2.1 表 2  1.2.2 链 2  1.2.3 匹配 3  1.2.4 目标 3  1.3 安装iptables 4  1.4 内核配置 5  1.4.1 基本Netfilter编译选项 6  1.4.2 结束内核配置 7  1.4.3 可...

    基于Linux的防火墙系统的设计与实现 (2010年)

    本文首先对Netfilter的内核架构进行了分析,并在此基础之上,采用模块编程方式开发了一个高效的、稳定的、实用的基于包过滤的防火墙系统.实践证明由于Linux防火墙的开源性.Linux下的防火墙实现机制是一个值的探讨...

    Linux使用iptables限制多个IP访问你的服务器

    在Linux内核上,netfilter是负责数据包过滤、网络地址转换(NAT)和基于协议类型的连接跟踪等功能的一个子系统,这个子系统包含一些信息包过滤表组成,这些表包含内核用来控制信息包过滤处理的规则集。iptables是一个...

    详解Linux iptables常用防火墙规则

    IPTABLES 是与最新的 3.5 版本 Linux 内核集成的 IP 信息包过滤系统。如果 Linux 系统连接到因特网或 LAN、服务器或连接 LAN 和因特网的代理服务器, 则该系统有利于在 Linux 系统上更好地控制 IP 信息包过滤和...

    千万字肝翻Linux内核源码,对底层原理深耕深分析,从入门到入狱

    动态、缺页中断、Kfifo环形缓冲区、开发工具ARM-LInux-gcc安装、网络协议栈、构建嵌入式Lnux系 统、内存性能优化、核心知识CPU、内核编译、UDP收包率、反向映射机制、MMu-gather操作、进程 描述符、虚拟内存机制、...

    Centos7的Firewalld防火墙基础命令详解

    netfilter:指的是Linux内核中实现包过滤防火墙的内部结构,不以程序或文件的形式存在,属于“内核态”的防火墙功能体系; firewalld:指用来管理Linux防护墙的命令程序,属于“用户态”的防火墙管理体系; 1、...

    基于WEB的LINUX防火墙管理系统的设计与实现 (2008年)

    基于Linux内核的netfilter/iptables包过滤系统可以构建完善和强大的防火墙系统,但其配置和管理的可操作性差。在分析netfilter/iptables的基础上,给出了基于WEB界面的Linux防火墙配置和管理系统的实现思路,详细阐述了...

    构筑Linux防火墙之IPtables的概念与用法

    通过使用iptables系统提供的特殊命令 iptables,建立这些规则,并将其添加到内核空间的特定信息包过滤表内的链中。关于添加、除去、编辑规则的命令的一般语法如下: iptables [-t table] command [match] [target...

    Linux 用户态与内核态的交互――netlink 篇

    netlink 套接字实现的,例如iprote2网络管理工具,它与内核的交互就全部使用了netlink,著名的内核包过滤框架Netfilter在与用户空间的通读,也在最新版本中改变为netlink,无疑,它将是Linux用户态与内核态交流的...

    Linux内核??与sk_buff有关重要数据结构

    几个月之前做了关于Linux内核版本1.2.13网络栈的结构框架分析并实现了基于Netfilter的包过滤防火墙,这里以内核3.2.1内核为例来进一步分析,更全面的分析网络栈的结构。  1、先说一下sk_buff结构体  这个...

Global site tag (gtag.js) - Google Analytics