三 检测点
3.0 综述
包过滤需要检查IP包,因此它工作在网络层,截获IP包,并与用户定义的规则做比较。
3.1 ipchains
摘自【3】
----------------------------------------------------------------
| ACCEPT/ lo interface |
v REDIRECT _______ |
--> C --> S --> ______ --> D --> ~~~~~~~~ -->|forward|----> _______ -->
h a |input | e {Routing } |Chain | |output |ACCEPT
e n |Chain | m {Decision} |_______| --->|Chain |
c i |______| a ~~~~~~~~ | | ->|_______|
k t | s | | | | |
s y | q | v | | |
u | v e v DENY/ | | v
m | DENY/ r Local Process REJECT | | DENY/
| v REJECT a | | | REJECT
| DENY d --------------------- |
v e -----------------------------
DENY
总体来说,分为输入检测,输出检测和转发检测。但具体到代码的时候,输出检测实际分散到了几处(不同的
上层协议走IP层的不同的流程):
UDP/RAW/ICMP报文:ip_build_xmit
TCP报文:ip_queue_xmit
转发的包:ip_forward
其它:ip_build_and_send_pkt
正如ipchains项目的负责人Rusty Russell所说,在开始ipchians不久,便发现选择的检测点位置错了,最终
只能暂时将错就错。一个明显的问题是转发的包在此结构中必须经过三条链的匹配。地址伪装功能与防火墙模块
牵扯过于紧密,如果不详细了解其原理的话,配置规则很容易出错。
此部分详细的分析可参见我早期的一份文章【9】。
3.2 iptables
A Packet Traversing the Netfilter System:
--->PRE------>[ROUTE]--->FWD---------->POST------>
Conntrack | Filter ^ NAT (Src)
Mangle | | Conntrack
NAT (Dst) | [ROUTE]
(QDisc) v |
IN Filter OUT Conntrack
| Conntrack ^ Mangle
| | NAT (Dst)
v | Filter
四 规则
4.0 综述
4.1 ipchains
man ipfw可以看到这一段的详细解释。关键数据结构如下:
规则链用ip_chain结构来表示,缺省有input,ouptput,forward三条链。在配置规则的时候,也可以添加新的
链。每条链事实上就是一组相关的规则,以链表的形式存储。
struct ip_chain
{
ip_chainlabel label; /* Defines the label for each block */
struct ip_chain *next; /* Pointer to next block */
struct ip_fwkernel *chain; /* Pointer to first rule in block */
__u32 refcount; /* Number of refernces to block */
int policy; /* Default rule for chain. Only *
* used in built in chains */
struct ip_reent reent[0]; /* Actually several of these */
};
每条规则用一个ip_fwkernel结构表示:
struct ip_fwkernel
{
struct ip_fw ipfw;
struct ip_fwkernel *next; /* where to go next if current
* rule doesn';t match */
struct ip_chain *branch; /* which branch to jump to if
* current rule matches */
int simplebranch; /* Use this if branch == NULL */
struct ip_counters counters[0]; /* Actually several of these */
};
ip_fwkernel中的一个重要部分就是ip_fw,用来表示待匹配的数据包消息:
struct ip_fw
{
struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
__u32 fw_mark; /* ID to stamp on packet */
__u16 fw_proto; /* Protocol, 0 = ANY */
__u16 fw_flg; /* Flags word */
__u16 fw_invflg; /* Inverse flags */
__u16 fw_spts[2]; /* Source port range. */
__u16 fw_dpts[2]; /* Destination port range. */
__u16 fw_redirpt; /* Port to redirect to. */
__u16 fw_outputsize; /* Max amount to output to
NETLINK */
char fw_vianame[IFNAMSIZ]; /* name of interface "via" */
__u8 fw_tosand, fw_tosxor; /* Revised packet priority */
};
2.2内核中网络包与规则的实际匹配在ip_fw_check中进行。
4.2 iptables
一条规则分为三部分:
struct ipt_entry //主要用来匹配IP头
struct ip_match //额外的匹配(tcp头,mac地址等)
struct ip_target //除缺省的动作外(如ACCEPT,DROP),可以增加新的(如REJECT)。
man iptable:
>A firewall rule specifies criteria for a packet, and a
>target. If the packet does not match, the next rule in
>the chain is the examined; if it does match, then the next
>rule is specified by the value of the target, which can be
>the name of a user-defined chain, or one of the special
>values ACCEPT, DROP, QUEUE, or RETURN.
2.4内核中网络包与规则的实际匹配在ip_do_table中进行。这段代码的流程在
netfilter hacking howto 4.1.3描述的非常清楚。
简化代码如下:
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int
ipt_do_table(struct sk_buff **pskb,
unsigned int hook,
const struct net_device *in,
const struct net_device *out,
struct ipt_table *table,
void *userdata)
{
struct ipt_entry *e;
struct ipt_entry_target *t;
unsigned int verdict = NF_DROP;
table_base = (void *)table->private->entries
+ TABLE_OFFSET(table->private,
cpu_number_map(smp_processor_id()));
e = get_entry(table_base, table->private->hook_entry[hook]);
...
ip_packet_match(ip, indev, outdev, &e->ip, offset);
...
IPT_MATCH_ITERATE(e, do_match, *pskb, in, out, offset, protohdr, datalen, &hotdrop)
...
t = ipt_get_target(e);
...
verdict = t->u.kernel.target->target(pskb, hook, in, out, t->data, userdata);//非标准的target走这一步
...
return verdict;
}
流程:
--->NF_HOOK();(/include/linux/netfilter.h)
--->nf_hook_slow;(/net/core/netfilter.c)
--->nf_iterate();(/net/core/netfilter.c)
--->然后运行登记的函数;如果你希望有一套ipt_entry结构规则,并将它放到table里,你此时便可调用ipt_do_table
来匹配。
在2.4内核中,规则本身也是可扩展的,体现可自己定义并添加新的ip_match和ip_target上。
4.2 FW1
未作分析。
五 与应用层的交互
5.0 综述
防火墙除了内核里的功能以外,还需要在应用层有相应的的配置工具,如添加修改规则等,这就涉及如何与
内核通信的问题。
内核模块有三种办法与进程打交道:首先是系统调用,缺点是必须添加新的系统调用或修改原有的,造成对内核
代码原有结构的变换;第二种办法是通过设备文件(/dev目录下的文件),不必修改编译原有的代码,但在使用
之前要先用mknod命令产生一个这样的设备;第三个办法便是使用proc文件系统。
5.1 ipchains
由于ipchains是已经是内核的正式一部分,它采用了修改系统调用的办法来添加修改命令,采用的办法就是
扩展setsockopt系统调用:
int setsockopt (int socket, IPPROTO_IP, int command, void *data, int length)
man ipfw可以获得这方面的细节。
ipchains应用程序首先要需要建立一个raw socket(libipfwc.c),然后在之上调用setsockopt。
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)
调用顺序:
ipchains在应用层调用setsockopt,进入内核:
--->sys_socketcall(net/socket.c)
--->sys_setsockopt(net/socket.c)
--->inet_setsockopt(net/ipv4/af_inet.c)
--->sock_setsockopt(net/core/sock.c)
--->raw_setsockopt(net/ipv4/raw.c)
--->ip_setsockopt(net/ipv4/ip_sockglue.c)
--->ip_fw_ctl(net/ipv4/ip_fw.c)
5.2 iptables
原理同ipchains, 但内部命令格式作了大幅简化。详见nf_setsockopt()。
5.3 FW1
FW1 登记了一个字符设备,通过它来进行用户空间与内核空间的交互。相关代码(从汇编代码翻译成的C程序)如下:
static unsigned int fw_major=0;
static struct file_operations fw_fops=
{
NULL, /* lseek */
fw_read, /* read */
fw_write, /* write */
NULL, /* readdir */
fw_poll, /* poll */
fw_ioctl, /* ioctl */
NULL, /* mmap */
fw_open, /* open */
NULL, /* flush */
fw_release /* release */
NULL, /* fsync */
};
int init_module()
{
...
/*man register_chrdev
On success, register_chrdev returns 0 if major is a number
other then 0, otherwise Linux will choose a major number and
return the chosen value.*/
if(fw_major=register_chrdev(UNNAMED_MAJOR, “fw”, &fw_fops))
return -1;
...
}
void cleanup_module()
{
...
unregister_chrdev(fw_major, "fw");
...
}
fw_ioctl()用来做配置工作。
七 状态检测
7.0 综述
基于状态的检测对管理规则提出了非常大的方便,现在已成了防火墙的一项基本要求。但目的明确之后,其实现可以
选择多种不同的方法。
7.1 ipchains
ipchains本身不能完成状态检测,但有几份pacth为它做了一下这方面的补充,采用的是简单的动态添加规则的办法,
这是作者对其的介绍:
> I believe it does exactly what I want: Installing a temporary
> "backward"-rule to let packets in as a response to an
> outgoing request.
7.2 iptables
在2.4内核中,基于状态的检测已经实现,利用的是connection track模块。此模块检查所有到来的数据包,将得到
的状态(enum ip_conntrack_status)保留在sk_buff结构中(即skb->nfct,可通过ip_conntrack_get()得到)。
在规则中要指明状态信息(作为一个ipt_match),既实际上仍是比较每一条规则。从效率上,这种处理方式感觉不如
下面FW1采用的方式好。
7.3 FW1
这段的代码没有做分析,但有一些文章通过黑箱操作的办法“猜测“出了它的实现原理,如【1】。除规则表以外,
FW1另外维护一份状态表。当一个新的连接发生的时候,FW1与规则表配备,如果允许通过的话,则在状态表中
建立相应表项。以后的数据过来的时候首先匹配状态表,如果它属于一个连接,便允许通过,而不再检查规则表。
草草看了一下BSD下的防火墙ipfilter的howto,感觉它的实现与FW1基本相同。