-
我
引言
本文以防火墙功能分类为框架
,
逐个探讨了每项功
能的详细技术及实现
,
其中具
体实现均
取自
linux
系统
.
之所以采用
linux
系统
作技术分析
,
主要是因为其本身已基本实现了防火墙系统
的各类功能且经受了足够考验
,
因此具有极大
的参考价值
.
本文所描述的功能如下
:
1.
NAT;
2.
负载均衡
(load
balance,
又称
virtual server);;
3.
包过滤
;
4.
日志
;
5.
流量统计
6.
VPN;
7.
内容安全
;
8.
身份验证
;
9.
入侵监测
.
防火墙的核心功能
(
包过滤
,
伪装
,
负载均衡
)<
/p>
在
IP
层实现
,
其余大部分功能属于
应用层实现
(VP
N
除外
,
因为利用了封装机制
,
很难说究竟在那一层
).
尽管我们说
核心功能在
IP
层实现
,
但实际上只是这些功能函数
(ca
ll_in_firewall(),call_fw_firewall(),call_out_firew
all(),
ip_fw_masquerade(),
p>
及
ip_fw_demasquerade()
等
)
在网络层被调用
,
真正在完
成这些功能时也用到了上层协议
(TCP
/UDP/ICMP)
的头信息
(
如根
据端口
,flag
标
志
,ICMP
类型进行过滤等
).
2 linux
网络部分代码分析
<
/p>
(
注
:
加入这一
部分主要是因为目前没有一篇文章结合最新的
2.2
内核讲述了
linux
的网络原理
,
在此介绍一下其流程会有助于整体的理解
.)
p>
Linux
网络层采用统一的缓冲区结构
s
kbuff(include/skbuff.h)
。底层从网络设备
< br>接收到数据帧后
,
分配一块内存
,
然后将数据整理成
skbuff
的结
构
.
在网络协议处
理的时候
,
数据均以
skbuff
的形式在各层之间传递、处理
.
一个
个单独的
skbuff
被组织成双向链表的形式
.
Skbuff
的强大功能
在于它提供了众多指针
,
可以快速的定位协议头位置
;
它也同
时保留了许多数据包信息
(
如使用的网络设备等
),
以
便协议层根据需要灵活应用
.
整个网
络层的流程如下
(
以两个进程通过
TC
P/IP
进行通信为例
):
1
IP
协
议层有三个关键函数
:ip_rcv()
、
ip_forward()
、
ip_output(),<
/p>
分别处理
IP
层的
接收、转发和发送工作
.
防火墙的功能函数将在此三个函数中
调用
.
3
功能实现分析
1.
NAT.
简单的讲
,
网络地址转换
(NAT)
是将一个
(
或一组
)IP
地址转换成另一个
(
或一
组
)IP
地址
.
由于网络地址的缺乏和出于安全等因素的考虑
,
许多公司和机构采用了私有
IP
地址
(RFC 1918)
来建立自己的内部网络
,<
/p>
但为了实现与
internet
的互连<
/p>
,
必须对
外表现为合法的
IP.
通过带有
NAT
功能的
路由器或防火墙
,
便可实现私有与合法
IP
的转换
.
概念上
,NAT
可分为静态
NAT
(static address
translation)
和动态
NAT(dynamic
address
translation):
静态
NAT-----------
私有
IP
与合法
IP
之间是一
一映射关系
,
每一个内部
IP
都
有一个外部
IP
与之
对应
,
系统通过维持一张固定的映射表来实现此种功能
.
linux
中
p>
,
在
2.2.4
版
本中曾有专门的一部分处理静态的地址转换
,
并用相应的应用<
/p>
层工具
ipnatadm
来管理
,
但到了
2.2.
13
内核
(
我主要看的版本
)
中
,
无论是文件还是相
关
函数都有了
非常大的改变
.
与静态地址转换相关的文件只有
/ipv4/
ip_nat_dumb.c,
其中只有
ip_do_nat(
)
一个函数
.
此函数在
ip_forward(),
和
ip_output()
中被调用
.
其作用是修改转发
和发送的包的
源
和目的地址
(
仅此功能而已
).
但是我却未能发现与之相关的上层接口和应用层
管理工具
.
(
编译内核时要指定<
/p>
CONFIG_IP_ROUTE_NAT
和
< br>CONFIG_IP_MULYIPLE_TABLE).
动态
NAT--------------
动态的决定外部
与内部的
IP
地址之间的映射关系
.<
/p>
此
时可用的合法
IP
数往往少于内部网的主机数
,
极端的情况便是
linux
中的
IP
伪
p>
装
(
多对一的映射
).
对实际的应用来说
,
此时仅仅改变
IP
地址已经不够
< br>,
必须同时利用
TCP/UDP
的端
口号来实现多台主机共用一个地址
.
此时防火墙必须维持一个动态的映射表
,
且
< br>随时要对此表进行更新
.
原理如下图所示:
伪装功能相关文件有
(
均在
/ipv4
目录
):
ip_masq.c ip_masq_app.c
ip_masq_autofw.c
ip_masq_cuseeme.c
ip_masq_ftp.c ip_masq_irc.c
ip_masq_mfw.c ip_masq_mod.c
ip_masq_portfw.c
ip_masq_quake.c
ip_masq_raudio.c ip_masq_user.c
ip_masq_vdolive.c
头文件有
:
#include
#include
#ifdef CONFIG_IP_MASQUERADE_MOD
#include
#endif
其中最主要的文件是
ip_masq.c,
它定义了对应用层的
接口和实际的地址伪装处
理过程
.
其余
文件大多是根据专门应用的扩展
.
流
程为
(
没有结合包过滤
):
当
IP
层接受到信息
(ip_rcv)
以后
,
在确定信息准确无误后
,
查
路由
,
伪装的包和
去往防火墙本身的包
的目的地址均是防火墙的对外地址
,IP
层将用
ip_local_deliver()
进行处理
,<
/p>
其中便调用了
ip_fw_demasquerade()
。解伪装
会将真正的目的地址和端口恢复出来,经过再次查路由,如果
是发往本地的包
,
则交给相应的上层去处理
(tcp_ipv4_rcv, udp_rcv, raw_rcv
等
),
否则调用
ip_forward().
p>
ip_fw_masquerade()
则在
ip_forward()
中被调用
.
具体算法
:
公开地址与内部地址的映射表采用的数据结构是
ip_masq(
在
/include/net/ip_masq.h
中定义<
/p>
),
其格式为
:
struct ip_masq {
struct
list_head m_list, s_list, d_list; /* hashed
d-linked list
heads */
atomic_t refcnt; /* reference count */
struct timer_list timer; /* Expiration
timer */
/*****************************
***************************************
***
以下几个是最重要的参数
,
分
别为所用的协议
(protocol),
源、目的地址
(saddr,daddr),
源、目的端口
(
sport,dport)
,
经伪装后的地址、端口
(maddr,mport).
p>
*****************************************
****************************
*/
__u16 protocol; /* Which protocol are
we talking? */
__u16 sport, dport,
mport; /* src, dst & masq ports */
__u32 saddr, daddr, maddr; /* src, dst
& masq addresses */
/******************
**************************************************
**/
struct ip_masq_seq
out_seq, in_seq;
struct ip_masq_app
*app; /* bound ip_masq_app object */
void *app_data; /* Application private
data */
struct ip_masq *control; /*
Master control connection */
atomic_t
n_control; /* Number of
unsigned flags;
/* status flags */
unsigned timeout; /*
timeout */
unsigned state; /* state
info */
struct ip_masq_timeout_table
*timeout_table;
};
因为正常情况下
p>
linux
不会用到
32K
以上的端口号
,
负责伪装的程序把
61000-
65096
范围的
端口用作伪装
,
所以缺省情况下同时支
持的最大伪装数是
4096
个
.
当然可以通过
修改源程
序来更改
.
相关定义为
:
#define PORT_MASQ_BEGIN 61000
#define PORT_MASQ_END
(PORT_MASQ_BEGIN+4096)
伪装部分在
IP
层被调用
,
它为
< br>IP
层提供了四个函数作为调用接口
:
int ip_fw_masquerade(struct sk_buff **,
__u32 maddr);
/*tcp,udp
协议
的伪装
*/
int ip_fw_demasquerade(struct sk_buff
**); /*tcp,udp
协议的解伪装
*/
int ip_fw_masq_icmp(struct sk_buff **,
__u32 maddr);
/*icmp
协议的伪
装
*/
int ip_fw_unmasq_icmp(struct sk_buff
*); /*icmp
协议的解伪装
*/
我们着重分析
ip_fw_masquerade
:
因为只有从内部网到外部网需要伪装
,
p>
所以
ip_fw_masquerade
的
调用出现在
ip_forward()
函数中
.
ip_forward()
的函数流程为
:
1.
因为
ip_forward()
接收的参数是一个
p>
skbuff,
它首先利用
skbuff<
/p>
的指针
,
把
IP
头找出
:
struct iphdr
*iph; /* Our header */
iph = skb->
2.
因为
ip_forward()
由
ip_rcv()
调用
,
而在
ip_rcv()
中已查过了路
由
,
此处只
需利用
skbuff
的指针定位路由信息即可
:
struct rtable *rt; /* Route we use */
rt = (struct rtable*)skb->dst;
< br>3.
如果此
IP
包的生存时间<
/p>
(ttl)
已到
,
则丢弃
.
if (iph->ttl <= 1)
goto too_many_hops;
4.
如果在选项中指定了严格的源路由功能
(strict source
routing) ,
且此处无
法达到
,
也丢弃
:
if
(opt->is_strictroute && rt->rt_dst !=
rt->rt_gateway)
goto sr_failed;
5.
如果指定的伪装功能
,
且
上层协议是
ICMP,
则在此处处理一部分
,
且跳过后面
的包过滤处理
( Why ? ):
#ifdef
CONFIG_IP_MASQUERADE
if(!(IPCB(skb)->fl
ags&IPSKB_MASQUERADED)) {
if
(iph->protocol == IPPROTO_ICMP) {
........
fw_res =
ip_fw_masq_icmp(&skb, maddr);
if
(fw_res)
/* ICMP matched - skip
firewall */
goto skip_call_fw_firewall;
........
}
}
#endif
6.
如果上一步的前
提不成立
,
则要经过一次包过滤
.
fw_res=call_fw_firewall(PF_INET, dev2,
iph, NULL, &skb);
7.
我们知道
,
在当前版本中
,
包过
滤与伪装功能在许多地方是紧密联系在一次的
,
如采用同样的配
置工具
ipchains,
同样的配置接口
setsocketopt(),
其中是否启
动伪装的标志
也在放火墙的
chains
中
,
即如果你指定了
ipchains -A forward
-j MASQ,
则
call_fw_
firewall()
会返回
FW_MASQUERADE,
如果这样
,
程序将进行
到调用
ip_fw_masquerade()
的地方
.
skip_call_fw_firewall:
.......
if (maddr == 0)
maddr = inet_select_addr(dev2,
rt->rt_gateway, RT_SCOPE_UNIVERSE);
ip_fw_masquerade(&skb, maddr);
.......
8.
因为伪装可能改
变了
skbuff
的一些信息
,
此时要重新定位一下
IP
头及其选项
:
iph = skb->;
opt =
&(IPCB(skb)->opt);
9.
因为转发的数
据总是要送出的
,
紧接着会调用
cal
l_out_firewall(),
并把数
据送出去
.
(
后面分析
,
p>
此处略
)
ip_fw_demasqu
erade
的调用出现在
ip_local_deliver(
)
中
.
1.
如果需要
,
首先重组
IP
包
:
-
-
-
-
-
-
-
-
-
上一篇:路由器最基本的配置命令
下一篇:肺动脉高压与肺源性心脏病