全部博文(41)
分类: LINUX
2009-10-15 12:01:34
在早期,由于Unix系统庞大,硬件的昂贵使商业市场和个人无法接受。现在IMBPC兼容机系列386,486,PENTIUM已具备了告诉大容量的特点,这就为Unix在pc上的发展铺平了道路,1991年Linux B.TORVALDS网络上组织人员为PC机写了第一个免费的Unix内核(KERNEL),发展至今天已经成为一个能在PC上可靠稳定工作的UNIX/X-WIN操作系统,这个系统被命名为LINUX系统,由于这些发展,个人拥有工作站已不再是梦想。每个学生,计算机爱好者都可以把自己的PC变成类似SUN Station或 BSD Unix系统的工作站。
Linux是一个在386/486/pentiun PC机上运行的Unix系统,Linux系统具有最新的Unix的全部功能,包括真正的多任务,虚拟存储,共享函数库,即时负载,优越的存储管理和 TCP/IP,UUDP网络工具。Linux具有免费获取系统软件,硬件费用低廉的特点,今年来发展迅猛。Linux的应用软件已达上千种类,从dos环境模拟的图片,印象信号的处理,从游戏到中文软件,无所不包,个大软件公司已纷纷开始推出Linux版本的商业软件,最近对Linux所做的基准测试表明,pentium/100的速度可以跟SUN/20媲美,94年底在华盛顿万国毁誉中心召开了Linux世界年会,各大软件公司都派出了阵容强大的代表团,展出了丰富的Linxu产品,Linux开始进入商业应用时代。
Linxu和windows对于代码透明度这一哲学问题上是完全不同的,Linux符合GNU通用公用许可证,用户可以拷贝,复制,并分发源代码。
Linux平台为用户提供了构建防火墙的框架,用户在此框架上构建符合需要的防火墙。为了使防火墙更加安全、效率更高、更充分利用内核已有功能,需要深入分析内核源代码,提炼出重要数据结构和数据处理流程。
网络数据按照来源和去向,可以分为三类:流入的、流经的和流出的,其中流入和流经的数据需要经过路由才能区分,而流经和流出的数据则需要经过投递。netfilte是Linux2.4提供的防火墙内核级框架。IPv4协议栈为了实现对netfilter架构的支持,在IP packet在IPv4协议栈上的游历路线之中,仔细选择了五个参考点。在这五个参考点上,各引入了一行对NF_ HOOK()宏函数的一个相应的调用。netfilter根据网络数据的流向,在以下几个参考点(钩子点)插入处理过程:
(1) NF IP PRE ROUTING,数据包进入路由之前;
(2) NF IP FORWARD,数据包转发到另一个网卡;
(3) NF IP POST ROUTING,最后一个钩子点,数据包流出重新进入网络之前;
(4) NF IP LOCAL IN,流入本地的数据包;
(5) NF IP LOCAL OUT,流出本地的数据包(本地路由之前)。
一个数据包通过netfilter框架时,它将经过见图3-1所示流程。数据包从左边进入系统,校检IP,由NF_ IP PRE_ ROUTING钩子函数进行处理。然后路由判断,看数据包是转发还是进入本机。如是转发,则进入NF IP PORWARD钩子处理,否则进入NF IP LOCAL IN钩子处理,并在处理后传给上层协议。本地产生的数据包先经过NF_ IP_ LOCAL_ OUT钩子处理,再进行路由判断,决定是否发至外网。所有外发包都要经过NF_ IP_ POS几ROUTING处理,然后再发至外网。
内核模块可以对一个或多个这样的钩子函数进行注册挂接,并在数据包经过这些钩子函数是被调用。这样模块就可以修改数据包,并向netfilter返回如下值:
(1) NF ACCEPT:继续正常的报文处理;
(2) NF DROP:将报文丢弃;
(3) NF STOLEN:由钩子函数处理了该报文,不要再继续传送;
(4) NF一UEUE:将报文入队,通常交由用户程序处理;
(5) NF REPEAT:再次调用该钩子函数。
图3-1 netfilter框架数据包流程
(1) 链表结构:struct list_head{
struct list head *next,*prev;
};
(2)链表二维数组:
struct list head of hooks[NPROTO][NF MAX HOOKS];其中,一维为协议族号,二维为上述五个HOOK号。此链表数组用以链接nf_hookes_ops结构体。
Struct_nf_hook_ops{
struct list_head list;//链表
nf_hookfn *hook;//指向用户定义的在该钩子点对应的处理函数
int pf;//协议族号
int hooknum;//核心指定的那五个钩子点之一
int priority;//优先级,在of hooks链表中各处理函数按优先级排序};//有关钩子函数的结构
(3) sk_buff结构体
它是Linux内核处理网络数据包所用的缓冲区。数据包从网卡接收到后,被放到此缓冲区中并传到协议堆栈。此结构体较长,内部结构省略。
(4) netfilter原理
数据包都要经过NF_HOOK宏处理}。 NF HOOK宏是这样的:使用netfilter的用户首先需要将自己的处理函数在nf_hook链表数组上注册,即把相应nf_hook ops结构插入到nf_hooks链表中。
然后NF HOOK根据nf_hooks链表二维数组的pf、hook值找到相应链表,如果链表为空则调用okfn指针所指函数处理skb指针所指数据包,否则调用nf_hook_slow()函数处理此数据包。nf_hook_slow()函数又调用nf_iterate()函数遍历此链表。nf_iterate()遍历过程中,根据链表各元素((nf_hook_ops结构体)所含处理函数指针对skb所指数据包进行处理,并返回处理结果。nf_hook_slow()函数根据处理结果,选择对数据包接收还是丢弃。
netfilter上的钩子函数都将去执行一个重要函数—ipt_do_table()函数。此函数的几个输入参数解释如下:
(1) struct sk_buff **pskb:传入的待处理网络报;
(2) unsigned int hook:处理此网络报的钩子点;
(3) struct ipt_ table *table:用户定义的规则表。
Ipt_do_table()函数就是根据传入的规则表来处理网络数据包。此函数前半部分主要是从传入的待处理网络协议包中取出相应的字段,并将其整理成和新定义的数据结构。首先取出如下数据:IP头、对应协议地址、数据长度、输入输出设备、分片偏移量、规则表的入口指针、相应钩子点ipt_entry入口指针及下溢指针。然后调用函数ip_packet_match()。根据待处理的网络协议包的IP头、输入输出设备、分片偏移量与规则所定义的IP头信息进行匹配。匹配规则完成后,返回有标准目标和非标准目标。非标准目标的处理通过函数指针t->u.kernel.target->target()调用相应处理函数进行。
连线跟踪 (Connection Tracking,也称为状态检测)作为一个独立运行模块,
它是动态包过滤、地址转换的基础W1。它被设置在Netfilte:框架中四个钩子点:
(1) NF_IP_PRE_ROUTING;
(2) NF_IP_POST_ROUTING;
(3) NF_IP_LOCA_IN;
(4) NF_IP_LOCAL_OUT。
检查每一个有效连接的状态,并根据这些信息决定网络数据包是否能够通过防火墙。它在协议栈底层截取数据包进行分析,并且将当前数据包及其状态信息和其前一时刻数据包及状态信息进行比较,得到该数据包控制信息。以TCP三次握手连接为例,第一个SYN包到达后,由防火墙规则库检查此SYN包,SYN包在规则库中依次与规则匹配。如都不匹配,将此SYN包弃掉。如匹配则接收,并将这次连接的状态信息保存在一个连接跟踪信息表中。连接跟踪信息表位于内核中。SYN包之后的SYN/ACK, ACK包则不必用规则库匹配,而是跟连线跟踪信息表的内容比较,然后决定包丢弃还是接收。利用连线跟踪可以提高防火墙性能,因为每个数据包首先跟连线跟踪信息表进行比较,而不是跟规则库一一比较。
连线跟踪模块截获、分析并处理所有试图通过防火墙的数据包,保证网络安全和数据完整。连接跟踪信息表是动态的,它会根据网络状态不断更新自己。模块可以识别不同应用的服务类型,还可以通过以前的连接分析出状态信息。模块把相关状态和状态之间的关联信息,存储到动态连接表并随时更新。通过这些数据,连线跟踪模块可以检测到后继的连接。连线跟踪技术对应用程序透明,不需要针对每个服务设置单独代理,使其具有更高的安全性、更好的可扩展性。
连线跟踪模块将数据包连接分为四个状态:NEW, ESTABLISHED, RELATED和INVALID。通过对这四种状态匹配,可以实现状态防火墙。这四种状态对TCP,UDP, ICMP三种协议均有效。下面具体介绍这四种状态。
NEW: NEW说明这是conntrack模块看到的某一个连接的第一个包,它即将被匹配。比如,如果看到一个SYN包并且它是某连接的第一个包,它将被匹配。然而,数据包也可能不是一个SYN包但其状态却被当作NEW。某些情况下,这可能导致某些问题。但是,当需要从其它防火墙找到丢失的连接或者当一个连接己经超时但实际却未关闭时,状态为NEW的非SYN包也可能会带来很大帮助。
ESTABLISHED:此状态已经看到两个方向的数据传输并且将不断匹配它。处于ESTABLISHED状态的连接很容易理解。只要发送并接到应答,连接就处于ESTABLISHED状态。应答数据包一到达或一通过防火墙,此连接就从NEW状态转为ESTABLISHED状态。响应外发数据包而产生的ICMP报错及重定向消息也可以被看作ESTABLISHED状态。
RELATED: RELATED是一个较难理解的状态。当一个连接和某个已处于ESTABLISHED状态的连接有关系时,就被认为是RELATED的。换句话说,一个连接要想是RELATED的,首先要有个ESTABLISHED连接,由这个ESTABLISHED连接再产生一个主连接之外的连接(RELATED连接)。 FTP是个很好的例子,每个FTP由数据连接和控制连接组成,数据连接就是和控制连接有关联的。如果没有RELATED状态,数据连接是无法正确建立的。有了这个状态,ICMP响应、FTP传输等才能穿过防火墙正常工作。注意:依赖于此状态的大部分TCP和一些UDP协议是十分复杂的,并且在TCP, UDP数据段的有效载荷中发送连接信息,因此需要特殊的helper模块才能被正确理解。
INVALID: INVALID状态表示此数据包不能被识别或它没有任何状态。这可能由于几种原因,诸如系统内存耗尽或ICMP报错信息对任何已知连接不作响应。通常的选择是丢弃此状态下的所有包。
从内核角度,连接跟踪在查找哪些协议、规则匹配一个数据包之前,它首先要把此数据包转化为一个元组( tuple ),元组代表了数据包有用的部分。这个元组又分为可操纵部分和不可操纵部分,分别称为“源(src)”和“目的(dst ) "。方向相同的数据包流中的每一个数据包所对应的元组是相同的。
比如,一个TCP包之元组包含了可操纵部分:源IP地址和源端口,还包含了不可操纵部分目的IP地址和目的端口。不过,这两个部分并不要求是同样类型的。比如,一个ICMP包之元组的可操纵部分:源IP和ICMP编号,它的不可操纵部分:目的IP和ICMP类型和代码号。可见,这两部分元组组成是不同的。
每个元组都有一个逆(inverse ),逆是数据包流中应答包之元组。比如,有一个ICMP数据包(ping发出):icmp编号为12345,从192.168.1.1到
用ip_conntrack_tuple结构体表示的元组得到了广泛的使用。实际上,数据包流入所经过的hook点(它对操作类型有所影响)和涉及到的设备形成了有关数据包的完整信息。大部分元组都包含在ip_conntrac_tuple_hash结构体中,此结构体有一个双向链表入口地址和一个指向元组所属的连接的指针。连接用ip_conntrack结构体表示:它有两个ip_ conntrack_tuple_ hash(一个表示源数据包方向,另一个表示响应包方向)。
总之,连线跟踪模块的任务是对于一个己经存在的建立好的连接,指定哪些数据包属于这个连接。这个模块要做的工作如下:
(1) 告诉netfilte:此模块对哪些数据包感兴趣;
(2) 在netfilter中注册一个函数。一旦数据包匹配上述规则,就调用此函数;
(3) 可以调用ip_conntrack_expect_related()函数通知netfilte以要求相关连接要求的连接的第一个数据包到达时,如果有其他工作要做,模块可以注册一个回调函数在那时调用。
Linux内核对TCP流程的处理主要在tcp_ipv4.c文件中的函数实现。具体的,当处理TCP SYN包时,系统进入tcp_v4_conn_request函数。其中调用cookie_v4_init_sequence生成一个ISN(Initial Sequence Number)。Linux内核把它作为SYN Cookie流程中的cookie。Netfilte:防火墙由规则表构成,而规则表又由规则链(相对于netfiler中的五个钩子点,netfilter防火墙有五个内置规则链:PREROUTING, INPUT, OUTPUT,FORWARD以及POSTROUTING)构成。数据包依次到达每个规则链,然后按链中的规则进行匹配,根据匹配结果执行相应策略(接收、丢弃等)。按照这种划分方法,netfilte:
图3-2 防火墙的流程结构图
从上节netfilter内核分析中,可以看到netfilte框架在数据包接收、转发、发送过程中,都要经过NF_ HOOK宏处理。NF_HOOK又调用相应钩子函数执行一个通用函数—ipt_do table()。此函数从设置好的规则链中取出规则,对数据包进行匹配、目标处理等操作,最后返回处理结果。设置规则是通过iptables进行的。
iptables是Linux系统为用户提供的基于netfilte的一个用以配置防火墙的工具。此工具提供了一个命名规则集。数据包经过每个钩子点时,都应遍历规则集中的相应规则链。用户可以直接操作iptables、iptables又通过socket系统调用访问内核netfilter。 iptables规则表注册后,用户可以用getsockopt()系统调用得到它的内容,也可以调用setsockopt()修改它。iptables做为netfilter的用户配置工具,它为用户配置防火墙提供了强大的功能和方便的途径。
概括地说,iptables与netfilter的关系是:netfilter提供了内核中对网络数据包处理的手段,iptables则是内核netfilter在用户空间上的实现。netfilter提供了一个框架,而框架内容则由用户用iptables来填充。
(1) 一个struct ipt entry结构体;
(2) 0个或多个struct ipt entry match结构体,此结构体数据长度不定((0或多字节);
(3) 一个struct ipt entry target结构体,此结构体数据长度不定((0或多字节)由于规则由可变长结构体组成,它的长度也是未定的。这一特性为规则扩展提供了巨大的灵活性。以后将看到,特别在每个匹配(match)或目标(target )都能装载任意多个数据时,这一优点将得到体现。
insigned char elems[0];
这两个结构体非常类似,它们都包含了一个总长度域(相应为match_size和target_size)和一个共用体(包含了匹配名或目标名和一个指针,前者对应用户空间,后者对应内核空间)。
内核是靠hook点来找相应规则的,找到后它就按此规则处理数据包。首先,如果匹配ipt_entry结构体中的ipt_ip域,那么依次检查每一个ipt_entry_match结构体(调用相应匹配函数)。如果匹配函数返回0,在此规则上的检查停止。如果检查一直到终点,计数加一,并检查ipt entry target结构体:如果是标准目标,设置返回规则。如果是非标准目标,调用目标函数处理之,然后继续下一个规则。从上述数据处理流程可以看出每个规则的三个组成部分之间的关系。
规则是用链表组织起来的,形成规则链。规则链的入口地址、规则的偏移地址等信息由ipt_table结构体管理,真正记录这些信息的是ipt_table中private指针所指的ipt_table_info结构体:
struct ipt_table_info
{
unsigned int size ; // 表大小
unsigned int number; //表中的规则数
unsigned int initial_entrids; //初始的规则数,用于模块计数
/*相对于规则入口 (对应于具体HOOK)的偏移量*/
unsigned int hook_entry[NF_IP_UNMHOOKS];
/*于hook_entry相对应的规则表上偏移量*/
unsigned int underflow[NF_IP_NUMHOOKS];
char entries[0]___cacheline_aligned; //规则表入口
}
综上所述,iptables规则组织是分层次的。第一级是规则表,每个防火墙可以有多个规则表;第二级是hook,每个规则表都有一个hook集合,每个hook都有一个规则链:第三级为规则,每个规则包含三个部分:ip规则信息、匹配规则信息、目标信息。
iptables工作机制:用户事先设置好规则,然后将规则挂到规则链中去。netfilter会根据设置好的规则链取出规则逐一处理相应数据包。此机制通过Linux系统提供的iptables命令实现的。
iptables [-t table] command [match] [target]
下面介绍一下该命令各选项含义:
这是可选项。用以指定所操作的表。系统已设了三个内置表:filter, nat和mangle } filter作为缺省表。
filte:表用于数据包过滤,但不对包修改,只是判断接收还是丢弃。它在NF_IP_ LOCAL_ IN, NF_ IP_ FORWARD和NF_IP_ LOCAL_OUT三处注册了钩子函数。这样它就提供了与上述钩子对应的三条链进行数据过滤:进入内网的数据包,遍历INPUT规则链;转发的数据包将遍历FORWORD规则链;最后,本地发出的数据包将遍历OUTPUT规则链。通过这三条链可以检测所有数据包。
nat表用于要转发的数据包,它以Connection Tracking模块为基础,仅对每个连接的第一个数据包进行匹配和处理,然后交Connection Tracking模块将处理结果应用到该连接之后的所有数据包。nat在NF_IP_PRE_ROUTING , NF_ IP_ POST_ROUTING注册了钩子函数,如果需要,还可以在NF_ IP_ LOCAL_ IN和NF IP_LOCAL_OUT两处注册钩子,提供对本地数据包(出/入)的地址转换。nat仅对数据包头的地址信息进行修改,而不修改数据包内容,按所修改的部分,nat可分为源NAT ( SNAT)和目的NAT ( DNAT)两类,前者修改第一个数据包的源地址部分,而后者则修改第一个数据包的目的地址部分。SNAT可用来实现IP伪装,而DNAT yJ是透明代理的实现基础。
mangle表属于可以进行报文内容修改的IP Tables,可供修改的数据包内容包括MARK , TOS , TTL等,mangle表的操作函数嵌入在Netfilter的NF IP_PRE_ROUTING 和 NF_IP_LOCAL OUT两钩子点。
除上述三个内置表,用户可以通过挂接模块,调用Netfilte:的接口函数创建新的iptables。
必选项,它是iptables命令的最重要部分。它告诉iptables命令要做什么,例如,插入规则、将规则添加到链的末尾或删除规则。以下是最常用的一些命令:
(1) -A或--append:该命令将一条规则附加到链的末尾;
(2) -D或--delete:通过用一D指定要匹配的规则或者指定规则在链中的位置编号,该命令从链中删除该规则;
(3) _P或--policy:该命令设置链的缺省目标,即策略。所有与链中任何规则都不匹配的信息包都将被强制使用此链的策略;
(4) -N或--new-chain:用命令中所指定的名称创建一个新链;
(5) -F或--flush:如果指定链名,该命令删除链中的所有规则,如果未指定链名,该命令删除所有链中的所有规则;
(6) -L或--list:列出指定链中的所有规则。
可选项。iptables命令的match部分指定数据包与规则匹配所应具有的特征(如源和目的地址、协议等)。匹配分为两大类:通用匹配和特定于协议的匹配。这里,将研究可用于采用任何协议的信息包的通用匹配。下面是一些重要的且常用的通用匹配:
(1) _p或一protocol:协议匹配。用于检查某些特定协议:TCP, UDP, ICMP及它们的组合。ALL是缺省匹配。可以使用!符号,表示‘非’;
(2) -s或一source:源地址匹配。用于根据数据包的源IP地址来与它们匹配。
该匹配还允许对某一范围内的IP地址进行匹配,可以用!符号。缺省为与所有IP地址匹配;
(3) -d或一destination:目的地址匹配。用于根据数据包的目的IP地址来与它们匹配。该匹配还允许对某一范围内IP地址进行配,可以使用!符号。
target(目标)是由规则指定的操作,对与那些规则匹配的数据包执行这些操作。除了允许用户定义的目标之外,还有许多可选的目标项。
(1) ACCEPT:当数据包与规则完全匹配时,接受数据包,并停止遍历链;
(2) DROP:当数据包与规则完全匹配时,会阻塞该信息包,且不对它做进一步处理;
(3) REJECT:该目标的工作方式与DROP目标相同,但和DROP不同的是:
REJECT不会在服务器和客户机上留下死套接字。另外,REJECT将错误消息发回给数据包的发送方。
RETURN:停止遍历与该规则匹配的数据包,并返回到前面调用的链。
其它:用于建立高级规则的目标,如LOG , REDIRECT , MARK ,MIRROR和MASQUERADE等。
Linux内核是整体式结构,这使得用户在内核增加新功能很不方便。为此,Linux提供了一种全新的机制—“可安装”模块(module。有了这个机制,可以根据需要,在不必对内核重新编译连接的条件下,将可安装模块动态地插入运行中的内核,成为内核的一个有机组成部分;或从内核移走己经安装的模块(s10正是这种机制,使得内核的内存映像保持最少,但却具有很大的灵活性和可扩充性。Linux中很多设备驱动程序或文件系统都用模块来实现,系统灵活性大大提高。
加载和卸载模块有两种方式:
(1) root用户通过insmod 和 rmmod命令显式地将模块载入内核或从内核卸载;
(2) 内核也可在需要时,请求守护进程(kerneld)加载和卸载模块。
模块机制的优点是极大改善了Linux的灵活性,缺点是定义模块需要额外的代码,增加了系统开销。用户进程通过模块对核心访问是间接的,这会降低访问效率。
模块加载入 Linux内核后,就成了内核代码的一部分。内核中有一个
module_ list链表,它是作为全局变量出现的,其定义原型为extern struct module*module_ list。每当一个模块要加载入内核,这个模块就会被加到module_ list链表中。每当内核要使用某加载的模块时,它就查找这个链表,找到此模块后,再使用其提供的功能。
每个模块可以输出变量和函数,供其它模块或核心代码调用。模块在需要时,可通过符号表(symbol table)使用核心资源,内核将资源登记在符一号表中。当模块加载时,内核利用符号表来解决模块的资源引用问题。Linux支持模块堆栈(module stacking)机制,即一个模块可请求其它模块为之提供服务。模块加载入内核时,系统修改内核中的符号表将新加载模块提供的资源和符号加到内核符号表中。通过这种通信机制,新模块可以访问老模块提供的资源。此机制为模块可重用性提供了保证。
本章深入分析了Linux内核的防火墙框架,包括三方面内容:netfilter, iptables和模块机制。netfilter是Linux2.4提供的防火墙内核级框架。它通过预设的五个钩子点捕获网络数据包,再根据规则链对这些数据包进行判定(拒绝、接收或其它)。iptables是netfilter在用户空间上的实现,是netfilter的用户配置工具。用户用iptables命令设置自己需要的防火墙规则,然后iptables通过系统调用将规则置入规则链中,netfilter根据规则链对数据包判定。通过模块机制,可动态地将用户模块插入到运行中的netfilter,而不必对内核重新编译链接。本章是下一章的理论基础。