前言
有两台服务器,服务器 A 是移动的机房,服务器 B 是我们的阿里云服务器。‘
这两台服务器已经通过 GRE 隧道连接实现了数据互通。
移动侧内网内每个 IP 都代表了一张物联卡,此处用 A1、A2 ... An 表示。
经测试,发现:
- ✅ An <-> B 
- ❌ A1 <-> A2 
原因是移动侧做了限制 —— 不允许【终端互访】。也就是即使卡都是同一个内网内,互相之间仍然是隔离的。
PING 抓包发现 ICMP 数据包并没有发送到服务器 B。突发奇想,移动会拦截卡内网 IP 互访,那如果 B 划分一块内网网段和卡 IP 网段做映射,是不是可以绕过拦截呢?
IP 网段映射:A1 <=> C1,A2 <=> C2 ... An <=> Cn。
设计思路:
A1 (10.6.0.x) --[移动限制]--> A2 (10.6.0.y) ❌ 被阻断
改为:
A1 (10.6.0.x) --> 172.20.0.y (映射IP) --> GRE --> B --> DNAT回 10.6.0.y --> A2 ✅基本参数
gre:gre-pgw1
卡 IP 网段:10.6.0.0/22
B 内网 IP:172.18.141.168
DNAT 映射网段(虚拟 IP 网段):172.20.0.0/22(后续扩展可以考虑 172.20.0.0/14)
少量固定映射
直接写 iptables 规则:
iptables -t nat -A PREROUTING -d 172.20.0.1 -j DNAT --to-destination 10.6.0.1
iptables -t nat -A PREROUTING -d 172.20.0.2 -j DNAT --to-destination 10.6.0.2一、NETMAP(推荐)
Netmap 是 iptables 的 NAT 扩展,能够实现整个网段的一对一映射转换。
# 1. 添加路由,映射网段指定 gre(IP 不真实存在,只是 NAT 用)
# 如果设置为指向 eth0 设备的路由,要到阿里云 VPC 路由表配置跳回本ECS
# ip route add 172.20.0.0/22 dev gre-pgw1
# 上述 ip route add 非必须, 看情况添加:
# (1)如果有多个 gre 用同一个内网网段或者网段重叠, 才用指定 dev 挂载网卡
# gre1: 10.6.x.x, gre2: 10.6.x.x
# (2)如果 10.6.x.x 唯一归属于 gre1,则不需要这条路由。
# gre1: 10.6.x.x, gre2: 10.7.x.x
# 2. OUTPUT DNAT(用于本机发出的流量, 不经过 PREROUTING 链)
# 服务器 B ping 172.20.0.5
iptables -t nat -A OUTPUT -d 172.20.0.0/22 -j NETMAP --to 10.6.0.0/22
# 3. PREROUTING DNAT(用于转发流量)
# 10.6.0.1 ping 172.20.0.5
iptables -t nat -A PREROUTING -d 172.20.0.0/22 -j NETMAP --to 10.6.0.0/22
# tcpdump gre-pgw1, 会看见两条收发:
# 1、10.6.0.1 ping 172.20.0.5(10.6.0.1 ping => GRE 对端发送到服务器 B)
# 2、10.6.0.10 ping 10.6.0.5 (服务器 B gre 10.6.0.5)
二、nftables
nftables 作为 Linux 内核网络栈 的官方接班人,深度集成在内核中。它在内核网络的协议栈中预定义了几个关键的 Hook(钩子)点,当一个数据包从网卡进入内核后,它会依次流经这些钩子点。内核发现有 nftables 规则挂载(nft add rule ip nat prerouting...)时会做相应的处理。
Linux 内核 3.13(部分支持) 或 3.15(完整支持) 及以上使用。
我的阿里云 CentOS 7 通过 uname -r 看到内核是 3.10,使用不了 nftables。因此,下面命令也未曾验证,风险自行甄别。
# 创建 nftables 表、链和映射
# 1、创建表, 协议是IP, 表名为nat
nft add table ip nat
# 2、链名为 prerouting, type为 nat, hook点是 prerouting, 优先级为 -100                 
nft add chain ip nat prerouting { type nat hook prerouting priority -100 \; }   
# 3、计算动态映射: 172.20.0.0/22 -> 10.6.0.0/22
nft add rule ip nat prerouting ip daddr 172.20.0.0/22 \
    dnat to ip daddr & 0.0.255.255 | 10.6.0.0
# 16进制写法
nft add rule ip nat prerouting ip daddr 172.20.0.0/22 \
    dnat to ip daddr & 0x0000ffff | 0x0a060000规则持久化:
通过命令行添加的 nftables 规则在重启后不会保留。若要永久生效,请将当前规则集导出到配置文件:
nft list ruleset > /etc/nftables.conf三、ipset(不满足需求)
ipset 网段类型 hast:net,iptables 不是按照网段对应关系映射的,而是从指定的 IP 范围内随机或轮询一个IP。访问 172.20.0.1 期望是 DNAT 到 10.6.0.1,结果 DNAT 到了 10.6.0.35,不满足业务要求。
# 1、创建一个 hash:net 类型的 ipset, net表示网段
ipset create vnets hash:net
# 2、添加网段
ipset add vnets 172.20.0.0/22
# 3、映射
iptables -t nat -A PREROUTING -m set --match-set vnets dst -j DNAT --to-destination 10.6.0.1-10.6.0.254