`
totoxian
  • 浏览: 1074946 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

linux中nat的若干细节--基于2.6.8和2.6.17内核分析

 
阅读更多

在netfilter的nat模块中有一个alloc_null_binding函数,该函数在local_in这个hook点上会被调用,在nat没有初始化的时候也会被调用,在这两种情况会被调用,netfilter规定可在postrouting和local_in执行snat,但是local_in的时候tuplehash[IP_CT_DIR_REPLY]的目的地址是本机,转换源地址没有任何意义,因此只是分配一个null的映射,第二种情况中会在没有找到nat规则的情况下调用,因为如果找到了nat规则,则会在ipt_snat_target或者ipt_dnat_target中调用ip_nat_setup_info,后者会初始化nat,所谓的分配一个null的映射就是将信息原始连接复制一份,在2.6.8中nat的info中的num_manips会保持0,在后续的内核版本中,IPS_SRC_NAT和IPS_DST_NAT均不被置位,内核在nat模块中判断一个连接是否进行nat的依据就是上述的num_manips或者IPS_xxx的位信息,null类型的映射就是为了便于nat模块进行统一处理。
另外,只有一个数据流的第一个数据包进入主机,既连接流建立的时候才会初始化nat信息,后续的流中数据包只是简单取出nat信息,不会再去查nat规则表,因此不必担心数据流的回复包由于匹配上nat的规则被nat而连接包却没有。
nat只在数据流的第一个包到来时也就是建立流时起作用,道理如下:resolve_normal_ct为in_conntrack钩子中的流处理函数,其中:
{
h = ip_conntrack_find_get(&tuple, NULL);
//如果没有匹配到现有流则在此处新建一个流,调用init_conntrack
if (DIRECTION(h) == IP_CT_DIR_REPLY) { //如果是同流的反方向包到来了,那么就更新状态,并且在外层调用函数中不管之前IPS_SEEN_REPLY_BIT被设置与否都要再次设置IPS_SEEN_REPLY_BIT位
*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
*set_reply = 1;
} else { //如果是正方向的包,那么不再设置IPS_SEEN_REPLY_BIT位,而是维持其原状
if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) { //如果原来IPS_SEEN_REPLY_BIT位被设置,则连接肯定是已建立状态
*ctinfo = IP_CT_ESTABLISHED;
} else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) { //关联流
*ctinfo = IP_CT_RELATED;
}
...//IP_CT_NEW的情况
*set_reply = 0;
}
skb->nfct = &h->ctrack->infos[*ctinfo]; //skb的nfct字段设置成一个数组元素的地址,该数组的每一个元素代表一个包类型,该数组在后续的比如nat模块中取流状态的时候要使用。
return h->ctrack;
}
最关键的就是resolve_normal_ct的最后那一个赋值操作,这个赋值在后续的比如nat中会被使用,后续的nat模块的HOOK函数的开始需要匹配流,调用__ip_conntrack_get:
static inline struct ip_conntrack * __ip_conntrack_get(struct nf_ct_info *nfct, enum ip_conntrack_info *ctinfo)
{
struct ip_conntrack *ct = (struct ip_conntrack *)nfct->master;
*ctinfo = nfct - ct->infos; //resolve_normal_ct的最后赋值的数组元素地址减去数组头的地址得到一个索引,而该索引就是枚举类型ip_conntrack_info
return ct; //返回一个流结构,上述的强制转换在linux内核使用的很普遍,关键在于如何安排结构体的内容
}
enum ip_conntrack_info
{
IP_CT_ESTABLISHED,
IP_CT_RELATED,
IP_CT_NEW,
IP_CT_IS_REPLY,
IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 //这个就是所有流的类型
};
在init_conntrack会有如下调用:
for (i=0; i < IP_CT_NUMBER; i++)
conntrack->infos[i].master = &conntrack->ct_general;
上述的for循环奠定了后续取流状态的一切数据结构基础,ip_conntrack结构体和infos数组的布局也使得__ip_conntrack_get的强制转换成为可能。
最后看看IPS_SEEN_REPLY_BIT这个bit有什么用。很多人都知道在/proc/net/下面有一个ip_conntrack文件,该文件在加载了ip_conntrack.ko之后会自动生成(涉及到procfs的知识),文件中每条记录的前面会有ESTABLISHED等状态的字段,这些字段是内核基于一些标志判断得到的,比如IPS_SEEN_REPLY_BIT就是其中之一,一个流中的数据包有两个方向,以发起流的数据包(建立流的数据包)的方向为正方向,如果只有该方向的数据包过来,是不设置IPS_SEEN_REPLY_BIT标志的,可能原先就有这个标志,此时不管它,只有在反方向的数据包通过的时候才会设置IPS_SEEN_REPLY_BIT,这样内核会得到一个信息:通信是闭合的,也就是通信数据包的数量是偶数个。因为有些数据包可能有去无回,有请求无回应,只要有一个回应包,即反方向的数据包通过,则设置IPS_SEEN_REPLY_BIT,该标志的设置会延长流的寿命,毕竟机器内存及处理能力是有限的,大量的流状态保持会消耗很多时间和空间,因此必须为每一个流设置一个超时时间,超时时间过了还没有数据包过往则会删除一个流,为其他的流腾地方。以udp为例,IPS_SEEN_REPLY_BIT的设置会延长其超时时间:
static int udp_packet(struct ip_conntrack *conntrack,
const struct sk_buff *skb,
enum ip_conntrack_info conntrackinfo)
{
if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
ip_ct_refresh(conntrack, ip_ct_udp_timeout_stream);
set_bit(IPS_ASSURED_BIT, &conntrack->status);
} else
ip_ct_refresh(conntrack, ip_ct_udp_timeout);
return NF_ACCEPT;
}
细节不再细说,udp_packet也是一个回调函数,是一个协议回调函数,因为对于不同的协议要有不同的策略,比如tcp流是基于连接的,传输层会做好保持链接的一切工作,那就不需ip_conntrack做更多了,它的超时时间要比udp长得多,而且其状态也比udp多一些,所以需要回调函数将机制和策略分离,packet回调函数在ip_conntrack的HOOK函数中被调用。本来想到此打住,不再细说关于IPS_ASSURED_BIT的更多细节,涉及到early_drop等很多函数,也涉及到很多hash操作和链表组织以及没完没了的优化。可是...
init_conntrack(...)
{
...
static unsigned int drop_next; //静态局部变量
...
hash = hash_conntrack(tuple); //如果漫了
if (atomic_read(&ip_conntrack_count) >= ip_conntrack_max) {
unsigned int next = (drop_next++)%ip_conntrack_htable_size;
if (!early_drop(&ip_conntrack_hash[next]) //先从递增的一个类似随机的hash链表中尝试丢弃
&& !early_drop(&ip_conntrack_hash[hash])) { //再尝试从和本包hash到同一个冲突链的链表中排挤,副作用是冲突链大致守恒,进入一个的话就要挤出一个
... //谁也不能被挤出,那么就不允许当前包建立流,对于需要nat的连接,结果就是无法联通
return ERR_PTR(-ENOMEM);
}
}
...
}
static int early_drop(struct list_head *chain)
{
/* Traverse backwards: gives us oldest, which is roughly LRU */
struct ip_conntrack_tuple_hash *h;
int dropped = 0;
h = LIST_FIND_B(chain, unreplied, struct ip_conntrack_tuple_hash *);
if (h)从链表中找到一个能丢弃的,其原则就是找到一个没有设置IPS_ASSURED_BIT的流,这个在unreplied比较函数中体现
atomic_inc(&h->ctrack->ct_general.use);
if (!h)
return dropped;
if (del_timer(&h->ctrack->timeout)) {
death_by_timeout((unsigned long)h->ctrack);
dropped = 1;
}
ip_conntrack_put(h->ctrack);
return dropped;
}
对于tcp而言,情况大致类似,只是更复杂一些而已。

分享到:
评论

相关推荐

    IS620N-Ecat_v2.6.8.xml

    IS620N-Ecat_v2.6.8.xml

    linux-2.6.8内核源码

    linux-2.6.8.1内核源码,经编译后可移植到嵌入式系统中,非常好。

    ehcache-core-2.6.8.jar

    ehcache-core-2.6.8.jar mybatis 开发缓存文件配合使用jar包

    Wireshark-win32-2.6.8 .exe

    Wireshark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。

    layui-v2.6.8.zip

    在这个版本中,layui 提供了丰富的组件和模块,使得开发人员可以快速构建出响应式、美观的用户界面。 layui 框架的核心特点包括: 1. **模块化**:layui 采用模块化的设计,每个功能都封装为独立的模块,如表单、...

    mcrypt-2.6.8.tar.gz

    解压mcrypt-2.6.8.tar.gz后,你会得到一个名为mcrypt-2.6.8的目录,其中包含了源代码和其他构建所需的文件。为了在你的PHP环境中安装这个扩展,你需要按照以下步骤进行: 1. **解压和进入目录**:首先使用tar命令...

    layui-v2.6.8.rar

    layui-v2.6.8.rar这个压缩包文件包含的是layui的2.6.8版本,这个版本在社区中被广泛使用,提供了丰富的组件和模块,以帮助开发者快速构建现代Web应用。下面我们将深入探讨layui框架的核心特性、主要组件以及如何使用...

    kangle-vhms-2.6.8.zip

    1. **Kangle Web服务器**:Kangle是一款基于Linux操作系统的Web服务器软件,支持多种操作系统平台。它具有高并发、高性能、安全稳定的特点,支持HTTP/HTTPS协议,以及各种Web服务功能,如虚拟主机、URL重写、SSL加密...

    bochs-2.6.8.tar.gz

    Bochs的源码包"bochs-2.6.8.tar.gz"包含了构建Bochs模拟器所需的所有源代码、配置文件和文档,使得开发者和爱好者能够在Linux环境下自行编译和定制Bochs。 源码包下载后,解压bochs-2.6.8.tar.gz,会得到一个名为...

    php5.2-libiconv1.14-mcrypt2.6.8-mhash0.9.9.9-libmcrypt2.5.8.zip

    php5.2-libiconv1.14-mcrypt2.6.8-mhash0.9.9.9-libmcrypt2.5.8.zip 包涵下面的文件 php-5.2.10.tar.bz2 libiconv-1.14.tar.gz mcrypt-2.6.8.tar.gz mhash-0.9.9.9.tar.gz libmcrypt-2.5.8.tar.gz

    spring-boot-2.6.8.zip

    Spring Boot 是一个由 Pivotal 团队开发的 Java 框架,它极大地简化了创建独立、生产级的基于 Spring 的应用程序。Spring Boot 2.6.8 是该框架的一个稳定版本,提供了许多增强的功能和改进,旨在提高开发效率和应用...

    rh-ruby24-rubygems-devel-2.6.8-75.el7.noarch.rpm

    官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装

    linux2.6.8内核源码part1

    为linux2.6.8内核源码,是从linux官网上下载过来的,无病毒,放心使用

    linux2.6.8内核源码part2

    为linux2.6.8内核源码,是从linux官网上下载过来的,无病毒,放心使用

    spring-boot-2.6.8.tar.gz

    SpringBoot for linux 各个版本,免费下载 spring-boot for linux 各个版本,免费下载 如果不能免费下载,关注我,评论区联系我索要!

    bochs-2.6.8源码

    Bochs是一款开源的x86架构模拟器,它允许用户在非x86平台上运行基于x86指令集的操作系统和应用程序。Bochs是用C++编写的,它提供了高度可移植性,可以在多种操作系统上运行,如Windows、Linux、Mac OS X等。这款软件...

    java ehcache core 2.6.8.jar 核心包和mybatis-ehcache-1.0.3.jar分享

    Java Ehcache是一个流行的开源缓存解决方案,用于在Java应用程序中高效地存储和检索数据,以减少数据库负载并提高性能。Ehcache的核心是`ehcache-core-2.6.8.jar`,它提供了缓存管理的基本功能,如缓存创建、缓存...

    2017-06-13.php授权验证系统最新版2017-全新UI-v2.6.8

    在v2.6.8中,这个功能可能更加自动化,提高了用户体验。 "data"目录存储了程序运行所需的数据库或其他数据文件,可能包括用户信息、授权数据等。安全地管理和访问这些数据对于授权验证系统至关重要。 "Sys.php"很...

    linux2.6.8-usbwifi移植文档

    3. **编译与配置内核**:获取驱动后,需要将其整合到Linux 2.6.8内核中。这涉及编辑`./include/configs/`目录下的配置文件,启用相应的内核选项,比如`CONFIG_USB`和`CONFIG_USB_NET`。然后运行`make menuconfig`...

    layui-v2.6.8(html文档)[注:没有示例]

    layui-v2.6.8版本是这个框架的一个更新版本,包含了一系列优化和改进,旨在提升开发效率和用户体验。 layui的核心特性在于其模块化的设计,它将功能组件划分为多个独立的模块,如表格、表单、按钮、弹窗等,这使得...

Global site tag (gtag.js) - Google Analytics