linux2.4中netfilter_nat_alg机制分析--以FTP流程为例,分析NAT和ALG
以FTP流程为例,分析NAT和ALG
网络环境:
192.168.1.2-----192.168.1.1 NAT 200.100.100.1------202.100.100.2
阶段一:
src/dst/sport/dport:192.168.1.2/202.100.100.2/3333/21 syn client->server control connection
iptables规则:
iptables –t nat –I POSTROUTING –o ppp0 –j MASQUERADE
1. PREROUTING:
Conntrack:ip_conntrack_in中,记录下:
tuple:
tuple.src.ip=192.168.1.2;
tuple.dst.ip=202.100.100.2;
tuple.dst.protonum=tcp;
tuple.src.u.tcp.port=3333;
tuple.dst.u.tcp.port=21;
和repl_tuple:
repl_tuple.src.ip=202.100.100.2;
repl_tuple.dst.ip=192.168.1.2;
repl_tuple.dst.protonum=tcp;
repl_tuple.src.u.tcp.port=21;
repl_tuple.dst.u.tcp.port=3333;
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple;
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack;
conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple;
conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack;
由于是控制报文,故找不到expected;
但是,可以找到conntrack->helper = (ip_conntrack_helper *)&ftp;
*ctinfo = IP_CT_NEW;且在conntrack->helper中不处理
NAT:在ip_nat_fn中,最终调用alloc_null_binding建立空的NAT转换表:
mr.rangesize=1;
mr.range[0].flags=IP_NAT_RANGE_MAP_IPS;
mr.range[0].min_ip=202.100.100.2;
mr.range[0].max_ip=202.100.100.2;
mr.range[0].min=0;
mr.range[0].max=0;
不进行NAT地址转换处理
2. POSTROUTING:
Conntrack:ip_refrag,不处理conntrack相关;
NAT:ip_nat_out-> ip_nat_fn->:
ip_nat_rule_find,根据iptables规则,建立NAT地址转换表:
newrange.rangesize=1;
newrange.range[0].flags|=IP_NAT_RANGE_MAP_IPS;
newrange.range[0].min_ip=202.100.100.1;
newrange.range[0].max_ip=202.100.100.1;
newrange.range[0].min=mr.range[0].min;
newrange.range[0].max= mr.range[0].min;
再通过ip_nat_setup_info:
² 根据newrange,得到SNAT后的orignal方向的tuple为:
new_tuple:
src/dst/sport/dport:202.100.100.1/202.100.100.2/2222/21
而原来的original tuple为:
orig_tp:
src/dst/sport/dport:192.168.1.2/202.100.100.2/3333/21
(这里,源端口可以改变)
² 根据new_tuple,得到反向reply tuple:
reply:
src/dst/sport/dport:202.100.100.2/202.100.100.1/21/2222
从而更新原有conntrack的reply tuple为这里新的reply tuple,用于识别反向报文:
IP_CT_DIR_ORIGINAL:src/dst/sport/dport:192.168.1.2/202.100.100.2/3333/21;
IP_CT_DIR_REPLY:src/dst/sport/dport:202.100.100.2/202.100.100.1/21/2222;
² 同时,再记录原有orig_tp的反向tuple:
inv_tuple:
src/dst/sport/dport:202.100.100.2/192.168.1.2/21/3333
建立了上述四种tuple,并更改了conntrack的reply tuple后(通过新的reply更改),根据orig_tp和new_tuple建立NAT地址转换信息节点:
² 若orig_tp和new_tuple的srcip不同,则可以判定是要进行SNAT:
info->manips[info->num_manips++] =
((struct ip_nat_info_manip)
{ IP_CT_DIR_ORIGINAL, hooknum,IP_NAT_MANIP_SRC, new_tuple.src });
/*用于ORIGINAL方向的报文的SNAT
manip.direction=IP_CT_DIR_ORIGINAL;
manip.hooknum=NF_IP_POST_ROUTING;
manip.maniptype=IP_NAT_MANIP_SRC;
manip.manip.ip=202.100.100.1;
manip.manip.u.tcp.port=2222;
*/
info->manips[info->num_manips++] =
((struct ip_nat_info_manip)
{ IP_CT_DIR_REPLY, opposite_hook[hooknum],IP_NAT_MANIP_DST, orig_tp.src });
/*用于回应报文(REPLY方向)的DNAT
manip.direction=IP_CT_DIR_REPLY;
manip.hooknum=NF_IP_PRE_ROUTING;
manip.maniptype=IP_NAT_MANIP_DST;
manip.manip.ip=192.168.1.2;
manip.manip.u.tcp.port=3333;
*/
最后,调用do_bindings,做真正的SNAT处理:
调用manip_pkt,将skb由:
192.168.1.2/202.100.100.2/3333/21更改为:202.100.100.1/202.100.100.2/2222/21
阶段二:
src/dst/sport/dport:202.100.100.2/202.100.100.1/21/2222 syn +ack
server->client control connection
1. PREROUTING:
Conntrack:ip_conntrack_in中,得到:
tuple:
tuple.src.ip=202.100.100.2;
tuple.dst.ip=202.100.100.1;
tuple.dst.protonum=tcp;
tuple.src.u.tcp.port=21;
tuple.dst.u.tcp.port=2222;
它正好是conntrck->tuplehash[IP_CT_DIR_REPLY]方向的tuple记录信息
*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
set_bit(IPS_SEEN_REPLY_BIT, &ct->status);
NAT:ip_nat_fn中,得到先前建立的NAT地址信息表:info = &ct->nat.info,
然后通过do_bindings进行DNAT处理:
调用manip_pkt,将skb由:
202.100.100.2/202.100.100.1/21/2222更改为:202.100.100.2/192.168.1.2/21/3333
2.POSTROUTING
无处理;
接下来,clinet回给server的ack,也可以正确进行SNAT,到达server(过程类似阶段一)
/***********************PASV模式**************************************/
阶段三:
src/dst/sport/dport:202.100.100.2/202.100.100.1/21/2222 server->client control connection
含有内容:“227 Entering Passive Mode(202,100,100,2,5,6)\r\n”
1. PREROUTING
Conntrack:ip_conntrack_in中,得到:
tuple:
tuple.src.ip=202.100.100.2;
tuple.dst.ip=202.100.100.1;
tuple.dst.protonum=tcp;
tuple.src.u.tcp.port=21;
tuple.dst.u.tcp.port=2222;
它正好是conntrck->tuplehash[IP_CT_DIR_REPLY]方向的tuple记录信息
然后,调用conntrack->help指针,进行ALG处理:
首先,记录conntrack的本报文方向(这里就是IP_CT_DIR_REPLY方向)上的tuple.src.ip信息:
array[0] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 24) & 0xFF; //202
array[1] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 16) & 0xFF; //100
array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF; //100
array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF; //2
然后,解析报文,得到报文中的地址信息;更新array数组信息为报文应用层中的地址信息
最后,若报文应用层中的地址信息和本方向的tuple.src.ip信息一致,则:
建立exp结构信息:
exp->seq = ntohl(tcph->seq) + matchoff;
exp_ftp_info->len = matchlen;
exp_ftp_info->ftptype = search[i].ftptype;
exp_ftp_info->port = array[4] << 8 | array[5];//应用层中定义的数据连接的端口信息(256×5+6=1286)
创建exp的tuple信息(即所期待的tuple):
exp.tuple.src.ip=192.168.1.2;
exp.tuple.dst.ip=202.100.100.2;
exp.tuple.dst.protonum= IPPROTO_TCP;
exp.tuple.src.u.all=0;
exp.tuple.dst.u.tcp.port=1286;
使用ip_conntrack_expect_related,将exp与现有的conntrack关联起来:
exp->expectant=conntrack;
exp->sibling=NULL;
NAT:ip_nat_fn中,得到先前建立的NAT地址信息表:info = &ct->nat.info,
然后通过do_bindings进行DNAT处理:
调用manip_pkt,将skb由:
202.100.100.2/202.100.100.1/21/2222更改为:202.100.100.2/192.168.1.2/21/3333
然后,由于这里的conntrck的exp结构已经建立,所以会调用ip_nat_ftp.c中的help(再调用ftp_data_fixup)进行NAT的ALG处理:
对于PASV模式,有:
newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
/* Expect something from client->server */
newtuple.src.ip =
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
newtuple.dst.ip =
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
newtuple.dst.protonum = IPPROTO_TCP;
newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
/*所期待的数据连接的tuple
newip=202.100.100.2;
newtuple.src.ip=192.168.1.2;
newtuple.dst.ip=202.100.100.2;
newtuple.dst.protonum = IPPROTO_TCP;
newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; //0
*/
利用newip(202.100.100.2)和新的port(256×5+6=1286),更新skb的应用层信息(这里应用层信息还是“227 Entering Passive Mode(202,100,100,2,5,6)\r\n”)
(注:这里若采用clinet->server发送PORT命令,则其exp结构的创建过程更具有代表性)
2.POSTROUTING
无处理;
/************数据连接开始***************/
阶段四:
src/dst/sport/dport:192.168.1.2/202.100.100.2/1111/1286 client->server
SYN data connection
1. PREROUTING
Conntrack:ip_conntrack_in中,得到
tuple:
tuple.dst.ip=202.100.100.2;
tuple.dst.protonum=tcp;
tuple.src.u.tcp.port=1111;
tuple.dst.u.tcp.port=1286;
这是一个新的tuple,所以调用init_conntrack,得到反向tuple:
repl_tuple:
repl_tuple.src.ip=202.100.100.2;
repl_tuple.dst.ip=192.168.1.2;
repl_tuple.dst.protonum=tcp;
repl_tuple.src.u.tcp.port=1286;
repl_tuple.dst.u.tcp.port=1111;
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple;
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack;
conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple;
conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack;
通过expected = LIST_FIND(&ip_conntrack_expect_list, expect_cmp,struct ip_conntrack_expect *, tuple);,可以得到其所属exp,并找到conntrack->helper
最后,有:
__set_bit(IPS_EXPECTED_BIT, &conntrack->status);
conntrack->master = expected;
expected->sibling = conntrack;
至此,控制报文和数据报文建立如下关系:
控制报文的conntrack:
IP_CT_DIR_ORIGINAL:src/dst/sport/dport:192.168.1.2/202.100.100.2/3333/21
IP_CT_DIR_REPLY:src/dst/sport/dport:202.100.100.2/202.100.100.1/21/2222
控制报文有conntrack_help的handle处理;
数据报文的conntrack:
IP_CT_DIR_ORIGINAL:src/dst/sport/dport:192.168.1.2/202.100.100.2/1111/1286
IP_CT_DIR_REPLY:src/dst/sport/dport:202.100.100.2/192.168.1.2/1286/1111
数据报文的conntrack_help为NULL;
它们之间联系用的exp为:
exp_ftp_info->port = 256*5+6 = 1286;
exp.tuple.src.ip=192.168.1.2;
exp.tuple.dst.ip=202.100.100.2;
exp.tuple.dst.protonum= IPPROTO_TCP;
exp.tuple.src.u.all=0;
exp.tuple.dst.u.tcp.port=1286;
设置*ctinfo = IP_CT_RELATED;(相关)
NAT:ip_nat_fn中,得到skb的conntrack信息,并得到info = &ct->nat.info。显然,这个conntrack是新的,并没有nat.info。并且,这个conntrack是有exp的,所以,会进行call_expect操作。
这里进入的就是ftp的expect函数:ftp_nat_expected:
首先,获取conntrack->master,就是ftp的控制连接的conntrack;以及exp_info信息;
由于是PASV模式,所以得到:
newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; //202.100.100.2
newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; //202.100.100.1
由于HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST,所以得到:
newip = newdstip; //202.100.100.2;
建立mr信息:
mr.rangesize=1;
mr.range[0].flags= IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED;
mr.range[0].min_ip=202.100.100.2;
mr.range[0].max_ip=202.100.100.2;
mr.range[0].min= exp_ftp_info->port;
mr.range[0].max= exp_ftp_info->port;
2. POSTROUTING
Conntrack:ip_refrag,不处理conntrack相关;
NAT:ip_nat_fn中,得到skb的conntrack信息,并得到info = &ct->nat.info。显然,这个conntrack是新的,并没有nat.info。并且,这个conntrack是有exp的,所以,会进行call_expect操作。
这里进入的就是ftp的expect函数:ftp_nat_expected:
首先,获取conntrack->master,就是ftp的控制连接的conntrack;以及exp_info信息;
由于是PASV模式,所以得到:
newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; //202.100.100.2
newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; //202.100.100.1
由于HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC,所以得到:
newip = newdstip; //202.100.100.1;
建立mr信息:
mr.rangesize=1;
mr.range[0].flags= IP_NAT_RANGE_MAP_IPS;
mr.range[0].min_ip=202.100.100.1;
mr.range[0].max_ip=202.100.100.1;
mr.range[0].min= exp_ftp_info->port;
mr.range[0].max= exp_ftp_info->port;
最后,调用ip_nat_setup_info,得到:
orig_tp:src/dst/sport/dport:192.168.1.2/202.100.100.2/1111/1286;
new_tp:src/dst/sport/dport:202.100.100.1/202.100.100.2/4444/1286;
reply_tp:src/dst/sport/dport:202.100.100.2/202.100.100.1/1286/4444;
inv_tp:src/dst/sport/dport:202.100.100.2/192.168.1.2/1286/1111;
通过ip_conntrack_alter_reply,更改数据报文的reply方向的tuple为:src/dst/sport/dport:202.100.100.2/202.100.100.1/1286/4444
建立nat_info:
/*用于ORIGINAL方向的报文的SNAT
manip.direction=IP_CT_DIR_ORIGINAL;
manip.hooknum=NF_IP_POST_ROUTING;
manip.maniptype=IP_NAT_MANIP_SRC;
manip.manip.ip=202.100.100.1;
manip.manip.u.tcp.port=4444;
*/
/*用于REPLY方向的报文的DNAT
manip.direction=IP_CT_DIR_REPLY;
manip.hooknum=NF_IP_PRE_ROUTING;
manip.maniptype=IP_NAT_MANIP_DST;
manip.manip.ip=192.168.1.2;
manip.manip.u.tcp.port=1111;
*/
从而得到数据连接的新的conntrack:
IP_CT_DIR_ORIGINAL:src/dst/sport/dport:192.168.1.2/202.100.100.2/1111/1286
IP_CT_DIR_REPLY:src/dst/sport/dport:202.100.100.2/202.100.100.1/1286/4444
然后,调用do_bindings,进行skb的地址转换:由:
192.168.1.2/202.100.100.2/1111/1286更改为:202.100.100.1/202.100.100.2/4444/1286
阶段五:
src/dst/sport/dport:202.100.100.2/202.100.100.1/1286/4444 server->client
SYN+ACK data connection
1. PREROUTING:
Conntrack:ip_conntrack_in中,得到:
tuple:
tuple.src.ip=202.100.100.2;
tuple.dst.ip=202.100.100.1;
tuple.dst.protonum=tcp;
tuple.src.u.tcp.port=1286;
tuple.dst.u.tcp.port=4444;
它正好是conntrck->tuplehash[IP_CT_DIR_REPLY]方向的tuple记录信息
*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
set_bit(IPS_SEEN_REPLY_BIT, &ct->status);
NAT:ip_nat_fn中,得到先前建立的NAT地址信息表:info = &ct->nat.info,
然后通过do_bindings进行DNAT处理:
调用manip_pkt,将skb由:
202.100.100.2/202.100.100.1/1286/4444更改为:202.100.100.2/192.168.1.2/1286/1111
2.POSTROUTING
无处理;
/***********************PORT模式**************************************/
阶段三:
src/dst/sport/dport:192.168.1.2/202.100.100.2/3333/21 client>server control connection
含有内容:“227 Entering PORT Mode(192.168.1.2,5,6)\r\n”
2. PREROUTING
Conntrack:ip_conntrack_in中,得到:
tuple:
tuple.src.ip=192.168.1.2;
tuple.dst.ip=202.100.100.2;
tuple.dst.protonum=tcp;
tuple.src.u.tcp.port=3333;
tuple.dst.u.tcp.port=21;
*ctinfo = IP_CT_ESTABLISHED;
调用conntrack->help指针,进行ALG处理:
首先,记录conntrack的本报文方向(这里就是IP_CT_DIR_ORIGINAL方向)上的tuple.src.ip信息:
array[0] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 24) & 0xFF; //192
array[1] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 16) & 0xFF; //168
array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF; //1
array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF; //2
然后,解析报文,得到报文中的地址信息;更新array数组信息为报文应用层中的地址信息
最后,若报文应用层中的地址信息和本方向的tuple.src.ip信息一致,则:
建立exp结构信息:
exp->seq = ntohl(tcph->seq) + matchoff;
exp_ftp_info->len = matchlen;
exp_ftp_info->ftptype = search[i].ftptype;
exp_ftp_info->port = array[4] << 8 | array[5];//应用层中定义的数据连接的端口信息(256×5+6=1286)
创建exp的tuple信息(即所期待的tuple):
exp.tuple.src.ip=202.100.100.2;
exp.tuple.dst.ip=192.168.1.2;
exp.tuple.dst.protonum= IPPROTO_TCP;
exp.tuple.src.u.all=0;
exp.tuple.dst.u.tcp.port=1286;
使用ip_conntrack_expect_related,将exp与现有的conntrack关联起来:
exp->expectant=conntrack;
exp->sibling=NULL;
NAT:ip_nat_fn中,得到先前建立的NAT地址信息表:info = &ct->nat.info,
然后通过do_bindings进行SNAT处理:
/*
manip.direction=IP_CT_DIR_ORIGINAL;
manip.hooknum=NF_IP_POST_ROUTING;
manip.maniptype=IP_NAT_MANIP_SRC;
manip.manip.ip=202.100.100.1;
manip.manip.u.tcp.port=2222;
*/
这里由于Hooknum是NF_IP_PRE_ROUTING,所以无法匹配manip,故不做NAT转换处理;
然后,由于这里的conntrck的exp结构已经建立,所以会调用ip_nat_ftp.c中的help进行NAT的ALG处理:
这里由于Hooknum是NF_IP_PRE_ROUTING,且dir == IP_CT_DIR_ORIGINAL,故不做ALG转换处理;
2.POSTROUTING
Conntrack:ip_refrag,不处理conntrack相关;
NAT:ip_nat_out-> ip_nat_fn中,得到先前建立的NAT地址信息表:info = &ct->nat.info,
然后通过do_bindings进行SNAT处理:
/*
manip.direction=IP_CT_DIR_ORIGINAL;
manip.hooknum=NF_IP_POST_ROUTING;
manip.maniptype=IP_NAT_MANIP_SRC;
manip.manip.ip=202.100.100.1;
manip.manip.u.tcp.port=2222;
*/
将skb由:
192.168.1.2/202.100.100.2/3333/21转换为202.100.100.1/202.100.100.2/2222/21
然后,由于这里的conntrck的exp结构已经建立,所以会调用ip_nat_ftp.c中的help(再调用ftp_data_fixup)进行NAT的ALG处理:
对于PORT模式,有:
newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
/* Expect something from server->client */
newtuple.src.ip =
ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
newtuple.dst.ip =
ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
newtuple.dst.protonum = IPPROTO_TCP;
newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
/*
且此时的conntrack为:
IP_CT_DIR_ORIGINAL:src/dst/sport/dport:192.168.1.2/202.100.100.2/3333/21;
IP_CT_DIR_REPLY:src/dst/sport/dport:202.100.100.2/202.100.100.1/21/2222;
*/
/*所期待的数据连接的tuple
newip=202.100.100.1;
newtuple.src.ip=202.100.100.2;
newtuple.dst.ip=202.100.100.1;
newtuple.dst.protonum = IPPROTO_TCP;
newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; //0
*/
并更新exp信息为:
/*
exp->seq = ntohl(tcph->seq) + matchoff;
exp_ftp_info->len = matchlen;
exp_ftp_info->ftptype = search[i].ftptype;
exp_ftp_info->port = array[4] << 8 | array[5];//应用层中定义的数据连接的端口信息(256×5+6=1286)
创建exp的tuple信息(即所期待的tuple):
exp.tuple.src.ip=202.100.100.2;
exp.tuple.dst.ip=202.100.100.1;
exp.tuple.dst.protonum= IPPROTO_TCP;
exp.tuple.src.u.all=0;
exp.tuple.dst.u.tcp.port=1286;
*/
利用newip(202.100.100.1)和新的port(256×5+6=1286),更新skb的应用层信息(这里应用层信息修改为“227 Entering PORT Mode(202,100,100,1,5,6)\r\n”)
/************数据连接开始***************/
阶段四:
src/dst/sport/dport: 202.100.100.2/202.100.100.1/20/1286 server->client
SYN data connection
1. PREROUTING
Conntrack:ip_conntrack_in中,得到
tuple.src.ip=202.100.100.2;
tuple.dst.ip=202.100.100.1;
tuple.dst.protonum=tcp;
tuple.src.u.tcp.port=20;
tuple.dst.u.tcp.port=1286;
这是一个新的tuple,所以调用init_conntrack,得到反向tuple:
repl_tuple:
repl_tuple.src.ip=202.100.100.1;
repl_tuple.dst.ip=202.100.100.2;
repl_tuple.dst.protonum=tcp;
repl_tuple.src.u.tcp.port=1286;
repl_tuple.dst.u.tcp.port=20;
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple;
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack;
conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple;
conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack;
通过expected = LIST_FIND(&ip_conntrack_expect_list, expect_cmp,struct ip_conntrack_expect *, tuple);,可以得到其所属exp,并找到conntrack->helper
最后,有:
__set_bit(IPS_EXPECTED_BIT, &conntrack->status);
conntrack->master = expected;
expected->sibling = conntrack;
至此,控制报文和数据报文建立如下关系:
控制报文的conntrack:
IP_CT_DIR_ORIGINAL:src/dst/sport/dport:192.168.1.2/202.100.100.2/3333/21
IP_CT_DIR_REPLY:src/dst/sport/dport:202.100.100.2/202.100.100.1/21/2222
控制报文有conntrack_help的handle处理;
数据报文的conntrack:
IP_CT_DIR_ORIGINAL:src/dst/sport/dport:202.100.100.2/202.100.100.1/20/1286
IP_CT_DIR_REPLY:src/dst/sport/dport:202.100.100.1/202.100.100.2/1286/20
数据报文的conntrack_help为NULL;
它们之间联系用的exp为:
exp_ftp_info->port = 256*5+6 = 1286;
exp.tuple.src.ip=202.100.100.2;
exp.tuple.dst.ip=202.100.100.1;
exp.tuple.dst.protonum= IPPROTO_TCP;
exp.tuple.src.u.all=0;
exp.tuple.dst.u.tcp.port=1286;
设置*ctinfo = IP_CT_RELATED;(相关)
NAT:ip_nat_fn中,得到skb的conntrack信息,并得到info = &ct->nat.info。显然,这个conntrack是新的,并没有nat.info。并且,这个conntrack是有exp的,所以,会进行call_expect操作。
这里进入的就是ftp的expect函数:ftp_nat_expected:
首先,获取conntrack->master,就是ftp的控制连接的conntrack;以及exp_info信息;
由于是PORT模式,所以得到:
newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;//192.168.1.2
newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;//202.100.100.2
由于HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST,所以得到:
newip = newdstip; //192.168.1.2;
建立mr信息:
mr.rangesize=1;
mr.range[0].flags= IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED;
mr.range[0].min_ip=192.168.1.2;
mr.range[0].max_ip=192.168.1.2;
mr.range[0].min= exp_ftp_info->port;
mr.range[0].max= exp_ftp_info->port;
然后,调用ip_nat_setup_info,得到:
orig_tp:src/dst/sport/dport:202.100.100.2/202.100.100.1/20/1286;
new_tp:src/dst/sport/dport:202.100.100.2/192.168.1.2/20/4444;
reply_tp:src/dst/sport/dport:192.168.1.2/202.100.100.2/4444/20;
inv_tp:src/dst/sport/dport:202.100.100.1/202.100.100.2/1286/20;
通过ip_conntrack_alter_reply,更改数据报文的reply方向的tuple为:src/dst/sport/dport:192.168.1.2/202.100.100.2/1286/4444
建立nat_info:
/*用于ORIGINAL方向的报文的DNAT
manip.direction=IP_CT_DIR_ORIGINAL;
manip.hooknum=NF_IP_PRE_ROUTING;
manip.maniptype=IP_NAT_MANIP_DST;
manip.manip.ip=192.168.1.2;
manip.manip.u.tcp.port=4444;
*/
/*用于REPLY方向的报文的SNAT
manip.direction=IP_CT_DIR_REPLY;
manip.hooknum=NF_IP_POST_ROUTING;
manip.maniptype=IP_NAT_MANIP_SRC;
manip.manip.ip=202.100.100.1;
manip.manip.u.tcp.port=1286;
*/
这里由于conntrack->master存在,所以不会挂接help
从而得到数据连接的新的conntrack:
IP_CT_DIR_ORIGINAL:src/dst/sport/dport:202.100.100.2/202.100.100.1/20/1286
IP_CT_DIR_REPLY:src/dst/sport/dport:192.168.1.2/202.100.100.2/4444/20
然后,调用do_bindings,进行skb的地址转换:由:
202.100.100.2/202.100.100.2/20/1286更改为:202.100.100.2/192.168.1.2/20/4444
2.POSTROUTING
Conntrack:ip_refrag,不处理conntrack相关;
NAT:ip_nat_fn中,得到skb的conntrack信息,并得到info = &ct->nat.info。直接调用do_bindings:
由于不满足
info->manips[i].direction == dir
&& info->manips[i].hooknum == hooknum
故不进行地址变换处理(实际上在PREROUTING中已经转变过了,这里不需要再做了);
阶段五:
src/dst/sport/dport:192.168.1.2/202.100.100.2/4444/20 client->server
SYN+ACK data connection
1. PREROUTING:
Conntrack:ip_conntrack_in中,得到:
tuple:
tuple.src.ip=192.168.1.2;
tuple.dst.ip=202.100.100.2;
tuple.dst.protonum=tcp;
tuple.src.u.tcp.port=4444;
tuple.dst.u.tcp.port=20;
它正好是conntrck->tuplehash[IP_CT_DIR_REPLY]方向的tuple记录信息
*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
set_bit(IPS_SEEN_REPLY_BIT, &ct->status);
NAT:ip_nat_fn中,得到先前建立的NAT地址信息表:info = &ct->nat.info,
进入do_bindings,但由于不满足:
info->manips[i].direction == dir
&& info->manips[i].hooknum == hooknum
故不处理;
且没有helper
2. POSTROUTING
Conntrack:ip_refrag,不处理conntrack相关;
NAT:ip_nat_fn中,得到skb的conntrack信息,并得到info = &ct->nat.info。直接调用do_bindings:
满足
info->manips[i].direction == dir(IP_CT_DIR_REPLY)
&& info->manips[i].hooknum == hooknum(NF_IP_POST_ROUTING)
故进行SNAT转换,skb的地址转换:由:
192.168.1.2/202.100.100.2/4444/20更改为:202.100.100.1/202.100.100.2/1286/20
无helper
/***********************总结*******************************/
1. Netfilter:提供一个5个hook点的框架;
2. Conntrack:记录报文的运行轨迹。包括ORIGINAL和REPLY两个方向;
3. NAT:依据iptables的NAT规则,建立Conntrack的nat_info信息,并更改报文的地址信息;
4. ALG:用于更改某些报文的应用层中的地址信息;
处理模式:
1. 控制报文:
² PREROUTING:
Ø Conntrack:建立新的Conntrack信息:ORIGINAL+REPLY;
Ø NAT:无
² POSTROUTING:
Ø Conntrack:无
Ø NAT:根据iptables规则,建立该Conntrack的nat_info:SNAT+DNAT,并更改报文地址;并更改Conntrack中记录的REPLY方向的tuple信息(地址改为NAT处理后的值)
2. 应用层含有数据连接地址信息的控制报文:
² PREROUTING:
Ø Conntrack:找到已经建立的Conntrack信息;利用conntrack->help,建立exp结构,建立所exp的tuple信息
Ø NAT:可能不处理 或者 利用nat->help更新应用层报文中的地址信息,并更改exp中的地址信息
² POSTROUTING:
Ø Conntrack:无
Ø NAT:可能不处理 或者 利用nat->help更新应用层报文中的地址信息,并更改exp中的地址信息
3. 数据报文到达:
² PREROUTING:
Ø Conntrack:建立新的Conntrack信息:ORIGINAL+REPLY;且正好就是要expect的tuple;故与控制报文的Conntrack关联上;
Ø NAT:通过调用nat->exp函数,利用控制报文的Conntrack信息,建立自己的nat_info:SNAT+DNAT;并更改Conntrack中记录的REPLY方向的tuple信息(地址改为NAT处理后的值)
² POSTROUTING:
Ø Conntrack:无
Ø NAT:利用自己的nat_info,更改报文的地址信息