1. 概述
LINUX2.2.x中实现了地址伪装(本文引用的代码版本是2.2.19)。它在缺省的伪装流程中可以附加一些其他模块来扩展其功能,有两种类型的模块,ip_masq_mod和ip_masq_app。在这篇文章中,将分析ip_masq_app模块的实现,ip_masq_mod的实现将在以后的文章中分析。
2. IP_MASQ_FTP模块
2.1. 概述
ip_masq_app是对应用层协议数据进行处理的模块,以ip_masq_ftp的实现为例说明其实现要点。
struct ip_masq_app ip_masq_ftp = {
NULL,/* next */
"ftp",
/* name */
0,
/* type */
0,
/* n_attach */
masq_ftp_init_1,
/* ip_masq_init_1 */
masq_ftp_done_1,
/* ip_masq_done_1 */
masq_ftp_out,
/* pkt_out */
masq_ftp_in,
/* pkt_in */
};
首先定义一个ip_masq_app类型数据结构。结构成员中next用于在哈希表中指向相同哈希值单向链表中的下一个结构;name是结构的名称;type是一个由协议,端口和INBOUND或OUTBOUND或fwmark标记决定的值,有两种情况,如图:
[图2.1]
如果flags是0x80000000,则低24位是fwmark,这个值是ipchains在skb中做的标记;如果flags是0x01000000或0x02000000,则低16位是端口号,中间8位是协议号。
n_attach是结构的引用计数。 masq_ftp_init_1和masq_ftp_done_1分别在结构绑定和去绑定时调用;masq_ftp_in,masq_ftp_out分别由ip_masq_app_pkt_in和ip_masq_app_pkt_out调用。
接下来要把这个数据结构注册到系统的ip_masq_app_base哈希表中,哈希值是由结构中的type定义的,所以注册时要指定flags,协议,端口等参数。下面就详细分析一下ip_masq_ftp模块中的过程。
2.2. 初始化
初始化在模块注册时进行,如图:
[图2.2]首先注册OUTBOUND方向的结构。register_ports分配结构,并将它注册到系统中,同时将masq_ftp_objs或masq_in_ftp_objs数组中的指针指向这些结构(这是一个12元素的数组,其中最后一个元素用于mark和in_mark时的注册)。参数ports的默认值为{21},这是一个端口的数组,ip_masq_ftp可以指定多达11个端口,默认的端口为21。注册后,这些结构会在ip_masq_bind_app中被引用。接下来注册INBOUND方向的结构。参数in_ports的默认值是{0},所以在INBOUND方向上没有相应的结构。这使得FTP在目的转换中PASV模式不能使用,具体的分析将会在后面看到。
后面对mark和in_mark的处理与前面的处理大同小异,区别在于这里用指定的fwmark去计算结构中的type值,而前面使用端口号和协议去计算结构中的type值。它们所引用的函数也不同,一个是ip_masq_app_init_fwmark,一个是ip_masq_app_init_proto_port。
2.3. 释放
释放在模块卸载时进行,如图:
[图2.3]
模块卸载时,将注册到系统的结构从哈希表中取下,然后释放结构,并将masq_ftp_objs或masq_in_ftp_objs数组中的相应元素置为NULL。
2.4. 发出和收到的处理
对发出和收到处理的分析将以下图所示的环境为例分析:
[图2.4]
图中所示A是内网主机,FW是防火墙(有两个接口),B是外网主机。
2.4.1. 源转换时的处理
在FW上配置源转换,规则为:ipchains -A forward -s 10.0.0.0/8 -j MASQ。在B上运行FTP的服务器,A作为FTP的客户端。A从服务器B上取文件,过程如下:
[1] A(端口1735)发起到B(端口21)的TCP连接,在ip_fw_masquerade中将会创建如下的地址伪装结构:
[图2.5]同时这个伪装结构会与ip_masq_ftp绑定(在ip_masq_bind_app中)。然后FW会将包中的源地址/源端口改为伪装结构中的伪装地址/伪装端口。
B返回的应答包在ip_fw_demasquerade中处理时会找到这个伪装结构,并将其中的目的地址/目的端口改为伪装结构中的源地址/源端口。
[2] 如果A发出PORT命令,建立从B到A的主动的数据连接取文件。
A发出PORT命令的数据包中有A的地址和监听端口(port 10,0,0,1,6,217)。这个包会在ip_masq_app_pkt_out中由masq_ftp_out处理,如图:
[图2.6]处理时先检查伪装结构的state,如果不是IP_MASQ_S_ESTABLISHED状态,则返回。接下来检查数据的长度是否正确。然后检查ip_masq_app结构中的type。在上一步伪装结构与ip_masq_app绑定时得到的ip_masq_app结构的type是IP_MASQ_APP_OUTBOUND,所以使用OUTBOUND的过程。
它在数据包中匹配"PORT"或"PASV"字符串,如果含有"PORT"字符串,它在"PORT"之后的数据中找到A的地址和端口,地址是前四个逗号分隔的字符串,端口是第五个字符串的值乘256加第六个字符串的值的到。找到地址和端口后,查看它是否是系统允许的端口(masq_ftp_unsafe中会检查这个端口是否小于1024或在noports中包含的端口)。接下来检查包含此地址和端口的伪装结构是否已创建(ip_masq_out_get),如果没有创建,创建一个新的伪装结构(ip_masq_new),如图:
[图2.7](这个结构中目的端口为0是因为此时还不知道下一步B连接A的源端口)
然后将数据中包含的地址和端口用此伪装结构中的伪装地址和端口替换。
[3] B(端口为20)发起到地址192。168。0。88端口61012的TCP连接,ip_fw_demasquerade中的处理会将伪装结构中的目的地址更新为20,然后将它的目的地址/目的端口替换为伪装结构中的源地址(10。0。0。1)/源端口(1753),这样数据连接就可以建立了。
[4] 如果A发出"PASV"命令,这个包在masq_ftp_out中由OUTBOUND方向的过程检查,它将伪装结构的app_data置为masq_ftp_pasv。然后B会应答"Entering Passive Mode 192,168,0,88,221,30"的包,这个包会在masq_ftp_in中由OUTBOUND方向的过程检查(可以参考后面masq_ftp_in的流程,在这个流程中会检查masq_ftp_pasv标记),它会创建一个新的地址伪装结构,如图:
[图2.8](这个伪装结构的源端口为0的原因会在后面讲到)
[5] A(端口为1753)发起到B(端口为56606)的TCP连接,在ip_fw_masquerade中会伪装结构中的源端口更新为1753,然后将源地址/源端口替换为伪装结构中的伪装地址/伪装端口,这样数据连接就建立起来了。
2.4.2. 目的转换时的处理
在FW上配置目的转换和源转换,规则如下:
ipmasqadm portfw -a -P tcp -L 192.168.0.88 21 -R 10.0.0.1 21
ipchains -A forward -s 10.0.0.0/8 -j MASQ
A上运行FTP的服务器,B上运行FTP的客户端,B从服务器A上取文件,过程如下:
[1] B(端口为1735)发起到FW(端口为21)的TCP连接,在ip_fw_demasquerade中将会创建地址伪装结构:
[图2.9]同时这个结构会与ip_masq_app绑定(ip_masq_bind_app中会根据伪装结构中的目的端口和伪装端口去匹配在系统中注册的ip_masq_app结构。在ip_masq_ftp的实现中,默认只有OUTBOUND方向的端口为21的ip_masq_app结构,这里假设系统也注册了INBOUND方向的端口为21的ip_masq_app结构)。然后FW会将包中的目的地址/目的端口改为伪装结构中的源地址/源端口。
A返回的应答包会在ip_fw_masquerade中匹配到这个伪装结构,并将其中的源地址/源端口改为伪装结构中的伪装地址/伪装端口。
[2] 如果B发出"PORT"命令,建立从A到B的主动连接取文件。
B发出PORT命令,这个命令中有B的地址和监听端口(port 192.168.0.1.6.217),这个包会在ip_masq_app_pkt_in中由masq_ftp_in处理,如图:
[图2.10]
处理时先检查伪装结构的state,这与masq_ftp_out中的检查是一样的。然后检查ip_masq_app的type,这里匹配到的是IP_MASQ_IN_BOUND,所以使用INBOUND方向上的过程。它在数据包中匹配"PORT"字符串,如果找到,接下来在"PORT"后面的字符串中找到B的地址和端口。然后检查包含这个地址和端口的伪装结构是否已创建,如果没有,则创建一个新的伪装结构,如图:
[图2.11]
(这个结构中的源端口为0是因为此时还不知到下一步A到B的连接的源端口)
最后的操作是将ms-app_data置为NULL。这一步操作对应第一种情况中A发出"PASV"命令时所置的标志。
(在masq_ftp_in没有替换数据中的地址和端口是因为这个地址和端口都是外网的地址和端口,内网主机发起连接时可以找到这个地址和端口,这与masq_ftp_out中的处理不同)
[3] A(端口为20)发起到B(端口为1753)的TCP连接,ip_fw_masque