以FTP流程为例,分析NAT和ALG

网络环境:

×5+6=1286)

创建×5+6=1286),更新skb的应用层信息(这里应用层信息还是×5+6=1286)

创建×5+6=1286)

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


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

  1. 图解Janusgraph系列-并发安全:锁机制(本地锁+分布式锁)分析

    图解Janusgraph系列-并发安全:锁机制(本地锁+分布式锁)分析 大家好,我是洋仔,JanusGraph图解系列文章,实时更新~ 图数据库文章总目录: 整理所有图相关文章,请移步(超链):图数据 ...

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

    版权声明:本文由晋中望原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/123 来源:腾云阁 https://www.qclo ...

  3. .NET中反射机制的使用与分析

    .NET中反射机制的使用与分析 [日期:2008-06-30] 来源:  作者:志伟     .NET反射的定义:审查元数据并收集关于它的类型信息的能力. 元数据是一种二进制信息,用以对存储在公共语言 ...

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

    题记 很久之前就分析过这部分内容,但是那个时候不够深入,姑且知道这么个东西存在,到底怎么用,来龙去脉咋回事就不知道了.前段时间工作上遇到了一个initrd的问题,没办法只能再去研究研究,还好,有点眉目 ...

  5. Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]

    目录 前言 现象 源码分析 实战例子 总结 参考资料 前言 今天研究了一下tomcat上web.xml配置文件中url-pattern的问题. 这个问题其实毕业前就困扰着我,当时忙于找工作. 找到工作 ...

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

    Android平台Native开发与JNI机制详解 http://mysuperbaby.iteye.com/blog/915425 个人认为下面这篇转载的文章写的很清晰很不错. 注意Android平 ...

  7. Android事件传递机制详解及最新源码分析——ViewGroup篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...

  8. Android事件传递机制详解及最新源码分析——Activity篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在前两篇我们共同探讨了事件传递机制<View篇>与<ViewGroup篇>,我们知道View触摸事件是ViewGroup传递 ...

  9. Linux内核中锁机制之RCU、大内核锁

    在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linu ...

随机推荐

  1. 流畅的python第十九章元编程学习记录

    在 Python 中,数据的属性和处理数据的方法统称属性(attribute).其实,方法只是可调用的属性.除了这二者之外,我们还可以创建特性(property),在不改变类接口的前提下,使用存取方法 ...

  2. 用dd命令复制磁盘分区

    用dd命令复制磁盘分区 首先是复制 复制前对写入的分区执行umount操作 sudo dd if=/dev/sda1 of=/dev/sda2 可以在另外一个终端输入这句,然后在原来的dd终端看到进度 ...

  3. 错误com.android.ddmlib.AdbCommandRejectedException

    错误com.android.ddmlib.AdbCommandRejectedException 学习了:https://blog.csdn.net/u014447072/article/detail ...

  4. linux mysql 更改MySQL数据库目录位置

    MySQL默认的数据文件存储目录为/var/lib/mysql.假如要把目录移到/home/data下需要进行下面几步: 1.home目录下建立data目录 cd /home mkdir data 2 ...

  5. 云计算之路-阿里云上:启用Windows虚拟内存引发的CPU 100%故障

    今天上午11:35~11:40左右,由于负载均衡中的两台云服务器CPU占用突然飚至100%,造成网站5分钟左右不能正常访问,请大家带来了麻烦,请谅解! (上图中红色曲线表示CPU占用) 经过分析,我们 ...

  6. 【Spark】RDD操作具体解释2——值型Transformation算子

    处理数据类型为Value型的Transformation算子能够依据RDD变换算子的输入分区与输出分区关系分为下面几种类型: 1)输入分区与输出分区一对一型 2)输入分区与输出分区多对一型 3)输入分 ...

  7. swift 使用匿名函数初始化属性

    swift 使用匿名函数初始化属性 匿名函数格式: { //代码体 }() 大括号是定义函数体的,小括号()是调用函数体的. 为什么不写一个函数然后调用函数呢?为何用匿名函数呢? 主要还是代码的简洁性 ...

  8. Android app启动耗时分析

    前言 app启动耗时过长的话,无论你的app里面的内容多么丰富有趣,作为一个用户,首先是没有耐心去等待的,如果我是一个用户,我会这样想:这是什么垃圾公司出的什么烂app,再等2s不进来就卸载,黑人问号 ...

  9. POJ 1163&amp;&amp; 3176 The Triangle(DP)

    The Triangle Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 41169   Accepted: 24882 De ...

  10. js重置form表单

      CreateTime--2017年7月19日10:37:11Author:Marydon js重置form表单 需要使用的方法:reset() 示例: HTML部分 <form id=&qu ...