linux内核netfilter之ip_conntrack模块的作用举例--nat和REDIRECT为例

本文涉及的产品
公网NAT网关,每月750个小时 15CU
简介:
修改应用层协议控制包使用了ip_conntrack,iptables的REDIRECT target也使用了ip_conntrack,另外包括iptables的state模块也是如此,使用ip_conntrack,可见ip_conntrack的重要性,ip_conntrack的一个无比重要的作用是实现nat,可以说REDIRECT target和对诸如ftp的修改以实现server回连client最终都落实到了nat上,比如,所谓的REDIRECT就是内置一个nat规则,将符合matchs的包nat到本机的特定端口,这个和iptables的nat表原理是一样的,不同的是,nat表的配置是显式的nat,而REDIRECT和ip_nat_ftp是隐式的nat而已。它们都是nat,都依赖于原始的ip_conntrack,因此原始的链接流信息并没有丢失,还是可以得到的,事实上,内核就是通过原始的链接流来匹配nat规则的,如果丢弃了原始链接流信息,何谈匹配!如果一个原始链接是a->b,而后不管是显式的nat还是隐式的REDIRECT以及nat_ftp,将a->b改为了a->c,a->b还是可以得到的,内核正是从a->b的流信息中取得了“要转换为a->c”这个信息的。
     在init_conntrack中有以下逻辑:
conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC);
conntrack->ct_general.destroy = destroy_conntrack;
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple;  //初始化tuple,记录连接地址端口信息,该tuple在nat后不会被改掉,此谓原始流
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack;
conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple; //repl_tuple初始化成和tuple一样的,在nat之后就会被改成nat后的地址,端口信息,此谓修改后的流,repl是replace的意思.
conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack;
tuple中就包含了原始流的信息,在随后的nat表的查找后,在alloc_null_binding中初始化conntrack的nat信息,由此就将nat信息和原始流信息统一到了conntrack中了。resolve_normal_ct是ip_conntrack模块使用的,其最后一句:
skb->nfct = &h->ctrack->infos[*ctinfo];
会将连接信息设置到skb中,以备后面的nat或者REDIRECT使用,在nat中,调用ip_conntrack_get取得这个conntrack。在ip_nat_setup_info中会调用ip_conntrack_alter_reply,后者会改变conntrack->tuplehash[IP_CT_DIR_REPLY].tuple的值为一个新的nat后的值。对于REDIRECT target,netfilter提供了一个getsockopt接口可以取得原始流的信息,该接口就是SO_ORIGINAL_DST,最终调用getorigdst,在getorigdst中有以下逻辑:
struct inet_opt *inet = inet_sk(sk);
struct ip_conntrack_tuple_hash *h;
struct ip_conntrack_tuple tuple;
IP_CT_TUPLE_U_BLANK(&tuple);
tuple.src.ip = inet->rcv_saddr;   //原始流的源ip
tuple.src.u.tcp.port = inet->sport; //原始流的源端口
tuple.dst.ip = inet->daddr;   //本机重定向后的ip
tuple.dst.u.tcp.port = inet->dport; //本机重定向后的端口
...
h = ip_conntrack_find_get(&tuple, NULL);  //在既有链接中寻找一个ip_conntrack_tuple_hash的tuple字段和参数tuple一样的,返回ip_conntrack_tuple_hash结构体
...
ip_conntrack_tuple_hash的定义如下:
struct ip_conntrack_tuple_hash
{
    struct list_head list;
    struct ip_conntrack_tuple tuple;
    struct ip_conntrack *ctrack;
};
现在看一下ip_conntrack_find_get如何找到h,在REDIRECT target中,数据肯定要进入本机,而进入本机就要进入NF_IP_LOCAL_IN链,在NF_IP_LOCAL_IN链上注册有一个ip_conntrack_local_in_ops,其HOOK函数为ip_confirm,最终要调用到__ip_conntrack_confirm,__ip_conntrack_confirm有以下逻辑:
unsigned int hash, repl_hash;
...
hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
...
list_prepend(&ip_conntrack_hash[hash], &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
list_prepend(&ip_conntrack_hash[repl_hash], &ct->tuplehash[IP_CT_DIR_REPLY]);
最后的两行将修改后的contrack信息的hash加入了ip_conntrack_hash表,在getorigdst调用ip_conntrack_find_get的时候,所使用的信息就是修改后的contrack信息,因此最终肯定能找到&ct->tuplehash[IP_CT_DIR_REPLY],而tuplehash[IP_CT_DIR_REPLY]和tuplehash[IP_CT_DIR_ORIGINAL]二者统一到了conntrack中,因此getorigdst的后半段:
sin.sin_port = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port;
sin.sin_addr.s_addr = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
将可以得到原始流的信息。
     最后看看redirect的HOOK函数:
static unsigned int redirect_target(...)
{
    ...
    ct = ip_conntrack_get(*pskb, &ctinfo); 
    ...
    indev = (struct in_device *)(*pskb)->dev->ip_ptr;
    newdst = indev->ifa_list->ifa_local;
    newrange = ((struct ip_nat_multi_range)
        { 1, { { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
             newdst, newdst,
             mr->range[0].min, mr->range[0].max } } });
    return ip_nat_setup_info(ct, &newrange, hooknum); //重定向到本机的地址转换
}
__ip_conntrack_confirm函数不仅仅由NF_IP_LOCAL_IN链上的operations调用,在POSTROUTING链上也有调用,这就是ip_conntrack_out_ops中的HOOK函数ip_refrag,因此不管怎样,只要发生了地址转换,总逃不过将新的转换后的流信息加入到conntrack中。

作罢!



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271819

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
7月前
|
网络协议 Linux 网络架构
Linux三种网络模式 | 仅主机、桥接、NAT
Linux三种网络模式 | 仅主机、桥接、NAT
1010 0
|
4月前
|
负载均衡 算法 Linux
在Linux中,LVS-NAT模型的特性是什么?
在Linux中,LVS-NAT模型的特性是什么?
|
4月前
|
负载均衡 算法 Linux
在Linux中,LVS-NAT模式的原理是什么?
在Linux中,LVS-NAT模式的原理是什么?
|
4月前
|
负载均衡 Linux 网络虚拟化
在Linux中,什么是NAT,常见分为那几种,DNAT与SNAT有什么不同,应用事例有那些?
在Linux中,什么是NAT,常见分为那几种,DNAT与SNAT有什么不同,应用事例有那些?
|
5月前
|
Linux 网络安全 虚拟化
Ngnix04系统环境准备-上面软件是免费版的,下面是收费版的,他更快的原因使用了epoll模型,查看当前Linux系统版本, uname -a,VMWARE建议使用NAT,PC端电脑必须使用网线连接
Ngnix04系统环境准备-上面软件是免费版的,下面是收费版的,他更快的原因使用了epoll模型,查看当前Linux系统版本, uname -a,VMWARE建议使用NAT,PC端电脑必须使用网线连接
|
6月前
|
Linux 网络安全
NAT 技术与 Linux iptables
NAT 技术与 Linux iptables
|
7月前
|
Java Shell Linux
Linux【脚本 01】简单Shell脚本实现定时备份文件、压缩、删除超时文件操作(showDoc文件备份脚本举例)
Linux【脚本 01】简单Shell脚本实现定时备份文件、压缩、删除超时文件操作(showDoc文件备份脚本举例)
373 0
|
7月前
|
XML Linux 定位技术
Linux【工具 02】OpenStreetMap数据处理工具OSMCTools下载安装使用举例(osmconvert\osmfilter命令说明)如何获取区域边界说明
Linux【工具 02】OpenStreetMap数据处理工具OSMCTools下载安装使用举例(osmconvert\osmfilter命令说明)如何获取区域边界说明
210 0
|
7月前
|
Java 调度 Docker
Docker【应用 01】Spring Boot 项目部署在Linux环境下的Docker容器内举例(任务调度系统 xxl-job 任务调度中心)(手动版)
Docker【应用 01】Spring Boot 项目部署在Linux环境下的Docker容器内举例(任务调度系统 xxl-job 任务调度中心)(手动版)
140 0
|
25天前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
59 8