诺甘农圆盘

参考资料收集库

用户工具

站点工具


cisco:在cisco_ios_router上实现双向网络地址转换

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

蓝莓的铲屎官

蓝莓的铲屎官

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

14 人赞同了该文章

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

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

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

  • 第一个,理论上可以解决没错,但可维护性呢?不同的实体之间的网络互连是为了特定的业务交换 而存在的,这和Internet有本质的不同——Internet就是要让大家通,设计不通的人,脑袋里装着一脑袋的浆糊
  • 第二个,仅靠防火墙可以实现安全,但使用NAT并不见得是为了安全,更多是为了网络的维护性,不然久而久之,你就再也看不懂你的内网路由表了……

在负载均衡技术中解决来回路径不一致的问题,无论是网络设备实现的粗暴的四层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也不只是处理顺序的差别。先说一下我和这玩意打交道多年后的结论——

  • 第一个结论,outside和inside,就是为了将网络分为内部网络和外部网络, 这带来了不灵活的NAT配置,但实际上,非常适合路由器,因为路由器这个设备,一定是为了“连接自己和别人”。
  • 第二个结论,Cisco IOS Router 的NAT,一定是和数据流反着的,所你要“反着思考”
  • 第三个结论,虽然 Cisco IOS Router 确实支持双向地址转换,但它还是个废物,如果能用防火墙,还是用防火墙做最合适……在后文,我们会讲到它的一些“莫名其妙”的设计和限制。

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

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

  • inside to outside
  • outside to inside

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

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

  • 如果希望从outside访问inside一个服务器,我需要做的是 inside source static 转换
outside client --> inside server
  • 如果希望从inside访问outside服务器,转换inside的源地址,并且希望复用源地址,那么就需要做 inside source dynamic 转换
inside client --> outside server
  • 如果希望隐藏outside目标的地址,也就是从inside去访问outside的一个服务器但不访问outside的真实地址,而是访问一个分配给outside服务器的“inside”的地址,所以就需要做 outside source static 转换
inside client --> outside server inside address --> outside server outside address
  • 如果希望从outside访问inside一个服务器,并确保服务器看到一个“inside”地址,就需要做 outside source dynamic 转换,需要注意的是,这个场景不支持PAT,多个outside client不能复用1个inside地址(蠢爆了)
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来实现

  • inside source dynamic,实现inside client的源地址转换
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

  • outside source static,实现outside server的源地址转换
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转换,这里涉及一个重要的逻辑——路由器的数据包处理顺序

  • 从inside到outside是先路由,再转换
  • 从outside到inside是先转换,再路由

所以,访问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

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

  • outside source dynamic,实现outside client的源地址转换
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,又没有这个问题了……给外部实体两个地址就好了,分别对应两个方向发起的数据流

  • inside source static,实现inside server的源地址转换

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

ip nat inside source static 10.254.0.2 192.168.198.2
  • 仍然不要忘记配置路由, 只不过这次,不能只配一条路由了,所以我们可以考虑整个pool的路由都写过去,实际中取决于你如何为outside侧的client分配地址
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

cisco/在cisco_ios_router上实现双向网络地址转换.txt · 最后更改: 2020/11/08 21:43 由 packingbox