Netfilter连接跟踪的详细流程

上一篇我们了解了连接跟踪的基本框架和大概流程,本篇我们着重分析一下,数据包在连接跟踪系统里的旅程,以达到对连接跟踪运行原理深入理解的目的。

连接跟踪机制在Netfilter框架里所注册的hook函数一共就五个:ip_conntrack_defrag()、ip_conntrack_in()、ip_conntrack_local()、ip_conntrack_help()和ip_confirm()。

前几篇博文中我们知道ip_conntrack_local()最终还是调用了ip_conntrack_in()。这五个hook函数及其挂载点,想必现在大家应该也已经烂熟于心了,如果记不起来请看【上】篇博文。

在连接跟踪的入口处主要有三个函数在工作:ip_conntrack_defrag()、ip_conntrack_in()、ip_conntrack_local();

在出口处就两个:ip_conntrack_help()和ip_confirm()。

接下来的事情就变得非常奇妙,请大家将自己当作一个需要转发的数据包,且是一条新的连接。然后跟随我去连接跟踪里耍一圈吧。在进入连接跟踪之前,我需要警告大家:连接跟踪虽然不会改变数据包本身,但是它可能会将数据包丢弃。

我们的旅行的线路图已经有了:

ip_conntrack_defrag()

当我们初到连接跟踪门口的时候,是这位小生来招待我们。这个函数主要是完成IP报文分片的重新组装,将属于一个IP报文的多个分片重组成一个真正的报文。关于IP分片,大家可以去阅读《TCP/IP详解卷1》了解一点基础,至于IP分片是如何被重新组装一个完整的IP报文也不是我们的重心,这里不展开讲。该函数也向我们透露了一个秘密,那就是连接跟踪只跟踪完整的IP报文,不对IP分片进行跟踪,所有的IP分片都必须被还原成原始报文,才能进入连接跟踪系统。

ip_conntrack_in()

该函数的核心是resolve_normal_ct()函数所做的事情,其执行流程如下所示:

在接下来的分析中,需要大家对上一篇文章提到的几个数据结构:

ip_conntrack{}、ip_conntrack_tuple{}、ip_conntrack_tuple_hash{}和ip_conntrack_protocol{}以及它们的关系必须弄得很清楚,你才能彻底地读懂resolve_normal_ct()函数是干什么。最好手头再有一份2.6.21的内核源码,然后打开source insight来对照着阅读效果会更棒!

第一步:ip_conntrack_in()函数首先根据数据包skb的协议号,在全局数组ip_ct_protos[]中查找某种协议(如TCP,UDP或ICMP等)所注册的连接跟踪处理模块ip_conntrack_protocol{},如下所示。

在结构中,具体的协议必须提供将属于它自己的数据包skb转换成ip_conntrack_tuple{}结构的回调函数pkt_to_tuple()和invert_tuple(),用于处理新连接的new()函数等等。

第二步:找到对应的协议的处理单元proto后,便调用该协议提供的错误校验函数(如果该协议提供的话)error来对skb进行合法性校验。

第三步:调用resolve_normal_ct()函数。该函数的重要性不言而喻,它承担连接跟踪入口处剩下的所有工作。该函数根据skb中相关信息,调用协议提供的pkt_to_tuple()函数生成一个ip_conntrack_tuple{}结构体对象tuple。然后用该tuple去查找连接跟踪表,看它是否属于某个tuple_hash{}链。请注意,一条连接跟踪由两条ip_conntrack_tuple_hash{}链构成,一“去”一“回”,参见上一篇博文末尾部分的讲解。为了使大家更直观地理解连接跟踪表,我将画出来,如下图,就是个双向链表的数组而已。

如果找到了该tuple所属于的tuple_hash链表,则返回该链表的地址;如果没找到,表明该类型的数据包没有被跟踪,那么我们首先必须建立一个ip_conntrack{}结构的实例,即创建一个连接记录项。

然后,计算tuple的应答repl_tuple,对这个ip_conntrack{}对象做一番必要的初始化后,其中还包括,将我们计算出来的tuple和其反向tuple的地址赋给连接跟踪ip_conntrack里的tuplehash[IP_CT_DIR_ORIGINAL]和tuplehash[IP_CT_DIR_REPLY]。

最后,把ip_conntrack->tuplehash[IP_CT_DIR_ORIGINAL]的地址返回。这恰恰是一条连接跟踪记录初始方向链表的地址。Netfilter中有一条链表unconfirmed,里面保存了所有目前还没有收到过确认报文的连接跟踪记录,然后我们的ip_conntrack->tuplehash[IP_CT_DIR_ORIGINAL]就会被添加到unconfirmed链表中。

第四步:调用协议所提供的packet()函数,该函数承担着最后向Netfilter框架返回值的使命,如果数据包不是连接中有效的部分,返回-1,否则返回NF_ACCEPT。也就是说,如果你要为自己的协议开发连接跟踪功能,那么在实例化一个ip_conntrack_protocol{}对象时必须对该结构中的packet()函数做仔细设计。

虽然我不逐行解释代码,只分析原理,但有一句代码还是要提一下。

resolve_normal_ct()函数中有一行ct = tuplehash_to_ctrack(h)的代码,参见源代码。其中h是已存在的或新建立的ip_conntrack_tuple_hash{}对象,ct是ip_conntrack{}类型的指针。不要误以为这一句代码的是在创建ct对象,因为创建的工作在init_conntrack()函数中已经完成。本行代码的意思是根据ip_conntrack{}结构体中tuplehash[IP_CT_DIR_ORIGINAL]成员的地址,反过来计算其所在的结构体ip_conntrack{}对象的首地址,请大家注意。

大家也看到ip_conntrack_in()函数只是创建了用于保存连接跟踪记录的ip_conntrack{}对象而已,并完成了对其相关属性的填充和状态的设置等工作。简单来说,我们这个数据包目前已经拿到连接跟踪系统办法的“绿卡”ip_conntrack{}了,但是还没有盖章生效。

ip_conntrack_help()

大家只要把我前面关于钩子函数在五个HOOK点所挂载情况的那张图记住,就明白ip_conntrack_help()函数在其所注册的hook点的位置了。当我们这个数据包所属的协议在其提供的连接跟踪模块时已经提供了ip_conntrack_helper{}模块,或是别人针对我们这种协议类型的数据包提供了扩展的功能模块,那么接下来的事儿就很简单了:

首先,判断数据包是否拿到“绿卡”,即连接跟踪是否为该类型协议的包生成了连接跟踪记录项ip_conntrack{};

其次,该数据包所属的连接状态不属于一个已建连接的相关连接,在其响应方向。

两个条件都成立,就用该helper模块提供的help()函数去处理我们这个数据包skb。最后,这个help()函数也必须向Netfilter框架返回NF_ACCEPT或NF_DROP等值。任意一个条件不成立则ip_conntrack_help()函数直接返回NF_ACCEPT,我们这个数据包继续传输。

ip_confirm()

该函数是我们离开Netfilter时遇到的最后一个家伙了,如果我们这个数据包已经拿到了“绿卡”ip_conntrack{},并且我们这个数据包所属的连接还没收到过确认报文,并且该连接还未失效。然后,我们这个ip_confirm()函数要做的事就是:

拿到连接跟踪为该数据包生成ip_conntrack{}对象,根据连接“来”、“去”方向tuple计算其hash值,然后在连接跟踪表ip_conntrack_hash[]见上图中查找是否已存在该tuple。如果已存在,该函数最后返回NF_DROP;如果不存在,则将该连接“来”、“去”方向tuple插入到连接跟踪表ip_conntrack_hash[]里,并向Netfilter框架返回NF_ACCEPT。之所以要再最后才将连接跟踪记录加入连接跟踪表是考虑到数据包可能被过滤掉。

至此,我们本次旅行就圆满结束了。这里我们只分析了转发报文的情况。发送给本机的报文流程与此一致,而对于所有从本机发送出去的报文,其流程上唯一的区别就是在调用ip_conntrack_in()的地方换成了ip_conntrack_local()函数。前面说过,ip_conntrack_local()里面其实调用的还是ip_conntrack_in()。ip_conntrack_local()里只是增加了一个特性:那就是对于从本机发出的小数据包不进行连接跟踪。

连接跟踪系统的初始化流程分析

有了前面的知识,我们再分析连接跟踪系统的初始化ip_conntrack_standalone_init()函数就太容易不过了。还是先上ip_conntrack_standalone_init()函数的流程图:

该函数的核心上图已经标出来了“初始化连接跟踪系统”和“注册连接跟踪的hook函数”。其他两块这里简单做个普及,不展开讲。至少让大家明白连接跟踪为什么需要两中文件系统。

procfs(/proc文件系统)

这是一个虚拟的文件系统,通常挂载在/proc,允许内核以文件的形式向用户空间输出内部信息。该目录下的所有文件并没有实际存在在磁盘里,但可以通过cat、more或>shell重定向予以写入,这些文件甚至可以像普通文件那样指定其读写权限。创建这些文件的内核组件可以说明任何一个文件可以由谁读取或写入。但是:用户不能在/proc目录下新增,移除文件或目录

sysctl(/proc/sys目录)

此接口允许用户空间读取或修改内核变量的值。不能用此接口对每个内核变量进行操作:内核应该明确指出哪些变量从此接口对用户空间是可见的。从用户空间,你可以用两种方式访问sysctl输出的变量:sysctl系统调用接口;procfs。当内核支持procfs文件系统时,会在/proc中增加一个特殊目录(/proc/sys),为每个由sysctl所输出的内核变量引入一个文件,我们通过对这些文件的读写操作就可以影响到内核里该变量的值了。

除此之外还有一种sysfs文件系统,这里就不介绍了,如果你感兴趣可以去研读《Linux设备驱动程序》一书的详细讲解。

那么回到我们连接跟踪系统里来,由此我们可以知道:连接跟踪系统向用户空间输出一些内核变量,方便用户对连接跟踪的某些特性加以灵活控制,如改变最大连接跟踪数、修改TCP、UDP或ICMP协议的连接跟踪超时是时限等等。

注意一点:/proc/sys目录下的任何一个文件名,对应着内核中有一个一模一样同名的内核变量。例如,我的系统中该目录是如下这个样子:

ip_conntrack_init()函数

该函数承担了连接跟踪系统初始化的绝大部分工作,其流程我们也画出来了,大家可以对照源码来一步一步分析。

第一步:连接跟踪的表大小跟系统内存相关,而最大连接跟踪数和连接跟踪表容量的关系是:最大连接跟踪数=8×连接跟踪表容量。代码中是这样的:

ip_conntrack_max = 8 × ip_conntrack_htable_size;那么从上面的图我们可以看出来,我们可以通过手工修改/proc/sys/net/ipv4/netfilter目录下同名的ip_conntrack_max文件即可动态修改连接系统的最大连接跟踪数了。

第二步:注册Netfilter所用的sockopt,先不讲,以后再说。只要知道是这里注册的就行了。

第三步:为连接跟踪hash表ip_conntrack_hash分配内存并进行初始化。并创建连接跟踪和期望连接跟踪的高速缓存。

第四步:将TCP、UDP和ICMP协议的连接跟踪协议体,根据不同协议的协议号,注册到全局数组ip_ct_protos[]中,如下所示:

最后再做一些善后工作,例如注册DROP这个target所需的功能函数,为其他诸如NAT这样的模块所需的参数ip_conntrack_untracked做初始化,关于这个参数我们在NAT模块中再详细讨论它。

这样,我们连接跟踪系统的初始化工作就算彻底完成了。有了前几篇关于连接跟踪的基础知识,再看代码是不是有种神清气爽,豁然开朗的感觉。

至于连接跟踪系统所提供的那五个hook函数的注册,我想现在的你应该连都不用看就知道它所做的事情了吧。

(六)洞悉linux下的Netfilteriptables:如何理解连接跟踪机制?(2)相关推荐

  1. 洞悉linux下的Netfilteriptables

    原网址:http://blog.chinaunix.net/uid-23069658-id-3160506.html (一)洞悉linux下的Netfilter&iptables:什么是Net ...

  2. 洞悉linux下的Netfilteriptables:什么是Netfilter?

    转自:http://blog.chinaunix.net/uid-23069658-id-3160506.html (一)洞悉linux下的Netfilter&iptables:什么是Netf ...

  3. linux进行端口跟踪,(五)洞悉linux下的Netfilteriptables:如何理解连接跟踪机制?【上】...

    其实PRE_ROUTING和LOCAL_OUT点可以看作是整个netfilter的入口,而POST_ROUTING和LOCAL_IN可以看作是其出口.在只考虑连接跟踪的情况下,一个数据包无外乎有以下三 ...

  4. linux内核snat分析,(十)洞悉linux下的Netfilteriptables:网络地址转换原理之SNAT

    gaopeiliang:怎么搞的,关键的数据怎么丢了那,,出现的问题是 从ppp0上走出的数据,正确的SNAT出去了,同时也得到了响应,但是数据却没有转发到eth1上,看来看去好像是回来的数据包没有被 ...

  5. (十)洞悉linux下的Netfilteramp;iptables:网络地址转换原理之SNAT

    源地址转换:SNAT SNAT 主要应用于下列场景: 这种情况下,我们只有一个公网地址A,而又有三台主机需要同时上网,这时就需要SNAT了.它的主要作用是将那些由私网发来的数据包skb的源地址改成防火 ...

  6. php编译freetds,Linux 下 PHP 5.2.x 连接 SQL Server 数据库 FreeTDS 配置笔记

    CentOS 5.4 Linux 下的 PHP(FastCGI) 需要连接相关部门的SQL Server 2000数据库,配置了扩展FreeTDS扩展. 1.编译安装FreeTDS mkdir -p ...

  7. 2021-05-14 linux下用root 登录ftp连接

    linux下用root 登录ftp连接:530 Permission denied 问题所在: [root@iz home]# ftp 172.xxx.xxx.xxx 220 (vsFTPd 3.0. ...

  8. Linux下使用VisualVm通过JMX连接远程机器

    Linux下使用VisualVm通过JMX连接远程机器 1. 修改远程机器JDK配置文件     a.进入JAVA_HOME\jre\lib\management\目录    b.拷贝jmxremot ...

  9. linux下网易云音乐无法连接网络问题的解决思路

    linux下网易云音乐无法连接网络问题的解决思路 1.当所有需要联网的音乐播放器都出现网络无法连接或者google chrome 出现代理问题,可以检查下你最近是否弄了代理. 例如: 我最近就弄了个代 ...

  10. linux查看哪个网卡插着网线,(笔记)Linux下检测网卡与网线连接状态

    Linux下检测网卡与网线连接状态,使用ioctl向socket发送SIOCETHTOOL命令字. #include #include #include #include #include #incl ...

最新文章

  1. LeetCode简单题之找出数组排序后的目标下标
  2. C++ 计算并输出三角形的面积
  3. 无线覆盖项目地勘——无线地勘记录
  4. 微型计算机的层次结构,计算机系统层次结构微程序级
  5. java deque.pop_Java ArrayDeque pop()方法
  6. DPDK如何释放大页内存(巨页内存hugepage)
  7. C#中的委托和Java中的“委托”(Java8 lambda表达式)
  8. 红帽初级认证RHCSA考试环境——供实验练习
  9. 细胞生物学-7-叶绿体和线粒体
  10. 【洛谷 P4180】【模板】严格次小生成树[BJWC2010](倍增)
  11. 两个三进制数相加,输出一个结果为三进制形式的和
  12. word07去掉回车符
  13. 使用卷积神经网络(普通CNN和改进型LeNet)以及数据增强和迁移学习技巧识别猫和狗,并制作成分类器软件(基于Keras)
  14. 那些git中常见的面试题及知识点
  15. RF自动化-RIDE(跑自动化注意事项)和(配置环境注意事项)
  16. SUSAN算子——边缘检测
  17. 基于FPGA的SRIOIP例程及仿真实现
  18. stlinkv2红灯闪烁_STLINK V2安装使用详解
  19. 颜值爆表,Redis官方可视化工具来啦,功能很强大~
  20. 中国芯片首富300亿办的大学“亮相”:占地2250亩,陈十一院士领衔筹建,已与西湖大学达成合作...

热门文章

  1. postman 第4节 切换环境和设置读取变量(转)
  2. 批处理,扫描当前目录下的文件,传到指定文本中
  3. Android Hessian 通信
  4. python垃圾回收机制gcgo_python垃圾处理机制
  5. 使用Redis计数器防止并发请求
  6. LMAX Disruptor——一个高性能、低延迟且简单的框架
  7. 大数据市场交易趋旺物联网平台争夺加剧
  8. Android自动化测试(UiAutomator)简要介绍 - 萧瑟一笑的专栏 - 博客频道 - CSDN.NET...
  9. Android JSON 解析库的使用 - Gson 和 fast-json
  10. 分布式缓存MemcacheHelper