目录

[NAT] 在 Cisco IOS Router上实现双向网络地址转换

蓝莓的铲屎官

蓝莓的铲屎官

资深劝退大法师/BGP调参侠

14 人赞同了该文章

网络地址转换(NAT)大家都不陌生,大街上随便抓一个网工,也能告诉你“网络地址转换技术是为了解决IPv4公网地址不够用的……”这么一个标准的回答。但在实际中,网络地址转换技术能做到的远不止于此,许多人对NAT存在误解,抓着NAT大加抨击,对此也只能评价一句——少见多怪。

在多年的实践中,NAT至少在两个场景中非常的有意义——

属于不同的实体网络互连,使用NAT可以规避两方使用相同的IP地址规划而产生的冲突,在存在1个实体对接多个不同实体的时候,NAT更可以避免多个不同的实体使用相同的网络规划产生的问题; 没有哪个网络运维会希望各种外部实体使用的IP地址跑到自己的网络中。IPv6可以解决这个问题么?只使用防火墙保证安全足够了吗?

在负载均衡技术中解决来回路径不一致的问题,无论是网络设备实现的粗暴的四层TCP负载均衡,还是F5实现的七层负载均衡,SNAT在这种场景中始终是有用的

P.S. NAT阻碍了IPv6的发展?不,是IPv6自己太蠢,设计过于理想化,而且过时。

今天这篇,主要讲一下第一个场景,不同的实体的网络对接中的NAT运用,以及在Cisco IOS Router上的实现原理和范例


需求

两个不同的实体因为业务需要,信息系统需要互通,网络工程师就要把网络打通。但我们不可能让网络直接就全通了,而一定是将其限定在特定业务数据的范围之内。除了防火墙上访问控制,在路由层面,由于两个实体完全有可能采用同样或者部分相同的私网IP地址规划,仅靠路由无法解决这个问题,因而就需要使用NAT来解决这个问题。

在不同实体互连的架构上,如果网络工程师从一开始就设计了合理的NAT模型,可以避免很多问题,反之,随着互连的外部实体越来越多,网络里的路由也会越来越乱,哪天挖坑的人一走,接坑的人就是一脸懵逼。

这里的需求还有人情因素——强势的外部实体可能并不在乎你的网络咋样咋样,他只要求你,要将访问过来的地址映射为他“规划”的地址。当然了,公司之间的合作,任何时候都是有强有弱,这里强那里弱,这里硬那里软的,所以,更普遍的情况应该是——

实体A要求实体B,将从B访问A的数据流的源地址,转换为实体A要求的地址。简单来说就是实体A是爸爸,实体B要听话。但是实体B也不希望看见实体A的地址,而实体A并不怎么好说话,不想做地址转换,所以实体B就只能“自行”对实体A的地址进行地址转换。

于是,当实体B访问实体A的时候,实体B的主机需要访问一个假的地址,实体B需要对这个地址进行翻译,同时,还要将自己的源地址翻译为实体A要求的地址。

我估计看到这里,你已经晕了。没关系,下面的例子会说清楚一切的。


例子

1.jpg

如上所示,随便画了一个拓扑。TEST-RTR是一个外部实体,用蓝色的图标,GW,RTR1,RTR2则是本地网络,用绿色的图标。GW里是两个网络的边界,对TEST-RTR的接口为outside,对本地网络的接口为inside。

在这个简易拓扑中,RTR1和RTR2想要访问TEST-RTR,但本地网络中并不想出现TEST-RTR的路由,同时,TEST-RTR对于RTR1和RTR2访问过来的地址也有要求。

RTR1和RTR2在本地运维的策略下,希望通过访问10.254.1.1这个地址,来访问TEST-RTR的地址,也就是192.168.199.199。TEST-RTR希望RTR1和RTR2访问过来的时候,把自己的地址变成192.168.198.1,它只允许这个地址来访问它。

所以,GW就需要做两个方向的地址转换。下面为访问的是RTR1访问TEST-RTR的来回流量的示意图——

2.jpg

RTR1的地址为10.254.0.2,它将访问10.254.1.1,这是“RTR1认为的TEST-RTR的IP地址”,对,尽管是个假的。

10.254.0.2 –> 10.254.1.1 的数据包在GW上,需要被翻译为 192.168.198.1 –> 192.168.199.199,真实的源地址被映射为TEST-RTR所要求的地址,反之,映射后的TEST-RTR的地址被翻译为它的真实地址。

10.254.0.2 --> 10.254.1.1 变成 192.168.198.1 --> 192.168.199.199

P.S. 红色的虚线代表了真实地址到映射地址 的翻译,而黑色的虚线则代表映射地址到真实地址 的翻译。

从TEST-RTR返回的数据包,目的地址为192.168.198.1,源地址则是TEST-RTR的真实地址192.168.199.199,GW对返回的数据流再次进行翻译——

192.168.199.199 --> 192.168.198.1 变成 10.254.1.1 --> 10.254.0.2

熟悉这个套路的人,绝大部分,使用防火墙来完成这波操作,然鹅,其实路由器也可以很轻易的实现,然鹅鹅,很多人并不知道路由器也可以做这个事情……

这个真不怪大家,Cisco IOS Router 的NAT逻辑实在过于吊诡,且文档写的晦涩难懂,当年考CCIE,在这个东西上真的是想破了脑子

所以,在继续讲如何实现之前,我们先复习一波Cisco IOS Router 那吊诡无比的NAT逻辑


Cisco IOS Router 的NAT实现

曾有人吐槽过说outside和inside这个名字起的不好,应该叫pre-Routing和post-Routing,那位大哥文章写的非常激愤,然而,还是没想明白。

outside和inside这个名字有问题吗?并没有。并且,outside和inside也不只是处理顺序的差别。先说一下我和这玩意打交道多年后的结论——

IOS Router 的NAT实现过于吊诡,如果只是看书,很容易就把自己看晕过去,什么inside local,inside global,outside local,outside global……简直emmmmmm,看看ASA的逻辑,就清楚多了,Real IP & Mapped IP,多清晰……

所以,用好IOS Router的NAT,首先要搞清楚数据流的流向——

两个流向的流量的逻辑是有些差异的,并且有一些特性只在特定的方向上才能实现

其次,如上第二点所述,IOS Router的NAT的配置逻辑总是反着的,意即任何时候,我们在IOS Router上都是在配置“源地址转换”,也就是SNAT,而对于目的地址的转换,实际上是SNAT的反向逻辑,因此——

outside client --> inside server
inside client --> outside server
inside client --> outside server inside address --> outside server outside address
outside client outside address --> outside client inside address --> inside server

当然,IOS Router 支持一种Destination的场景——ip nat inside destination ,是一种四层负载均衡实现(好像也被强化过了!)

以上四个逻辑,基本上就是我们通常做NAT时需要实现的逻辑,我们永远只需要记住的是,转换 outside 的地址就是 outside source ,转换inside侧的地址就是 inside source‘

至于那个什么gloabl address和local address的概念……忘记它吧,蠢死了。


例子解析1 inside client –> outside server

看明白上面四个逻辑,再回过头去看例子——

10.254.0.2 --> 10.254.1.1 变成 192.168.198.1 --> 192.168.199.199
192.168.199.199 --> 192.168.198.1 变成 10.254.1.1 --> 10.254.0.2

192.168.199.x 和 192.168.198.x 都是 outside 地址

10.0.254.1.x 和 10.254.0.x 都是 inside 地址

在这个例子中,数据流是由inside侧发起,访问outside侧的server,对比上面的四个逻辑,我们需要两条NAT来实现

ip access-list extended insideClient
  permit ip host 10.254.0.2 any
  permit ip host 10.254.0.3 any
ip nat pool insidePool 192.168.198.1 192.168.198.1 prefix-length 25
ip nat inside source list insideClient pool insidePool overload

P.S. 这里使用了PAT来复用一个地址,关键字overload

ip nat outside source static 192.168.199.199 10.254.1.1

P.S. 前面我说忘记global address和local address的概念吧,太愚蠢,你只需要记住,不管是inside还是outside,写在前面的是Real IP,写在后面的是NATed IP

当然,注意接口的inside和outside,不能配反

interface GigabitEthernet4
 ip address 10.254.0.1 255.255.255.248
 ip nat inside
interface GigabitEthernet1
 ip address 192.168.199.2 255.255.255.248
 ip nat outside

然后,非常重要的事情来了——

当inside侧的client,10.254.0.2要去访问10.254.1.1的时候,流量从inside进入,并不会直接触发NAT转换,这里涉及一个重要的逻辑——路由器的数据包处理顺序

所以,访问10.254.1.1的数据包必须先通过路由确定到下一跳的出接口,才能触发NAT转换,因此,NAT路由器上要有一条路由

ip route 10.254.1.1 255.255.255.255 192.168.199.1

当然,指向真实地址192.168.199.199的路由也不能少

ip route 192.168.199.199 255.255.255.255 192.168.199.1

在路由器匹配第一条路由,确定了出接口后,进一步会匹配到NAT策略,由于我们已经设置了静态NAT,所以10.254.1.1会被转换为192.168.199.199,与此同时,由于还存在inside侧的dynamic转换,所以源地址10.254.0.2会被转换为192.168.198.1

在NAT GW上执行 show ip nat translations 会看到如下输出

Pro  Inside global   Inside local          Outside local         Outside global
---  ---                   ---                   10.254.1.1            192.168.199.199
tcp  192.168.198.1:34414   10.254.0.2:34414      10.254.1.1:23         192.168.199.199:23

如此这般,192.168.199.199看到通过telnet连接过来的IP地址将会是192.168.198.1,反向的数据流由于优先匹配NAT表项,因此会再次转换,双向数据皆可通

在192.168.199.199,也就是TEST-RTR这台路由器上,通过log日志,可以看到登陆过来的地址是什么——

*Jun 28 04:25:19.198: %SEC_LOGIN-5-LOGIN_SUCCESS: Login Success [user: admin] [Source: 192.168.198.1] [localport: 23] at 04:25:19 UTC Fri Jun 28 2019

大功告成!

不,还没完,这是inside client –> outside server

那outside client –> inside server 呢?


例子解析2 outside client –> inside server

这是一个正好反过来的场景,同样的,我们去匹配上述四个逻辑,可以得出结论——

ip access-list extended outsideClient
  permit ip host 192.168.199.199 any
ip nat pool outsidePool 10.254.1.2 10.254.1.100 prefix-length 24
ip nat outside source list outsideClient pool outsidePool

其实配置上差别不大,只是inside换成outside而已,但是,需要注意,我也不知道为什么,C记也没解释,从outside到inside的Dynamic转换是不支持PAT的,这真是见了个鬼了

所以在和外部实体对接的是,如果使用路由器来做双向转换,当数据流由外部实体发起时,就不能只分给外部实体一个地址,而必须分一个Pool,和外部实体实际要发起数据流的客户端数量相当或者更多,换言之,最好的方法是为外部实体分一个IP段

然鹅,如果是防火墙来实现双向NAT,又没有这个问题了……给外部实体两个地址就好了,分别对应两个方向发起的数据流

配好了从outside访问到inside的Dynamic NAT之后,当然还少不了inside侧的静态转换

ip nat inside source static 10.254.0.2 192.168.198.2
ip route 10.254.1.0 255.255.255.0 192.168.199.1
ip route 192.168.199.199 255.255.255.255 192.168.199.1

这样配过之后,我们可以看一下NAT GW的转换表,这就跟我们通常配置的inside静态NAT没什么区别

Pro  Inside global   Inside local          Outside local         Outside global
---  192.168.198.2         10.254.0.2            ---                   ---
Total number of translations: 1

从outside client连接到inside server

TEST-RTR#telnet 192.168.198.2 /source-interface loopback 0
Trying 192.168.198.2 ... Open

User Access Verification

Username: admin
Password:
RTR1_lab#

然后查看NAT GW的转换表

Pro  Inside global   Inside local          Outside local         Outside global
---  ---                   ---                   10.254.1.2            192.168.199.199
---  192.168.198.2         10.254.0.2            ---                   ---
tcp  192.168.198.2:23      10.254.0.2:23         10.254.1.2:38401      192.168.199.199:38401
tcp  192.168.198.2:23      10.254.0.2:23         10.254.1.2:24577      192.168.199.199:24577

由于我发起了两次连接,因此出现了两条NAT转换。其实从这里看可以发现,从 outside –> inside 完全有能力实现PAT的,只是不知道为什么,C记在软件上做了限制,将overload这一feature,限制在了 inside –> outside 这一方向上。

P.S. 从路由器那吊诡的local/global地址的设计中不难看出,在最初设计NAT时,应该是只考虑了路由器连接内网和Internet这样的场景 ,而不是遵循“真实地址”和“映射地址”这样的逻辑,这可能就是问题的根源了。场景化来设计功能是C记诸多设备上都有的问题,好处是功能设计出来,用户可能更容易理解(也可能更不容易理解),但缺点就是会有一些莫名其妙的限制。

我们可以进一步观察从 outside –> inside 这个方向上Dynamic NAT的实现,现在,修改NAT GW上的配置——

ip access-list extended outsideClient
  permit ip host 192.168.199.199 any
  permit ip host 192.168.199.198 any
ip nat pool outsidePool 10.254.1.2 10.254.1.2 prefix-length 24

Client多了一个,我们用TSET-RTR上的另一个IP地址来模拟。同时我把pool内的地址限制在了1个

测试结果是——

*Jun 28 05:50:38.022: %IOSXE-6-PLATFORM: R0/0: cpp_cp: QFP:0.0 Thread:000 TS:00000936958686029110 %NAT-6-ADDR_ALLOC_FAILURE: Address allocation failed; pool 4 may be exhausted [2]
GW#show ip nat trans
Pro  Inside global   Inside local          Outside local         Outside global
---  ---                   ---                   10.254.1.2            192.168.199.199
---  192.168.198.2         10.254.0.2            ---                   ---
tcp  192.168.198.2:23      10.254.0.2:23         192.168.199.198:42497 192.168.199.198:42497
tcp  192.168.198.2:23      10.254.0.2:23         10.254.1.2:24577      192.168.199.199:24577
Total number of translations: 4

不出意外的,.199被正常转换,而.198没有,因为pool内的地址资源耗尽

让我们来试试overload参数……

ip nat outside source list outsideClient pool outsidePool overload
   ^
% Invalid input detected at '^' marker.

啊,看来并没有隐藏这个参数,而是真的没有……shit!

小结

虽然Router的NAT功能在不断地版本迭代中一直在被强化,但始终是“不那么好用”,除了双转,在IOS-XE上,还有VASI这一看上去很强,实际上复杂的1B的神器。对一个网络来说,单个Platform的feature是不是强,完整,很多时候并不重要,只要它在合适的位置发挥合适的作用就行了。所以对于IOS Router的NAT,我的使用感受是——没办法的时候就用用,能上防火墙的时候就不依靠它。就酱。


引用文章链接https://zhuanlan.zhihu.com/p/71065075