linux2.4中netfilter_nat_alg机制分析--以FTP流程为例,分析NAT和ALG

FTP流程为例,分析NATALG

网络环境:

192.168.1.2-----192.168.1.1 NAT 200.100.100.1------202.100.100.2

 

阶段一:

src/dst/sport/dport192.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

Conntrackip_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

Conntrackip_refrag,不处理conntrack相关;

NATip_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/dport202.100.100.1/202.100.100.2/2222/21

而原来的original tuple为:

orig_tp

src/dst/sport/dport192.168.1.2/202.100.100.2/3333/21

(这里,源端口可以改变)

² 根据new_tuple,得到反向reply tuple

reply

src/dst/sport/dport202.100.100.2/202.100.100.1/21/2222

从而更新原有conntrackreply tuple为这里新的reply tuple,用于识别反向报文:

IP_CT_DIR_ORIGINALsrc/dst/sport/dport192.168.1.2/202.100.100.2/3333/21

IP_CT_DIR_REPLYsrc/dst/sport/dport202.100.100.2/202.100.100.1/21/2222

² 同时,再记录原有orig_tp的反向tuple

inv_tuple

src/dst/sport/dport202.100.100.2/192.168.1.2/21/3333

 

       建立了上述四种tuple,并更改了conntrackreply tuple后(通过新的reply更改),根据orig_tpnew_tuple建立NAT地址转换信息节点:

² orig_tpnew_tuplesrcip不同,则可以判定是要进行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/dport202.100.100.2/202.100.100.1/21/2222  syn +ack

server->client control connection

1. PREROUTING

Conntrackip_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);

NATip_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

2POSTROUTING

无处理;

 

接下来,clinet回给serverack,也可以正确进行SNAT,到达server(过程类似阶段一)


/***********************PASV模式**************************************/

阶段三:

src/dst/sport/dport202.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

Conntrackip_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×561286

              创建exptuple信息(即所期待的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;

NATip_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

然后,由于这里的conntrckexp结构已经建立,所以会调用ip_nat_ftp.c中的help(再调用ftp_data_fixup)进行NATALG处理:

       对于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×561286),更新skb的应用层信息(这里应用层信息还是227 Entering Passive Mode(202,100,100,2,5,6)\r\n

(注:这里若采用clinet->server发送PORT命令,则其exp结构的创建过程更具有代表性)

2POSTROUTING

无处理;

 

/************数据连接开始***************/

阶段四:

src/dst/sport/dport192.168.1.2/202.100.100.2/1111/1286  client->server

SYN data connection

1. PREROUTING

Conntrackip_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=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_ORIGINALsrc/dst/sport/dport192.168.1.2/202.100.100.2/3333/21

              IP_CT_DIR_REPLYsrc/dst/sport/dport202.100.100.2/202.100.100.1/21/2222

              控制报文有conntrack_helphandle处理;

       数据报文的conntrack

              IP_CT_DIR_ORIGINALsrc/dst/sport/dport192.168.1.2/202.100.100.2/1111/1286

              IP_CT_DIR_REPLYsrc/dst/sport/dport202.100.100.2/192.168.1.2/1286/1111

              数据报文的conntrack_helpNULL

       它们之间联系用的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;(相关)

NATip_nat_fn中,得到skbconntrack信息,并得到info = &ct->nat.info。显然,这个conntrack是新的,并没有nat.info。并且,这个conntrack是有exp的,所以,会进行call_expect操作。

       这里进入的就是ftpexpect函数: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

Conntrackip_refrag,不处理conntrack相关;

NATip_nat_fn中,得到skbconntrack信息,并得到info = &ct->nat.info。显然,这个conntrack是新的,并没有nat.info。并且,这个conntrack是有exp的,所以,会进行call_expect操作。

       这里进入的就是ftpexpect函数: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_tpsrc/dst/sport/dport192.168.1.2/202.100.100.2/1111/1286;

new_tpsrc/dst/sport/dport202.100.100.1/202.100.100.2/4444/1286;

reply_tpsrc/dst/sport/dport202.100.100.2/202.100.100.1/1286/4444;

inv_tpsrc/dst/sport/dport202.100.100.2/192.168.1.2/1286/1111;

通过ip_conntrack_alter_reply,更改数据报文的reply方向的tuple为:src/dst/sport/dport202.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_ORIGINALsrc/dst/sport/dport192.168.1.2/202.100.100.2/1111/1286

              IP_CT_DIR_REPLYsrc/dst/sport/dport202.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/dport202.100.100.2/202.100.100.1/1286/4444  server->client

SYNACK data connection

1. PREROUTING

Conntrackip_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);

NATip_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

2POSTROUTING

无处理;

 

/***********************PORT模式**************************************/

阶段三:

src/dst/sport/dport192.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

Conntrackip_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×561286

              创建exptuple信息(即所期待的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;

NATip_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;

*/

这里由于HooknumNF_IP_PRE_ROUTING,所以无法匹配manip,故不做NAT转换处理;

然后,由于这里的conntrckexp结构已经建立,所以会调用ip_nat_ftp.c中的help进行NATALG处理:

这里由于HooknumNF_IP_PRE_ROUTING,且dir == IP_CT_DIR_ORIGINAL,故不做ALG转换处理;

2POSTROUTING

Conntrackip_refrag,不处理conntrack相关;

NATip_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

然后,由于这里的conntrckexp结构已经建立,所以会调用ip_nat_ftp.c中的help(再调用ftp_data_fixup)进行NATALG处理:

       对于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_ORIGINALsrc/dst/sport/dport192.168.1.2/202.100.100.2/3333/21

IP_CT_DIR_REPLYsrc/dst/sport/dport202.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×561286

              创建exptuple信息(即所期待的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×561286),更新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

Conntrackip_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=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_ORIGINALsrc/dst/sport/dport192.168.1.2/202.100.100.2/3333/21

              IP_CT_DIR_REPLYsrc/dst/sport/dport202.100.100.2/202.100.100.1/21/2222

              控制报文有conntrack_helphandle处理;

       数据报文的conntrack

              IP_CT_DIR_ORIGINALsrc/dst/sport/dport202.100.100.2/202.100.100.1/20/1286

              IP_CT_DIR_REPLYsrc/dst/sport/dport202.100.100.1/202.100.100.2/1286/20

              数据报文的conntrack_helpNULL

       它们之间联系用的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;(相关)

NATip_nat_fn中,得到skbconntrack信息,并得到info = &ct->nat.info。显然,这个conntrack是新的,并没有nat.info。并且,这个conntrack是有exp的,所以,会进行call_expect操作。

       这里进入的就是ftpexpect函数: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_tpsrc/dst/sport/dport202.100.100.2/202.100.100.1/20/1286;

new_tpsrc/dst/sport/dport202.100.100.2/192.168.1.2/20/4444;

reply_tpsrc/dst/sport/dport192.168.1.2/202.100.100.2/4444/20;

inv_tpsrc/dst/sport/dport202.100.100.1/202.100.100.2/1286/20;

通过ip_conntrack_alter_reply,更改数据报文的reply方向的tuple为:src/dst/sport/dport192.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_ORIGINALsrc/dst/sport/dport202.100.100.2/202.100.100.1/20/1286

              IP_CT_DIR_REPLYsrc/dst/sport/dport192.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

2POSTROUTING

Conntrackip_refrag,不处理conntrack相关;

NATip_nat_fn中,得到skbconntrack信息,并得到info = &ct->nat.info。直接调用do_bindings

       由于不满足

info->manips[i].direction == dir

                  && info->manips[i].hooknum == hooknum

       故不进行地址变换处理(实际上在PREROUTING中已经转变过了,这里不需要再做了);

 

阶段五:

src/dst/sport/dport192.168.1.2/202.100.100.2/4444/20  client->server

SYNACK data connection

1. PREROUTING

Conntrackip_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);

NATip_nat_fn中,得到先前建立的NAT地址信息表:info = &ct->nat.info

进入do_bindings,但由于不满足:

info->manips[i].direction == dir

                  && info->manips[i].hooknum == hooknum

故不处理;

且没有helper

2. POSTROUTING

Conntrackip_refrag,不处理conntrack相关;

NATip_nat_fn中,得到skbconntrack信息,并得到info = &ct->nat.info。直接调用do_bindings

       满足

info->manips[i].direction == dirIP_CT_DIR_REPLY

                  && info->manips[i].hooknum == hooknumNF_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:提供一个5hook点的框架;

2. Conntrack:记录报文的运行轨迹。包括ORIGINALREPLY两个方向;

3. NAT:依据iptablesNAT规则,建立Conntracknat_info信息,并更改报文的地址信息;

4. ALG:用于更改某些报文的应用层中的地址信息;

 

处理模式:

1.       控制报文:

² PREROUTING

Ø Conntrack:建立新的Conntrack信息:ORIGINALREPLY

Ø NAT:无

² POSTROUTING

Ø Conntrack:无

Ø NAT:根据iptables规则,建立该Conntracknat_infoSNATDNAT,并更改报文地址;并更改Conntrack中记录的REPLY方向的tuple信息(地址改为NAT处理后的值)

2.       应用层含有数据连接地址信息的控制报文:

² PREROUTING

Ø Conntrack:找到已经建立的Conntrack信息;利用conntrack->help,建立exp结构,建立所exptuple信息

Ø NAT:可能不处理 或者 利用nat->help更新应用层报文中的地址信息,并更改exp中的地址信息

² POSTROUTING

Ø Conntrack:无

Ø NAT:可能不处理 或者 利用nat->help更新应用层报文中的地址信息,并更改exp中的地址信息

3. 数据报文到达:

² PREROUTING

Ø Conntrack:建立新的Conntrack信息:ORIGINALREPLY;且正好就是要expecttuple;故与控制报文的Conntrack关联上;

Ø NAT通过调用nat->exp函数,利用控制报文的Conntrack信息,建立自己的nat_infoSNATDNAT;并更改Conntrack中记录的REPLY方向的tuple信息(地址改为NAT处理后的值)

² POSTROUTING

Ø Conntrack:无

Ø NAT:利用自己的nat_info,更改报文的地址信息


posted @ 2015-06-18 10:24  拉古小师傅  阅读(1808)  评论(0编辑  收藏  举报