Netfilter中对FTP连线跟踪的处理
时间:2006-04-03 来源:noahtang
Netfilter中对FTP连线跟踪的处理
一、首先,我们要搞清楚FTP协议的一些特点。
FTP协议与大多数协议最大的一个不同是:它使用双向的多个连接,而且使用的端口很难预计。
FTP连接包含一个控制连接(control connection)。这个连接用于传递客户端的命令和服务器端对命 令的响应。它使用FTP协议众所周知的21端口(当然也可使用其它端口),生存期是整个FTP会话时间。
还包含几个数据连接(data connection)。这些连接用于传输文件和其它数据,例如:目录列表等。这种连接在需要数据传输时建立,而一旦数据传输完毕就关闭,每次使用的端口也不一定相同。而且,数据连接既可能是客户端发起的,也可能是服务器端发起的。
根据建立数据连接发起方的不同,FTP可分为两种不同的模式:主动(Port Mode)模式和被动模式(Pasv Mode)。这两种不同模式数据连接建立方式分别如下:
(假设客户端为C,服务端为S)
Port模式:
当客户端C与服务端S建立控制连接后,若使用Port模式,那么客户端C会发送一条命令告诉服务端S:客户端C在本地打开了一个端口N在等着你进行数据连接。当服务端S收到这个Port命令后就会向客户端打开的那个端口N进行连接,这种数据连接就建立了。
例:客户端->服务器端:PORT 192,168,1,1,15,176
客户端<-服务器端:200 PORT command successful.
在上面的例子中192,168,1,1构成IP地址,15,176构成端口号(15*256+176)。
Pasv模式:
当客户端C与服务端S建立控制连接后,若使用Pasv模式,那么客户端C会向服务端S发送一条Pasv命令,服务端S会对该命令发送回应信息,这个信息是:服务端S在本地打开了一个端口M,你现在去连接我吧。当客户端C收到这个信息后,就可以向服务端S的M端口进行连接,连接成功后,数据连接也就建立了。
例:客户端->服务器端:PASV
客户端<-服务器端:227 Entering Passive Mode (192,168,1,2,14,26).
从上面的解释中,可以看到两种模式主要的不同是数据连接建立的不同。对于Port模式,是客户端C在本地打开一个端口等服务端S去连接而建立数据连接;而Pasv模式则是服务端S打开一个端口等待客户端C去建立一个数据连接。
二、Netfilter对FTP连线跟踪的处理
Netfilter对FTP连线跟踪的处理主要集中在ip_conntrack_ftp.c中。
在ip_conntrack_ftp.c中最重要的一个结构为ftp_search,它对FTP中各种模式的特征进行了总结。比如PORT模式,最重要的一个特征是在报文中会出现"PORT"字符串,IP地址前的字符为空格,结尾字符为"\r",这两字符之间有一串数字(参考前面对FTP协议的分析),这一串数字就是IP地址和端口号;PASSIVE模式,在报文中会出现"227"字符串,IP地址前的字符为"(",结尾字符为")".
static struct ftp_search {
enum ip_conntrack_dir dir;
const char *pattern;
size_t plen;
char skip;
char term;
enum ip_ct_ftp_type ftptype;
int (*getnum)(const char *, size_t, u_int32_t[], char);
} search[] = {
{
IP_CT_DIR_ORIGINAL, /*PORT模式*/
"PORT", sizeof("PORT") - 1, ' ', '\r',
IP_CT_FTP_PORT,
try_rfc959,
},
{
IP_CT_DIR_REPLY, /*PASSIVE模式*/
"227 ", sizeof("227 ") - 1, '(', ')',
IP_CT_FTP_PASV,
try_rfc959,
},
{
IP_CT_DIR_ORIGINAL,
"EPRT", sizeof("EPRT") - 1, ' ', '\r',
IP_CT_FTP_EPRT,
try_eprt,
},
{
IP_CT_DIR_REPLY,
"229 ", sizeof("229 ") - 1, '(', ')',
IP_CT_FTP_EPSV,
try_espv_response,
},
};
对FTP连线跟踪的处理是从函数help()开始的。在该函数中定义了一个数组u_int32_t array[6] = { 0 };该数组就是用来保存IP地址和端口号的,其中array[0]~array[3]保存IP地址,array[4]和array[5]和起来组成端口号,计算方法为array[4]*256+array[5]。
下面使用前面定义的搜索模式来搜索报文,把获得的IP地址和端口号保存在array中。
for (i = 0; i < sizeof(search) / sizeof(search[0]); i++) {
if (search[i].dir != dir)
continue;
found = find_pattern(data, datalen, search[i].pattern,
search[i].plen, search[i].skip, search[i].term,
&matchoff, &matchlen, array, search[i].getnum);
if (found)
break;
}
在find_pattern函数中有*numlen = getnum(data + i, dlen - i, array, term);这条语句,这儿的getnum分别对应ftp_search中的try_rfc959(),try_eprt(),try_espv_response(),这些函数就是主要对报文进行分析的函数。
在获取了IP地址和端口号后就可以建立元组,然后再建立期望连接,并把期望连接加入期望连接表中。下面这段代码就是建立元组和期望连接。
t = ((struct ip_conntrack_tuple)
{ { ct->tuplehash[!dir].tuple.src.ip,
{ 0 } },
{ htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]),
{ htons(array[4] << 8 | array[5]) },
IPPROTO_TCP }});
mask = ((struct ip_conntrack_tuple)
{ { 0xFFFFFFFF, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
/* Ignore failure; should only happen with NAT */
ip_conntrack_expect_related(ct, &t, &mask, NULL);
之所以称为期望连接,是因为现在还处于控制连接阶段,数据连接此时还未建立,但将来是会建立的。如此处理之后,我们就可以预先获取建立数据连接的信息,当真正的数据连接需要建立时,我们只需在期望连接表中进行查找,保证了多连接协议的正确处理,同时还提高了效率。
以上是我在读Netfilter源代码时的一些心得,希望对大家有些帮助。对于文章中的不当之处也希望大家予以指正!
一、首先,我们要搞清楚FTP协议的一些特点。
FTP协议与大多数协议最大的一个不同是:它使用双向的多个连接,而且使用的端口很难预计。
FTP连接包含一个控制连接(control connection)。这个连接用于传递客户端的命令和服务器端对命 令的响应。它使用FTP协议众所周知的21端口(当然也可使用其它端口),生存期是整个FTP会话时间。
还包含几个数据连接(data connection)。这些连接用于传输文件和其它数据,例如:目录列表等。这种连接在需要数据传输时建立,而一旦数据传输完毕就关闭,每次使用的端口也不一定相同。而且,数据连接既可能是客户端发起的,也可能是服务器端发起的。
根据建立数据连接发起方的不同,FTP可分为两种不同的模式:主动(Port Mode)模式和被动模式(Pasv Mode)。这两种不同模式数据连接建立方式分别如下:
(假设客户端为C,服务端为S)
Port模式:
当客户端C与服务端S建立控制连接后,若使用Port模式,那么客户端C会发送一条命令告诉服务端S:客户端C在本地打开了一个端口N在等着你进行数据连接。当服务端S收到这个Port命令后就会向客户端打开的那个端口N进行连接,这种数据连接就建立了。
例:客户端->服务器端:PORT 192,168,1,1,15,176
客户端<-服务器端:200 PORT command successful.
在上面的例子中192,168,1,1构成IP地址,15,176构成端口号(15*256+176)。
Pasv模式:
当客户端C与服务端S建立控制连接后,若使用Pasv模式,那么客户端C会向服务端S发送一条Pasv命令,服务端S会对该命令发送回应信息,这个信息是:服务端S在本地打开了一个端口M,你现在去连接我吧。当客户端C收到这个信息后,就可以向服务端S的M端口进行连接,连接成功后,数据连接也就建立了。
例:客户端->服务器端:PASV
客户端<-服务器端:227 Entering Passive Mode (192,168,1,2,14,26).
从上面的解释中,可以看到两种模式主要的不同是数据连接建立的不同。对于Port模式,是客户端C在本地打开一个端口等服务端S去连接而建立数据连接;而Pasv模式则是服务端S打开一个端口等待客户端C去建立一个数据连接。
二、Netfilter对FTP连线跟踪的处理
Netfilter对FTP连线跟踪的处理主要集中在ip_conntrack_ftp.c中。
在ip_conntrack_ftp.c中最重要的一个结构为ftp_search,它对FTP中各种模式的特征进行了总结。比如PORT模式,最重要的一个特征是在报文中会出现"PORT"字符串,IP地址前的字符为空格,结尾字符为"\r",这两字符之间有一串数字(参考前面对FTP协议的分析),这一串数字就是IP地址和端口号;PASSIVE模式,在报文中会出现"227"字符串,IP地址前的字符为"(",结尾字符为")".
static struct ftp_search {
enum ip_conntrack_dir dir;
const char *pattern;
size_t plen;
char skip;
char term;
enum ip_ct_ftp_type ftptype;
int (*getnum)(const char *, size_t, u_int32_t[], char);
} search[] = {
{
IP_CT_DIR_ORIGINAL, /*PORT模式*/
"PORT", sizeof("PORT") - 1, ' ', '\r',
IP_CT_FTP_PORT,
try_rfc959,
},
{
IP_CT_DIR_REPLY, /*PASSIVE模式*/
"227 ", sizeof("227 ") - 1, '(', ')',
IP_CT_FTP_PASV,
try_rfc959,
},
{
IP_CT_DIR_ORIGINAL,
"EPRT", sizeof("EPRT") - 1, ' ', '\r',
IP_CT_FTP_EPRT,
try_eprt,
},
{
IP_CT_DIR_REPLY,
"229 ", sizeof("229 ") - 1, '(', ')',
IP_CT_FTP_EPSV,
try_espv_response,
},
};
对FTP连线跟踪的处理是从函数help()开始的。在该函数中定义了一个数组u_int32_t array[6] = { 0 };该数组就是用来保存IP地址和端口号的,其中array[0]~array[3]保存IP地址,array[4]和array[5]和起来组成端口号,计算方法为array[4]*256+array[5]。
下面使用前面定义的搜索模式来搜索报文,把获得的IP地址和端口号保存在array中。
for (i = 0; i < sizeof(search) / sizeof(search[0]); i++) {
if (search[i].dir != dir)
continue;
found = find_pattern(data, datalen, search[i].pattern,
search[i].plen, search[i].skip, search[i].term,
&matchoff, &matchlen, array, search[i].getnum);
if (found)
break;
}
在find_pattern函数中有*numlen = getnum(data + i, dlen - i, array, term);这条语句,这儿的getnum分别对应ftp_search中的try_rfc959(),try_eprt(),try_espv_response(),这些函数就是主要对报文进行分析的函数。
在获取了IP地址和端口号后就可以建立元组,然后再建立期望连接,并把期望连接加入期望连接表中。下面这段代码就是建立元组和期望连接。
t = ((struct ip_conntrack_tuple)
{ { ct->tuplehash[!dir].tuple.src.ip,
{ 0 } },
{ htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]),
{ htons(array[4] << 8 | array[5]) },
IPPROTO_TCP }});
mask = ((struct ip_conntrack_tuple)
{ { 0xFFFFFFFF, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
/* Ignore failure; should only happen with NAT */
ip_conntrack_expect_related(ct, &t, &mask, NULL);
之所以称为期望连接,是因为现在还处于控制连接阶段,数据连接此时还未建立,但将来是会建立的。如此处理之后,我们就可以预先获取建立数据连接的信息,当真正的数据连接需要建立时,我们只需在期望连接表中进行查找,保证了多连接协议的正确处理,同时还提高了效率。
以上是我在读Netfilter源代码时的一些心得,希望对大家有些帮助。对于文章中的不当之处也希望大家予以指正!
相关阅读 更多 +