• 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)

    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)

    (注:这里若采用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)

    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)              

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

    阶段四:

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


  • 相关阅读:
    深度系统安装wine
    Android应用真正的入口在哪里?
    推荐系统算法概览
    Android面试题:Scrollview内嵌一个Button他的事件消费是怎样的?Move,Down,Up分别被哪个组件消费?
    Java面试题:多线程交替打印字符串
    EventBus使用初体验
    Andoird面试题:A活动启动B活动,他们的生命周期各是怎么样的?
    Android在开发过程中如何选择compileSdkVersion,minSdkVersion和targetSdkVersion
    华为2020暑期实习面经(已拿Offer)
    工商银行软件开发中心2020暑期实习面经(已拿Offer)
  • 原文地址:https://www.cnblogs.com/lagujw/p/4585156.html
Copyright © 2020-2023  润新知