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

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章