曾树洪惠州学院计算机科学系 广东 516015
Netfilter是Linux操作系统从2.3.15版本开始推出的一款防火墙。Netfilter防火墙功能强大,除了可以进行包过滤之外,还可以进行Nat转换等其他功能。并且Netfilter防火墙具有很强的扩展性,通过构造一个简单的内核模块就可以实现网络新特性的扩展。这些特性使得Netfilter很快就成为linux下的一个重要的网络工具。
从Linux2.4内核开始,Netfilter就实现了连接跟踪功能,内核可以跟踪并记录每个连接的状态。每一个经过 Netfilter的网络数据包都对应一条连接记录,每条连接记录包括源地址、目的地址、源端口、目的端口、协议类型、连接状态、连接引用数目和连接时间等信息。利用这些信息可以更加灵活的配置防火墙。连接跟踪功能还是网络地址转换和其他功能模块实现的基础,在防火墙配置和扩展方面都有极其重要的作用。
Linux内核在不停的完善和增强中,连接跟踪的实现在Linux2.6中也有了改进。本文中引用的内核代码来源于Linux2.6.28版本。
每条连接记录包括源地址、目的地址、源端口、目的端口、协议类型、连接状态、连接引用数目和连接时间等信息。所有的连接记录都放在一个表里,这个表就是连接跟踪表。Linux内核采用hash表来表示连接跟踪表。如图1所示。
图1 连接跟踪hash表
连接跟踪表是一个以hash值排列的双向链表数组,链表中的每一个节点都是nf_conntrack_tuple_hash类型的数据结构:
struct nf_conntrack_tuple_hash
{
struct hlist_node hnode;
struct nf_conntrack_tuple tuple;
};
hnode成员用于组织双向链表,tuple成员是网络数据包进入Netfilter后的一个转换。tuple包含src(来源)、dst(目的)两个成员,src包含两个成员:ip和相应协议端口。
图2 链表节点结构
一个完整的连接包括正反两个方向,在内核中使用nf_conn类型的数据结构来表示连接:
struct nf_conn
{
struct nf_conntrack ct_general;
……
struct nf_conntrack_tuple_hash tuplehash [IP_CT_DIR_MAX];
unsigned long status;
……
struct timer_list timeout;
……
};
nf_conn包含了许多成员,其中的tuplehash成员实际上是一个包含两个连接跟踪链表节点的数组:tuplehash[IP_CT_DIR_ORIGINAL]指向初始方向的连接跟踪链表节点,tuplehash[IP_CT_DIR_REPLY]指向应答方向的连接跟踪链表节点。通过tuplehash数组将一条完整的连接在内核里表示出来了。
类型为nf_conntrack的ct_general成员可用于对本连接记录的公开引用进行计数。
struct nf_conntrack
{
atomic_t use;
};
status成员标记连接的状态,timeout成员可对连接进行计时,并对超时的连接进行处理。
内核为连接跟踪在Netfilter的其中4个注册点注册了处理函数:ipv4_conntrack_in()、ipv4_confirm()和ipv4_conntrack_local,他们的位置如图3所示。
图3 连接跟踪在Netfilter中的注册点
函数 ipv4_conntrack_in()的主要功能是判断当前数据包的转换 tuple是否已经在连接跟踪表里。网络数据包进入Netfilter后被转换为一个tuple,函数ipv4_conntrack_in()首先在连接跟踪表里查找该tuple。如果在,说明当前数据包相关联的连接已经建立好了;如果不在,说明这是一个新连接,内核会为该数据包生成相关的连接记录并放到临时表里。此时一条新的连接并未正式建立,因为该连接记录并没有被放到连接跟踪表里,而是被放到一个临时表里。
函数 ipv4_conntrack_local()进行一些相关操作后将马上调用 ipv4_conntrack_in(),ipv4_conntrack_local()的主要功能就是调用ipv4_conntrack_in()进行连接跟踪处理。
函数 ipv4_confirm()的主要功能是再次确认是否为数据包建立连接跟踪并添加到连接跟踪表中。在函数ipv4_conntrack_in()中建立的连接记录到了 ipv4_confirm()里才从临时表里添加到连接跟踪表,一条新的连接才正式建立。如果数据包在中间的处理过程中被过滤掉了,数据包将不能到达ipv4_confirm(),也就不会为该数据包建立连接记录。
由图3可知新建一条连接记录有以下三种途径:
(1)本地接收数据包
数据包流经路线:
NF_INET_PRE_ROUTING ---> ROUTE --->NF_INET_LOCAL_IN
在 NF_INET_PRE_ROUTING处生成新的连接记录(ipv4_conntrack_in()),在 NF_INET_LOCAL_IN处将该新连接记录添加到连接跟踪表(ipv4_confirm())。
(2)转发数据包
数据包流经路线:
NF_INET_PRE_ROUTING ---> ROUTE --->NF_INET_FORWARD---> NF_INET_POST_ROUTING
在 NF_INET_PRE_ROUTING处生成新的连接记录(ipv4_conntrack_in()),在NF_INET_POST_ROUTING处将该新连接记录添加到连接跟踪表(ipv4_confirm())。
(3)本地发送包
数据包流经路线:
NF_INET_LOCAL_OUT ---> ROUTE --->NF_INET_POST_ROUTING
在 NF_INET_LOCAL_OUT处生成新的连接记录(ipv4_conntrack_local()),在 NF_INET_POST_ROUTING 处将该新连接记录添加到连接跟踪表(ipv4_confirm())。
连接跟踪表记录了每个连接的相应ip地址、协议、端口、连接数目以及连接状态等相关信息。利用这些信息可以配置更加灵活和安全的防火墙。笔者的实验环境:防火墙服务器采用双网卡,一个连接外网(校园网),一个连接内网(实验室)。防火墙服务器的操作系统为Fedora8,内核版本为2.26.28,Iptables版本为1.4.4。外网接口eth0的IP地址为:172.17.21.31,子网掩码为:255.255.255.0,网关为:172.17.21.254,内网接口eth1的IP地址为:192.168.1.254。
使用connlimit模块可以限制连接数,起到负载均衡的作用。这样可以将工作负担相对平均的分散到多部主机上,使每部主机获得大致相等的工作量。
(1)限制局域网内每个用户的连接数
如果需要限制局域网内每个用户的连接数为 100,只需在服务器上添加如下规则:
iptables –A FORWARD -m connlimit --connlimit-above 100 -j DROP
这样局域网内每个用户的连接数当超过100时数据封包将被丢弃。
也可以写成这样的规则:
iptables –A FORWARD -m connlimit !--connlimit-above 100 -j ACCEPT
这样局域网内的每个用户的连接数在100以内的数据封包将被转发,也就是连接数超过100的数据封包将被丢弃。以下的规则同理。
也可以针对某种传输协议限制每个用户的连接数,如限制每用户的tcp连接数在100以内,只需将规则修改如下:
iptables -A FORWARD -p tcp -m connlimit--connlimit-above 100 -j DROP
将规则里“-p tcp”的tcp修改成upd或icmp就可以限制每用户的upd或icmp协议的连接数。
(2)限制局域网内单个用户的连接数
如限制局域网内192.168.1.1用户的连接数为100,在服务器上添加如下规则:
iptables -I FORWARD -s 192.168.1.1 -m connlimit--connlimit-above 100 -j DROP
如果要限制局域网内 192.168.1.1以外的用户的连接数为100,规则只需修改如下:
iptables -I FORWARD !-s 192.168.1.1 -m connlimit--connlimit-above 100 -j DROP
(3)限制本机的连接数
数据封包进入局域网内或者从局域网内发出到外网,都要经过转发链(FORWARD)。进入本机的数据封包或者从本机发出的数据封包不经过转发链(FORWARD),但所有进入本机的数据封包都要经过输入链(INPUT),从本机发出的数据封包都要经过输出链(OUTPUT),限制本机的连接数需将规则里的FORWARD改成INPUT或OUTPUT:
iptables -I INPUT -m connlimit --connlimit-above 100 -j DROP
或者
iptables -I OUTPUT -m connlimit --connlimit-above 100 -j DROP
(4)基于某种协议限制连接数
connlimit功能模块还可以只限制某种协议的连接数,只需在前面规则的基础上加上“-p”选项,比如限制局域网内每个用户的tcp连接数为100,规则如下:
iptables –A FORWARD -p tcp -m connlimit--connlimit-above 100 -j DROP
将tcp修改成udp或者icmp就可以限制相应协议的连接数,比如限制局域网内192.168.1.1用户的udp连接数为100,规则修改如下:
iptables -I FORWARD -p udp -s 192.168.1.1 -m connlimit--connlimit-above 100 -j DROP
(5)限制某个端口连接数
connlimit模块具有限制端口连接数的功能,如果要限制局域网内每个用户的80端口连接数为10,规则如下:
iptables -I FORWARD -p tcp --dport 80 -m connlimit--connlimit-above 10 -j DROP
使用state模块可以根据连接的状态来进行防火墙配置,这里的连接状态是指在用户空间里能使用的4种状态,如表1所示。
表1 数据包在用户空间的状态
如要禁止外网发起连接,可以添加规则:
iptables -A FORWARD -i eth0 -m state state NEW -j DROP
这样一条连接只能是由局域网内用户发起建立(这里服务器连接外网的网卡是eth0,连接内网的网卡是eth1)。外网进入NEW状态的数据包都将丢弃。如果是针对主机而不是局域网设置,规则修改如下:
iptables -A INPUT -m state state NEW -j DROP
如要禁止局域网内用户回应外网的ICMP封包,添加规则:
iptables -A FORWARD -p icmp -o eth 0 -m state --state ESTABLISHED -j DROP
Linux下的 Netfilter防火墙功能强大、扩展性强。本文分析了2.6.28内核下网络数据包连接跟踪的工作原理。利用Netfilter的连接跟踪功能使实验室或其他局域网内的用户机负载更加均衡,更加方便和灵活的配置防火墙。
[1]博嘉科技.Linux 防火墙技术探秘[M].北京:国防工业出版社.2002.
[2]顾栋梁,周健,程克勤.基于Netfilter 的连接限制的研究与实现[J].计算机工程.2009.
[3] www.kernel.org.
[4]郭锡泉.应用层协议分析在状态检测防火墙中的应用[J].计算机工程.2007.