微信公众号:DPDK VPP源码分析 关注公众号可了解更多的学习资源,请留言:资料。 加入vpp相关的微信群,请留言:加群,扫码加我微信,注明加群。 vpp-dpdk相关学习资料:https://github.com/jin13417/dpdk-vpp-learning。 公众号回复:nat 、ipsec,获取对应学习资料 版权归本公众号所有,如需转载请注明出处。 如果你觉得内容对你有帮助,欢迎转发、点赞、赞赏[1]
近期,我们接到客户的一项明确需求:实现外部网络对内部网络特定服务(例如Web服务器、邮件服务)的无地域限制的随机访问。针对这一需求,NAT技术中的“两次NAT”(Twice NAT) 功能脱颖而出,成为理想的解决方案。此功能专为这类场景定制,能够有效地将源自外部、指向公共IP及端口的访问请求,精准桥接到内部网络中相对应的服务主机上。
两次NAT技术赋予在单一规则下同时转换数据包的源头与目标地址的能力,极大增强了网络配置的灵活性。当前实施重点聚焦于NAT44协议,专门支持由外网向内网服务会话的双向转换。值得注意的是,静态映射配置下的两次NAT仅适用于那些由外部网络发起到本地网络的会话初始化过程,确保了对外服务的同时保持了内网结构的安全与隔离性。
正好,VPP(Vector Packet Processing)平台在其NAT-ED模式下内置了对两次NAT(Twice NAT)功能的支持。VPP官方文档提供了详尽的配置指南,为了实践这一特性,本篇文章将遵循这些官方指导,逐步搭建实验环境,以验证两次NAT功能的实际应用效果。
参考官方文档网络架构,在腾讯云主机上搭建验证环境,网络拓扑如下:
代码语言:javascript复制 --------------------------
| linux netns inside |
| 10.0.0.2/24 (local host) |
--------------------------
|
---------------------------------
| 10.0.0.1/24 (tap1) (nat inside) |
| 20.0.0.1/24 (tap2) (nat outside)|
| vpp |
---------------------------------
|
---------------------------
| 20.0.0.2/24 (remote host) |
| linux netns outside |
---------------------------
在linux内核上创建2个命令空间 inside和outside,然后在vpp中分别创建两个tap接口,tap1实现vpp和命名空间inside直接通信。tap2实现vpp和命名空间outside通信。vpp使能NAT功能,并配置路由前NAT(理论上路由后NAT也可以,本文完全参照官方文档配置实现),具体配置如下:
- 内核创建2个命名空间
ip netns add inside
ip netns add outside
- 创建两个tap接口
creat tap id 1 host-ns inside host-ip4-addr 10.0.0.2/24 host-ip4-gw 10.0.0.1 host-if-name tap1
creat tap id 2 host-ns outside host-ip4-addr 20.0.0.2/24 host-ip4-gw 20.0.0.1 host-if-name tap2
set interface state tap1 up
set interface ip address tap1 10.0.0.1/24
set interface state tap2 up
set interface ip address tap2 20.0.0.1/24
在创建tap接口时,需要指定设置内核tap1接口ip地址及默认网关。
- NAT基本配置
#启用nat插件
nat44 plugin enable sessions 1000
#配置内部接口
set interface nat44 in tap1
#配置外部接口
set interface nat44 out tap2
#设置nat地址池和twice-nat地址池
nat44 add address 192.168.60.101
nat44 add address 20.0.0.1 twice-nat
此处NAT地址池twice-nat地址池配置和VPP官方文档中设置正好是相反的。将外部访问地址设置到NAT地址池中,将内网接口设置到twice-nat地址池中。
- 添加两次NAT的映射
nat44 add static mapping tcp local 10.0.0.2 5201 external 192.168.160.101 5201 twice-nat
这里需要注意,在官方文档中external 地址写的不是twice-nat地址池的地址,这里是存在问题的。上面静态NAT映射配置完之后,我们分别查询当前nat会话为空,flow-hash存在2条记录,如下所示:
代码语言:javascript复制#查询静态配置
dpdk-vpp源码分析: show nat44 static mappings
NAT44 static mappings:
TCP local 10.0.0.2:5201 external 192.168.160.101:5201 vrf 0 twice-nat
#查询会话表
dpdk-vpp源码分析: show nat44 sessions
NAT44 ED sessions:
-------- thread 0 vpp_main: 0 sessions --------
#查询hash tables表
dpdk-vpp源码分析: show nat44 hash tables detail
Hash table 'ed-flow-hash'
[288]: heap offset 44083784, len 1, refcnt 2, linear 0
0: local 10.0.0.2:5201 remote 0.0.0.0:0 proto TCP fib 0 thread-index 0 session-index 0
[875]: heap offset 44144832, len 1, refcnt 2, linear 0
0: local 0.0.0.0:0 remote 192.168.160.101:5201 proto TCP fib 0 thread-index 0 session-index 0
2 active elements 2 active buckets
0 free lists
0 linear search buckets
heap: 1 chunk(s) allocated
bytes: used 104k, scrap 0
配置完NAT映射表之后生成2个2个flow-hash表,其中875对应的是out2in反向;288对应的in2out方向。这两个表的作用就是用来生成NAT会话的。
接下来在inside命名空间开启IPerf3 服务端,然后在outside命名空间设置iperf3客户端连接服务器。连接正常建立。
NAT会话翻译如下:
从外到内翻译: 源地址:20.0.0.2 -> 192.168.160.101 目标地址:20.0.0.1 -> 10.0.0.2 从内到外的翻译: 源地址:10.0.0.2 -> 20.0.0.1 目标地址:192.168.160.101 -> 20.0.0.2
我们抓取了IPerf3建立tcp连接SYN交互trace流程:
代码语言:javascript复制00:04:48:324556: virtio-input
virtio: hw_if_index 2 next-index 4 vring 0 len 74
hdr: flags 0x00 gso_type 0x00 hdr_len 0 gso_size 0 csum_start 0 csum_offset 0 num_buffers 1
00:04:48:324561: ethernet-input
frame: flags 0x1, hw-if-index 2, sw-if-index 2
IP4: 02:fe:79:00:b8:be -> 02:fe:b9:84:d0:66
00:04:48:324567: ip4-input
TCP: 20.0.0.2 -> 192.168.160.101
tos 0x00, ttl 64, length 60, checksum 0xbf15 dscp CS0 ecn NON_ECN
fragment id 0x0697, flags DONT_FRAGMENT
TCP: 52558 -> 5201
seq. 0xd803aaad ack 0x00000000
flags 0x02 SYN, tcp header: 40 bytes
window 64240, checksum 0x539c
options:
mss 1460, window scale -139065856, timestamp -139065856, echo/reflected timestamp, sack permitted
00:04:48:324571: ip4-sv-reassembly-feature
[not-fragmented]
00:04:48:324574: nat-pre-out2in
out2in next_index 6 arc_next_index 10
00:04:48:324576: nat44-ed-out2in
NAT44_OUT2IN_ED_FAST_PATH: sw_if_index 2, next index 7
search key local 20.0.0.2:52558 remote 192.168.160.101:5201 proto TCP fib 0 thread-index 0 session-index 0
slow path because lookup failed
00:04:48:324579: nat44-ed-out2in-slowpath
NAT44_OUT2IN_ED_SLOW_PATH: sw_if_index 2, next index 10, session 1, translation result 'success' via o2if
i2of match: saddr 10.0.0.2 sport 5201 daddr 20.0.0.1 dport 11570 proto TCP fib_idx 0 rewrite: saddr 192.168.160.101 sport 5201 daddr 20.0.0.2 dport 52558
o2if match: saddr 20.0.0.2 sport 52558 daddr 192.168.160.101 dport 5201 proto TCP fib_idx 0 rewrite: saddr 20.0.0.1 sport 11570 daddr 10.0.0.2 dport 5201 txfib 0
TCP state: closed
00:04:48:324594: ip4-lookup
fib 0 dpo-idx 5 flow hash: 0x00000000
TCP: 20.0.0.1 -> 10.0.0.2
tos 0x00, ttl 64, length 60, checksum 0x1623 dscp CS0 ecn NON_ECN
fragment id 0x0697, flags DONT_FRAGMENT
TCP: 11570 -> 5201
seq. 0xd803aaad ack 0x00000000
flags 0x02 SYN, tcp header: 40 bytes
window 64240, checksum 0x4ac6
options:
mss 1460, window scale -139065856, timestamp -139065856, echo/reflected timestamp, sack permitted
00:04:48:324597: ip4-rewrite
tx_sw_if_index 1 dpo-idx 5 : ipv4 via 10.0.0.2 tap1: mtu:9000 next:4 flags:[] 02fe696ff93702fe051276900800 flow hash: 0x00000000
00000000: 02fe696ff93702fe0512769008004500003c069740003f061723140000010a00
00000020: 00022d321451d803aaad00000000a002faf04ac60000020405b40402
00:04:48:324599: tap1-output
tap1 flags 0x00180005
IP4: 02:fe:05:12:76:90 -> 02:fe:69:6f:f9:37
TCP: 20.0.0.1 -> 10.0.0.2
tos 0x00, ttl 63, length 60, checksum 0x1723 dscp CS0 ecn NON_ECN
fragment id 0x0697, flags DONT_FRAGMENT
TCP: 11570 -> 5201
seq. 0xd803aaad ack 0x00000000
flags 0x02 SYN, tcp header: 40 bytes
window 64240, checksum 0x4ac6
options:
mss 1460, window scale -139065856, timestamp -139065856, echo/reflected timestamp, sack permitted
00:04:48:324601: tap1-tx
buffer 0x9d232: current data 0, length 74, buffer-pool 0, ref-count 1, trace handle 0x11
l2-hdr-offset 0 l3-hdr-offset 14
hdr-sz 0 l2-hdr-offset 0 l3-hdr-offset 14 l4-hdr-offset 0 l4-hdr-sz 0
IP4: 02:fe:05:12:76:90 -> 02:fe:69:6f:f9:37
TCP: 20.0.0.1 -> 10.0.0.2
tos 0x00, ttl 63, length 60, checksum 0x1723 dscp CS0 ecn NON_ECN
fragment id 0x0697, flags DONT_FRAGMENT
TCP: 11570 -> 5201
seq. 0xd803aaad ack 0x00000000
flags 0x02 SYN, tcp header: 40 bytes
window 64240, checksum 0x4ac6
options:
mss 1460, window scale -139065856, timestamp -139065856, echo/reflected timestamp, sack permitted
Packet 19
00:04:48:324645: virtio-input
virtio: hw_if_index 1 next-index 4 vring 0 len 74
hdr: flags 0x00 gso_type 0x00 hdr_len 0 gso_size 0 csum_start 0 csum_offset 0 num_buffers 1
00:04:48:324649: ethernet-input
frame: flags 0x1, hw-if-index 1, sw-if-index 1
IP4: 02:fe:69:6f:f9:37 -> 02:fe:05:12:76:90
00:04:48:324654: ip4-input
TCP: 10.0.0.2 -> 20.0.0.1
tos 0x00, ttl 64, length 60, checksum 0x1cba dscp CS0 ecn NON_ECN
fragment id 0x0000, flags DONT_FRAGMENT
TCP: 5201 -> 11570
seq. 0xf4b46973 ack 0xd803aaae
flags 0x12 SYN ACK, tcp header: 40 bytes
window 65160, checksum 0xab86
options:
mss 1460, window scale -139065856, timestamp -139065856, echo/reflected timestamp, sack permitted
00:04:48:324656: ip4-sv-reassembly-feature
[not-fragmented]
00:04:48:324658: nat-pre-in2out
in2out next_index 2 arc_next_index 10
00:04:48:324660: nat44-ed-in2out
NAT44_IN2OUT_ED_FAST_PATH: sw_if_index 1, next index 10, session 1, translation result 'success' via i2of
i2of match: saddr 10.0.0.2 sport 5201 daddr 20.0.0.1 dport 11570 proto TCP fib_idx 0 rewrite: saddr 192.168.160.101 sport 5201 daddr 20.0.0.2 dport 52558
o2if match: saddr 20.0.0.2 sport 52558 daddr 192.168.160.101 dport 5201 proto TCP fib_idx 0 rewrite: saddr 20.0.0.1 sport 11570 daddr 10.0.0.2 dport 5201 txfib 0
search key local 10.0.0.2:5201 remote 20.0.0.1:11570 proto TCP fib 0 thread-index 0 session-index 0
TCP state: closed
00:04:48:324664: ip4-lookup
fib 0 dpo-idx 4 flow hash: 0x00000000
TCP: 192.168.160.101 -> 20.0.0.2
tos 0x00, ttl 64, length 60, checksum 0xc5ac dscp CS0 ecn NON_ECN
fragment id 0x0000, flags DONT_FRAGMENT
TCP: 5201 -> 52558
seq. 0xf4b46973 ack 0xd803aaae
flags 0x12 SYN ACK, tcp header: 40 bytes
window 65160, checksum 0xb45c
options:
mss 1460, window scale -139065856, timestamp -139065856, echo/reflected timestamp, sack permitted
00:04:48:324666: ip4-rewrite
tx_sw_if_index 2 dpo-idx 4 : ipv4 via 20.0.0.2 tap2: mtu:9000 next:3 flags:[] 02fe7900b8be02feb984d0660800 flow hash: 0x00000000
00000000: 02fe7900b8be02feb984d06608004500003c000040003f06c6acc0a8a0651400
00000020: 00021451cd4ef4b46973d803aaaea012fe88b45c0000020405b40402
00:04:48:324668: tap2-output
tap2 flags 0x00380005
IP4: 02:fe:b9:84:d0:66 -> 02:fe:79:00:b8:be
TCP: 192.168.160.101 -> 20.0.0.2
tos 0x00, ttl 63, length 60, checksum 0xc6ac dscp CS0 ecn NON_ECN
fragment id 0x0000, flags DONT_FRAGMENT
TCP: 5201 -> 52558
seq. 0xf4b46973 ack 0xd803aaae
flags 0x12 SYN ACK, tcp header: 40 bytes
window 65160, checksum 0xb45c
options:
mss 1460, window scale -139065856, timestamp -139065856, echo/reflected timestamp, sack permitted
00:04:48:324670: tap2-tx
buffer 0x9f90b: current data 0, length 74, buffer-pool 0, ref-count 1, trace handle 0x12
natted l2-hdr-offset 0 l3-hdr-offset 14
hdr-sz 0 l2-hdr-offset 0 l3-hdr-offset 14 l4-hdr-offset 0 l4-hdr-sz 0
IP4: 02:fe:b9:84:d0:66 -> 02:fe:79:00:b8:be
TCP: 192.168.160.101 -> 20.0.0.2
tos 0x00, ttl 63, length 60, checksum 0xc6ac dscp CS0 ecn NON_ECN
fragment id 0x0000, flags DONT_FRAGMENT
TCP: 5201 -> 52558
seq. 0xf4b46973 ack 0xd803aaae
flags 0x12 SYN ACK, tcp header: 40 bytes
window 65160, checksum 0xb45c
options:
mss 1460, window scale -139065856, timestamp -139065856, echo/reflected timestamp, sack permitted
流量从远程主机发起,当NAT设备接收到SYN报文时,在out2in方向slow path处理节点生成了twice-nat的双向会话,会话如下:
代码语言:javascript复制 i2o 10.0.0.2 proto TCP port 5201 fib 0
o2i 192.168.160.101 proto TCP port 5201 fib 0
external host o2i 20.0.0.2:52558 i2o 20.0.0.1:11570
i2o flow: match: saddr 10.0.0.2 sport 5201 daddr 20.0.0.1 dport 11570 proto TCP fib_idx 0
rewrite: saddr 192.168.160.101 sport 5201 daddr 20.0.0.2 dport 52558
o2i flow: match: saddr 20.0.0.2 sport 52558 daddr 192.168.160.101 dport 5201 proto TCP fib_idx 0
rewrite: saddr 20.0.0.1 sport 11570 daddr 10.0.0.2 dport 5201 txfib 0
index 1
last heard 290.68
timeout in 12.25
total pkts 147, total bytes 117745
static translation
twice-nat
至此,我们已深入学习了两次NAT(Twice NAT)的基础配置知识。然而,在遵循官方指南配置iperf3进行测试时,遇到了连接无法成功建立的问题。在此过程中,我辨识出配置指南中可能存在两处误导之处,并已在文中详细阐述了这些疑点。尽管通过一系列调试,功能现已正常运行,并且测试结果与期望的两次NAT效果相吻合,我个人对于修改后的配置准确性仍持保留意见,缺乏完全的自信,需要和vpp官方予以确认。