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

65 篇文章 1 订阅

https://www.cnblogs.com/lagujw/p/4585156.html

以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.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_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:

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,更改报文的地址信息


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值