netfilteriptables探讨(4)——nat的实现与使用

news2024/9/22 14:53:58

在之前的几篇文章中,我们讨论了netfilter与iptables的实现原理与基本用法。在netfilter&iptables的各种使用场景中,nat是最常用也是最复杂的用法之一。许多常用的网络使用模式都是通过nat iptables规则实现的,例如docker默认的bridge网络模式。

本文将具体分析iptables中nat规则的实现方式,介绍报文经过nat规则实现地址转换的过程。并以docker bridge模式使用的nat规则为例,具体了解nat规则的实际用法。

问题

  1. nat规则有哪几种,分别实现什么功能,用于什么场景?
  2. nat规则通过哪些iptables匹配函数实现?
  3. nat规则相关的iptables匹配函数在哪些netfilter hook点上被处理?
  4. nat规则会对哪些网络报文生效?
  5. nat规则与conntrack功能有什么关系?两者如何协同完成一个连接上所有报文的地址转换?
  6. docker是如何通过iptables nat规则来实现bridge模式下容器的网络访问的?配置了哪些规则?

nat规则的用途和种类

nat即网络地址转换(network address translation),主要用于在内部网络地址和外部网络地址间互相转换,从而让使用内部网络地址的设备或软件能够访问外部网络,或者让外部网络可以访问到没有外部网络地址的内部设备。nat一般运行在网关/路由设备上,内部设备访问外网或外部网络访问内部设备的报文都需要经过网关转发,网关在内外网转发报文的过程中执行nat转换。

nat主要分为两类:

snat:源地址转换。用于内网设备访问外网时,将请求报文的源地址从内网设备的地址(例如172.17.0.11:1234)转换为网关的外网地址(例如115.133.54.24:12345)

dnat:目的地址转换。用于从外网访问内网设备时,将请求报文的目的地址从网关的外网地址(例如115.133.54.24:8000)转换为内网设备的地址(例如172.17.0.11:80)。

snat和dnat一般都只是指对建连或第一个请求报文的转换。一个内网向外网的请求被snat转发后,必然会收到外网的响应报文,这个报文的目的地址也需要转换,但是这个转换一般就不会被称作是dnat了。在linux内核中,一个连接只有第一个报文才会被snat/dnat处理,后续的报文都会被连接追踪(conntrack)模块处理,按第一个报文的地址转换方式做相同的转换。

iptables中可以在nat表上定义两类动作,SNAT和DNAT,分别提供源地址和目的地址转换功能。

SNAT

SNAT规则配置命令为:
iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT --to-source 115.133.54.24
上面的规则将来自172.17.0.0/16地址段的请求报文的源地址转换成了115.133.54.24。源地址(-s)和转换地址(--to-source)也可以指定端口或端口段,这时只有符合端口段的请求才会被转换,转换后的端口在--to-source指定的端口段中。

DNAT

DNAT规则配置命令为:
iptables -t nat -A PREROUTING -p tcp --dport 6789 -j DNAT --to-destination 172.17.0.3:9876
上面的规则将发往6789端口的tcp报文的目的地址转换成172.17.0.3:9876。

虽然SNAT和DNAT常用于内网设备访问外网,或者让内网设备向外暴露端口,但其功能并不局限在这些方面。SNAT和DNAT规则可以用于实现任意的源地址和目的地址转换,例如:
iptables -t nat -A PREROUTING -s 33.44.55.66 -j DNAT --to-destination 172.17.0.3
可以将来自33.44.55.66的报文的目的地址全部转换为172.17.0.3。

MASQUERADE

MASQUERADE规则是SNAT规则的一个特例,也用于转换报文的源地址。与SNAT不同的是MASQUERADE规则不用指定转换后的源地址,MASQUERADE会自动选择可用的源地址用于转换。

MASQUERADE规则配置命令为:
iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j MASQUERADE
上面的规则将来自172.17.0.0/16地址段的请求报文的源地址转换成可用于访问目标的本地IP。

MASQUERADE规则最大的用途是在一些外网IP不固定的网关上,可以自动使用当前IP作为SNAT的转换目标,而不需要随着外网IP的变化去重新配置SNAT规则。

NAT规则的iptables实现

内核的nat iptables在iptable_nat模块中实现,入口在net/ipv4/netfilter/iptable_nat.c中。iptable_nat模块中实现了PRE_ROUTING、POST_ROUTING、LOCAL_OUT、LOCAL_IN这4个hook点的hook函数iptable_nat_do_chain。这个hook函数的实现直接调用了ipt_do_table这个标准规则匹配函数,会根据当前链上的nat规则逐一匹配和处理。

然而在hook注册过程ipt_nat_register_lookups中,上述hook函数并没有被直接注册到netfilter的hook点上,而是被注册到了nf_nat_lookup_hook_priv.entries中。真正被注册到hook点上的是在net/netfilter/nf_nat_proto.c中定义的4个函数:nf_nat_ipv4_in、nf_nat_ipv4_out、nf_nat_ipv4_local_fn、nf_nat_ipv4_fn,分别对应上面的4个hook点。其中nf_nat_ipv4_fn函数中包含了主要的实现逻辑,其余3个hook函数通过调用nf_nat_ipv4_fn->nf_nat_inet_fn实现功能。

nf_nat_inet_fn的实现和conntrack高度相关。nat的netfilter hook函数优先级(-100/100)低于conntrack的hook函数(-200),因此报文skb匹配nat规则时已经经过了conntrack模块的匹配和处理。这个函数的大体逻辑流程如下:

可以看到,一条连接只有第一个报文会真正去匹配NAT规则,匹配后的NAT转换模式会记录到conntrack信息中,后续报文就会直接按这个转换模式去做NAT转换。

其中的NAT规则匹配函数就是iptable_nat中定义的hook函数iptable_nat_do_chain。每个hook点会执行对应的nf_nat_lookup_hook_priv.entries中的函数,理论上也可以注册其他处理函数到这个列表中。

上面流程中比较特别的地方是在报文没有匹配NAT规则时,仍然可能需要对连接地址进行转换。在nf_nat_alloc_null_binding中,会检查当前连接的五元组是否唯一,如果五元组已经被占用,即使没有匹配任何NAT规则,还是需要对连接进行地址转换。这种情况可能出现在连接1在本地绑定端口向外建连,同时连接2通过NAT连接了同一个目标地址,刚好又把本地端口映射到了连接1绑定的端口。这时就只能把连接1的端口做一个转换,否则就会无法建连。因此,没有配置NAT规则的连接也有可能被执行NAT转换,实际收发的报文可能和绑定的本地端口并不一致。当然这种情况发生的概率应该是极低的。

iptables规则匹配函数——ipt_do_table

所有iptables模块的规则匹配功能最终都会调用ipt_do_table这个函数来实现。函数的原型为:unsigned int ipt_do_table(struct sk_buff *skb, const struct nf_hook_state *state, struct xt_table *table)

其中第三个参数是一个xt_table。每个IPv4(IPv6也是类似的逻辑)netns都会维护一系列xt_table,包括iptable_filter、iptable_mangle、iptable_raw、nat_table等,iptable的四类规则表,就都维护在这4个xt_table结构中。

xt_table中的规则保存在table->private->entries中,每条规则构成一个ipt_entry。一个xt_table中保存了多个链的规则,通过private->hook_entry[state->hook]来获取某个链规则在entries中的起始位置。每条ipt_entry规则中保存了多个xt_entry_match,每个xt_entry_match保存了规则的一个匹配特征和相应的匹配函数。举例来说,如果我们在一条NAT规则中配置了IP段和端口范围两个匹配条件,那么这条规则相应的ipt_entry中就会包含两个xt_entry_match,分别用于匹配IP段和端口范围。如果skb匹配了一个ipt_entry中的所有xt_entry_match,那么当前报文就匹配了一条iptables规则。接着就会按ipt_entry规则对应的xt_entry_target中指定的target结果返回,或者按指定的target函数处理。

连接追踪——conntrack

关于连接追踪(conntrack),本身是一个复杂的连接信息处理模块,在linux内核中同样基于netfilter实现,模块名为nf_conntrack。conntrack最常见的用途是和iptable_nat模块共同完成连接地址转换功能。可以参考连接跟踪(conntrack):原理、应用及 Linux 内核实现了解更多conntrack相关信息。

nf_conntrack在PRE_ROUTING、POST_ROUTING、LOCAL_OUT、LOCAL_IN这四个hook点注册了hook函数,优先级都是-200。由于conntrack注册的hook点与NAT完全相同,而且优先级高于NAT,因此保证了报文skb在匹配NAT规则前一定已经经过conntrack的处理,包含conntrack信息,保存在sk_buff._nfct中。

conntrack为每条连接都维护了连接信息,在hook函数中为每个skb查找或新建对应的连接信息,并记录在skb中。之后执行到nat的hook函数时,就能根据skb中的连接信息,来获取对应的nat处理操作。如果是连接的首包,那么会根据nat规则的匹配结果,将nat处理操作记录到连接信息结构nf_conn中。

用一张图来总结一下conntrack与NAT的整体处理流程:

docker的bridge网络模式

NAT iptable的一个重要应用就是docker的bridge网络模式。在bridge模式下,每个docker容器都会被分配一个独立的netns和虚拟网络设备veth。容器网络和主机网络间通过主机上的虚拟bridge设备转发网络报文。一般来说,给容器分配的都是单个主机内部的本地IP,是无法直接与主机外的其他外部IP通信的。这时就需要在主机上为容器网络报文做NAT转换来支持容器与外部设备的访问。

通过在创建容器时配置几条简单的NAT iptable规则,docker就实现了容器网络与主机网络地址的转换和互通。

root@VM-4-17-debian:~# docker run -d -p 54321:12345 ubuntu sleep infinity
59eab71ecbf8e7931580a7873a677bfe2ae3c7945b0ac91e5d6dea95282343ab

root@VM-4-17-debian:~# iptables -t nat -L --line-numbers -v
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1      308  9858 DOCKER     all  --  any    any     anywhere             anywhere             ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 MASQUERADE  all  --  any    docker0  anywhere             anywhere             ADDRTYPE match src-type LOCAL
2        5   323 MASQUERADE  all  --  any    !docker0  172.17.0.0/16        anywhere
3        0     0 MASQUERADE  tcp  --  any    any     172.17.0.2           172.17.0.2           tcp dpt:12345

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 DOCKER     all  --  any    any     anywhere             anywhere             ADDRTYPE match dst-type LOCAL

Chain DOCKER (2 references)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 DNAT       tcp  --  any    any     anywhere             anywhere             tcp dpt:54321 to:172.17.0.2:12345

上面就是一个docker容器创建后,docker为其自动生成的NAT规则。需要注意的是上述规则是在关闭了docker-proxy后的配置。docker自带了一个docker-proxy服务,启动这个服务后会和iptables共同完成地址转换工作,会造成NAT规则实现更加复杂和难以理解。

容器访问外部网络

docker通过一条通用的MASQUERADE为所有容器提供SNAT转换,也就是上图中POSTROUTING链上的rule2。这条规则把所有来自容器网络地址段(172.17.0.0/16)访问外部IP的报文都进行SNAT转换,从而支持容器的外部网络访问。

外部节点访问容器

外部节点对容器的访问实现相对就要复杂一些了。首先,只有创建时配置了端口映射的容器才能被其他节点主动访问。对于这类容器,需要配置相应的DNAT规则,将访问宿主机端口的报文转换成访问容器地址/端口的报文后,再通过bridge转发到对应容器上。

可以看到,docker将DNAT规则配置在自定义的DOCKER规则链中,而通过PREROUTING和OUTPUT链的报文会匹配DOCKER规则。这样配置的原因是访问容器的报文不一定来自外部节点(POSTROUTING rule1),也可能是来自宿主机或本地其他容器(OUTPUT rule1),在两条链上都需要配置相同DNAT规则,因此docker将规则统一到DOCKER链中。DOCKER链的rule1就是DNAT规则。

可以看到NAT规则表在POSTROUTING链上还有两条规则。这两条规则都用于支持本地容器或宿主机通过映射的主机端口来访问容器的情况。当在宿主机上访问127.0.0.1:54321或者hostip:54321时,除了DNAT外,还需要rule1来将源IP转换成bridge IP。而当容器通过hostip:54321来访问本容器的主机映射端口时,就需要rule3来将源IP转换成bridge IP。

总结一下:通过hostip:54321访问容器时,都会使用PREROUTING rule1/OUTPUT rule1=>DOCKER rule1来进行DNAT转换。同时,如果是在宿主机上访问,还会通过POSTROUTING rule1来进行SNAT转换;如果是容器自身访问,还会通过POSTROUTING rule3来进行SNAT转换。

小结

本文具体介绍了iptables中NAT规则的使用和实现,以及其在docker容器网络中的实际用法。最后来看一下开始时的问题:

  1. nat规则有哪几种,分别实现什么功能,用于什么场景?

    NAT规则有3种,其中SNAT和MASQUERADE规则都用于源地址转换,DNAT规则用于目的地址转换。
  2. nat规则通过哪些iptables匹配函数实现?

    通过iptable_nat_do_chain匹配规则,通过nf_nat_inet_fn进行转换。
  3. nat规则相关的iptables匹配函数在哪些netfilter hook点上被处理?

    在PRE_ROUTING、POST_ROUTING、LOCAL_OUT、LOCAL_IN这4个hook点上会调用nat相关的iptables函数。
  4. nat规则会对哪些网络报文生效?

    只对每条连接的第一个报文匹配NAT规则,后续报文按第一个报文匹配的NAT规则指定的地址转换操作进行转换。
  5. nat规则与conntrack功能有什么关系?两者如何协同完成一个连接上所有报文的地址转换?

    每条连接的第一个报文匹配NAT规则后,转换操作记录在连接的conntrack信息中,后续报文通过对应的conntrack信息完成地址转换。
  6. docker是如何通过iptables nat规则来实现bridge模式下容器的网络访问的?配置了哪些规则?

    docker会配置MASQUERADE规则和DNAT规则来支持容器的对外访问和外部节点访问容器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/79546.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Netty_06_手写RPC基础版(实践类)

文章目录一、前言二、整体运行三、客户端和服务端3.1 客户端3.2 服务端3.3 RpcServerInitializer和RpcClientInitializer四、小结一、前言 常用的rpc框架:dubbo thrift gRPC rpc定义:remote proceducer call rpc目的/解决的问题: 像调用本地…

Nginx实战应用-负载均衡

在上篇文章的基础上我们再创建两个服务,三个服务的端口分别是 8081 8082 8083. 2.Nginx配置 upstream块 upstream name{…} upstream gupao{ server 192.168.12.1:8081; server 192.168.12.1:8082; server 192.168.12.1:8083; } server { location / { pr…

平均月薪15k+?自动化测试工程师?3个月教你从“点工”蜕变为“码农”

前言 一、自动化测试工程师平均收入【看图(来自职友集)】 基本收入都在15k左右,随着技术的越来越牛逼工资也就会越来越高。 我的职业生涯开始和大多数测试人一样,刚开始接触都是纯功能界面测试。那时候在一家电商公司做测试&…

[附源码]计算机毕业设计基于vue+mysql开发的考试系统Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

【PAT甲级 - C++题解】1092 To Buy or Not to Buy

✍个人博客:https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 📚专栏地址:PAT题解集合 📝原题地址:题目详情 - 1092 To Buy or Not to Buy (pintia.cn) 🔑中文翻译:买还是不买 &#x…

JAVA中的运算符-关系运算符

文章目录0 写在前面1 关系运算符说明2 举例3 写在最后0 写在前面 JAVA包含丰富的关系运算符,这些关系运算符最终结果一定是boolean类型。即两个结果:true false 1 关系运算符说明 符号说明ab,判断a和b的值是否相等,成立为true,…

游戏开发48课 性能优化6

3.7.2 算法优化 思路是找出最耗CPU的算法或逻辑,优化之。 空间换时间。利用预排序/预处理/缓存/动态规划等等思路换取CPU的性能。选取更快的算法。属于数据结构和算法的范畴,思路是将O(n2)降低成O(n)或O(logn),具体可以参看《算法导论》《游…

springcloud 服务消费及熔断

目录 1. 服务消费方式 1.1 RestTemplate 1.2 feign 2. 服务熔断(降级) 2.1 在微服务架构中服务熔断的必要性 2.2 hystrix 1. 服务消费方式 1.1 RestTemplate 传统情况下在java代码里访问restful服务,一般使用Apache的HttpClient。不过…

PMP内容2

PMP内容2目录概述需求:实现思路分析1.沟通管理2.监督沟通:3.风险管理4.5.实施采购控制采购相关方管理:相关方Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make …

JavaWeb语法一:进程和线程的区别与联系

目录 1.进程的概念 1.1:进程控制块抽象(PCB) 1.2:进程调度的相关属性 1.2.1:进程的状态 1.2.2:优先级 1.2.3:上下文 1.2.4:记账信息 2:进程的虚拟地址空间 3.线程…

算法与数据结构29:四边形不等式技巧

四边形不等式技巧题目一题目二题目三四边形不等式技巧题目四题目五题目一 给定一个非负数组arr,长度为N, 那么有N-1种方案可以把arr切成左右两部分 每一种方案都有,min{左部分累加和,有部分累加和} 求这么多方案中,mi…

我当PM,把ChatGPT玩冒(bà)烟(gōng)了

最近ChatGPT太火,本拐也注册了一个。用着确实是十分上头。而且事实证明,在处理明确的工程类业务时,ChatGPT可以让程序员事倍功半(也有可能饭碗不保🙈)参见: 跟着ChatGPT手把实现一个websocket连…

数据管理 Valentina Studio Pro 12.6 Crack

Valentina Studio 被描述为与 MySQL、SQL Server、Maria DB、SQLite、PostgreSQL 以及最后但并非最不重要的 Valentina DB 数据库一起用于工作流程的数据库工具的通用管理。Valentina Studio 能够允许用户和开发人员连接所有重要的数据库并运行查询,以及创建图表以了…

Hadoop如何保证自己的江湖地位?Yarn功不可没

前言 任何计算任务的运行都离不开计算资源,比如CPU、内存等,那么如何对于计算资源的管理调度就成为了一个重点。大数据领域中的Hadoop之所以一家独大,深受市场的欢迎,和他们设计了一个通用的资源管理调度平台Yarn密不可分&#x…

高性能缓存Caffeine的基本使用方式

文章目录介绍性能比对使用方式一、Population(缓存类型)1.Cache2.Loading3.Asynchronous (Manual)4.Asynchronously Loading二、Eviction(驱除策略)1.Size-based(基于容量)2.Time-based(基于时间…

Redis Cluster 单机部署

高可用架构-Redis Cluster Redis服务器支持单机、主从复制、Sentinel、集群部署,部署架构也是由简单到复杂,Redis Cluster 集群架构是官方推荐应对大数据量、大并发场景部署方案。Redis的架构变迁如下图所示,其归属就是Redis Cluster Redis …

一款功能强大的课程报名系统 v6.2.0

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 课程报名系统可为专为教育培训机构设计的在线选课报名系统,学员可综合考虑课程分类、适合人群、上课地点、上课时间、课程价格等多种选报最合适的班级 课程报名系统发布v6.2…

组件库技术选型和开发环境搭建

点击上方卡片“前端司南”关注我您的关注意义重大原创前端司南本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!专栏上篇文章传送门:基于Vite打造业务组件库(开篇介绍…

盘点和总结秒杀服务的功能设计及注意事项技术体系

秒杀应该考虑哪些问题 超卖问题 分析秒杀的业务场景,最重要的有一点就是超卖问题,假如备货只有100个,但是最终超卖了200,一般来讲秒杀系统的价格都比较低,如果超卖将严重影响公司的财产利益,因此首当其冲…

0代码20min |微信接入chat-GPT

0代码20min | 微信接入chat-GPT 拥有一个openai账号 这个账号比较麻烦,需要国外手机号认证,这个的话可以直接参考这篇文章ChatGPT注册攻略 - 知乎 (zhihu.com),这篇文章是用在线接受短信的虚拟号码 - SMS-Activate这个网站来注册账号的。 用…