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


时间: 2024-10-28 21:26:34

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

RxJava &amp;&amp; Agera 从源码简要分析基本调用流程(1)

版权声明:本文由晋中望原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/123 来源:腾云阁 https://www.qcloud.com/community 相信很多做Android或是Java研发的同学对RxJava应该都早有耳闻了,尤其是在Android开发的圈子里,RxJava渐渐开始广为流行.同样有很多同学已经开始在自己的项目中使用RxJava.它能够帮助我们在处理异步事件时能够省去那些复杂而繁琐的代码,尤其是当

【转载】linux2.6内核initrd机制解析

题记 很久之前就分析过这部分内容,但是那个时候不够深入,姑且知道这么个东西存在,到底怎么用,来龙去脉咋回事就不知道了.前段时间工作上遇到了一个initrd的问题,没办法只能再去研究研究,还好,有点眉目,索性整理了一下. 网络上流传着很多关于ramdisk.initrd的各种版本的分析,我的这篇源于对他们的理解,非常感谢那些前辈的无私奉献,要不然我们这些晚辈学起东 西来该是多么艰难呀.在这里需要特别声明的是如果文中有引用了您的思想而没有给出参考文献,请您原谅我的疏忽.晚辈就是需要站在像您这种巨人的

SEAndroid安全机制对Android属性访问的保护分析

Android系统通过属性暴露设备和运行时信息,并且可以通过设置属性来控制系统行为.因此,属性也像文件一样,是一种需要保护的资源.在启用SEAndroid之前,敏感属性只能被预先设定的进程进行设置.启用SEAndroid之后,敏感属性会进一步被SEAndroid安全策略保护.这样就可以更有效地保护系统属性了.在本文中,我们就详细分析SEAndroid安全机制对Android属性设置保护提供的支持. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 在分析SE

Java中JIN机制及System.loadLibrary() 的执行过程

Android平台Native开发与JNI机制详解 http://mysuperbaby.iteye.com/blog/915425 个人认为下面这篇转载的文章写的很清晰很不错. 注意Android平台上的JNI机制使用包括Java代码中调用Native模块以及Native代码中调用Java模块. http://www.ophonesdn.com/article/show/263(misybing:很遗憾该站已经挂掉) 众所周知,OPhone平台上的应用开发主要基于Java语言,但平台完全支持且

大话Linux内核中锁机制之原子操作、自旋锁

转至:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实是由于操作系统中存在多进程对共享资源的并发访问,从而引起了进程间的竞态.这其中包括了我们所熟知的SMP系统,多核间的相互竞争资源,单CPU之间的相互竞争,中断和进程间的相互抢占等诸多问题. 通常情况下,如图1所示,对于一段程序,我们的理想是总是美好的,希望它能够这样执行:进程1先对临界区完成操作,

大话Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁

大话Linux内核中锁机制之内存屏障.读写自旋锁及顺序锁 在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论内存屏障的相关内容. 三.内存屏障 不知读者是是否记得在笔者讨论自旋锁的禁止或使能的时候,提到过一个内存屏障函数.OK,接下来,笔者将讨论内存屏障的具体细节内容.我们首先来看下它的概念,Memory Barrier是指编译器和处理器对代码进行优化(对读写指令进行重新排序)后,导致对内存的写入操作不能

大话Linux内核中锁机制之完成量、互斥量

大话Linux内核中锁机制之完成量.互斥量 在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等待另一个执行单元完成某事后方可执行,它是一种轻量级机制.事实上,它即是为了完成进程间的同步而设计的,故而仅仅提供了代替同步信号量的一种解决方法,初值被初始化为0.它在include\linux\completion.h定义. 如图8.1所示,对于执行单元A

大话Linux内核中锁机制之信号量、读写信号量

大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程才能执行临界区的代码:不同的是获取不到信号量时,进程不会原地打转而是进入休眠等待状态.它的定义是include\linux\semaphore.h文件中,结构体如图6.1所示.其中的count变量是计数作用,通过使用lock变量实现对count变量的保

大话Linux内核中锁机制之RCU、大内核锁

大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linux内核中锁机制>系列博文进行了总结,并提出关于目前Linux内核中提供的锁机制的一些基本使用观点. 十.RCU机制 本节将讨论另一种重要锁机制:RCU锁机制.首先我们从概念上理解下什么叫RCU,其中读(Read):读者不需要获得任何锁就可访问RCU保护的临界