---
title: NAT 协议详解（网络层）
description: 解析 NAT 的地址转换与端口映射机制，结合 LAN/WAN 通信与转换表，理解家庭与企业网络的实践细节。
category: 计算机基础
tag:
  - 计算机网络
head:
  - - meta
    - name: keywords
      content: NAT,地址转换,端口映射,LAN,WAN,连接跟踪,DHCP
---

很多设备在家用网络、公司内网里使用的都是私有 IP 地址，比如 `192.168.x.x`、`10.x.x.x`。这些地址不能直接在公网中路由，但内网设备依然可以访问互联网。

这背后通常就有 NAT 在工作。NAT 会在内网地址和公网地址之间做转换，让多个内网设备共享一个或少量公网 IP 对外通信。

这篇文章主要回答几个问题：

1. NAT 主要解决什么问题？
2. NAT 转换表是如何记录内外网地址和端口映射的？
3. 内网主机访问公网时，源 IP 和端口会发生什么变化？
4. NAT 会带来哪些限制，比如外部主动访问内网主机为什么更麻烦？

## 应用场景

**NAT 协议（Network Address Translation）** 的应用场景如同它的名称——网络地址转换，应用于内部网到外部网的地址转换过程中。具体地说，在一个小的子网（局域网，Local Area Network，LAN）内，各主机使用的是同一个 LAN 下的 IP 地址，但在该 LAN 以外，在广域网（Wide Area Network，WAN）中，需要一个统一的 IP 地址来标识该 LAN 在整个 Internet 上的位置。

这个场景其实不难理解。随着一个个小型办公室、家庭办公室（Small Office, Home Office, SOHO）的出现，为了管理这些 SOHO，一个个子网被设计出来，从而在整个 Internet 中的主机数量将非常庞大。如果每个主机都有一个“绝对唯一”的 IP 地址，那么 IPv4 地址的表达能力可能很快达到上限（$2^{32}$）。因此，实际上，SOHO 子网中的 IP 地址是“相对的”，这在一定程度上也缓解了 IPv4 地址的分配压力。

SOHO 子网的“代理人”，也就是和外界的窗口，通常由路由器扮演。路由器的 LAN 一侧管理着一个小子网，而它的 WAN 接口才是真正参与到 Internet 中的接口，也就有一个“绝对唯一的地址”。NAT 协议，正是在 LAN 中的主机在与 LAN 外界通信时，起到了地址转换的关键作用。

## 细节

![NAT 将内网私有地址转换为公网地址的过程](https://oss.javaguide.cn/github/javaguide/cs-basics/network/nat-demo.png)

假设当前场景如上图。中间是一个路由器，它的右侧组织了一个 LAN，网络号为 `10.0.0/24`。LAN 侧接口的 IP 地址为 `10.0.0.4`，并且该子网内有至少三台主机，分别是 `10.0.0.1`、`10.0.0.2` 和 `10.0.0.3`。路由器的左侧连接的是 WAN，WAN 侧接口的 IP 地址为 `138.76.29.7`。

首先，针对以上信息，我们有如下事实需要说明：

1. 路由器右侧子网的网络地址为 `10.0.0.0/24`（网络前缀 24 位，主机号占 8 位），三台主机地址以及路由器的 LAN 侧接口地址，均由 DHCP 协议规定。而且，该 DHCP 运行在路由器内部（路由器自维护一个小 DHCP 服务器），从而为子网内提供 DHCP 服务。
2. 路由器的 WAN 侧接口地址同样由 DHCP 协议规定，但该地址是路由器从 ISP（网络服务提供商）处获得，也就是该 DHCP 通常运行在路由器所在区域的 DHCP 服务器上。

现在，路由器内部还运行着 NAT 协议，从而为 LAN-WAN 间通信提供地址转换服务。为此，一个很重要的结构是 **NAT 转换表**。为了说明 NAT 的运行细节，假设有以下请求发生：

1. 主机 `10.0.0.1` 向 IP 地址为 `128.119.40.186` 的 Web 服务器（端口 80）发送了 HTTP 请求（如请求页面）。此时，主机 `10.0.0.1` 将随机指派一个端口，如 `3345`，作为本次请求的源端口号，将该请求发送到路由器中（目的地址将是 `128.119.40.186`，但会先到达 `10.0.0.4`）。
2. `10.0.0.4` 即路由器的 LAN 接口收到 `10.0.0.1` 的请求。路由器将为该请求指派一个新的源端口号，如 `5001`，并将请求报文发送给 WAN 接口 `138.76.29.7`。同时，在 NAT 转换表中记录一条转换记录 **138.76.29.7:5001——10.0.0.1:3345**。
3. 请求报文到达 WAN 接口，继续向目的主机 `128.119.40.186` 发送。

之后，将会有如下响应发生：

1. 主机 `128.119.40.186` 收到请求，构造响应报文，并将其发送给目的地 `138.76.29.7:5001`。
2. 响应报文到达路由器的 WAN 接口。路由器查询 NAT 转换表，发现 `138.76.29.7:5001` 在转换表中有记录，从而将其目的地址和目的端口转换成为 `10.0.0.1:3345`，再发送到 `10.0.0.4` 上。
3. 被转换的响应报文到达路由器的 LAN 接口，继而被转发至目的地 `10.0.0.1`。

![LAN-WAN 间通信提供地址转换](https://oss.javaguide.cn/github/javaguide/cs-basics/network/nat-demo2.png)

🐛 修正（参见：[issue#2009](https://github.com/Snailclimb/JavaGuide/issues/2009)）：上图第四步的 Dest 值应该为 `10.0.0.1:3345` 而不是~~`138.76.29.7:5001`~~，这里笔误了。

## 划重点

针对以上过程，有以下几个重点需要强调：

1. 当请求报文到达路由器，并被指定了新端口号时，由于端口号有 16 位，因此，通常来说，一个路由器管理的 LAN 中的最大主机数 $≈65500$（$2^{16}$ 的地址空间），但通常 SOHO 子网内不会有如此多的主机数量。
2. 对于目的服务器来说，从来不知道“到底是哪个主机给我发送的请求”，它只知道是来自 `138.76.29.7:5001` 的路由器转发的请求。因此，可以说，**路由器在 WAN 和 LAN 之间起到了屏蔽作用**，所有内部主机发送到外部的报文，都具有同一个 IP 地址（不同的端口号），所有外部发送到内部的报文，也都只有一个目的地（不同端口号），是经过了 NAT 转换后，外部报文才得以正确地送达内部主机。
3. 在报文穿过路由器，发生 NAT 转换时，如果 LAN 主机 IP 已经在 NAT 转换表中注册过了，则不需要路由器新指派端口，而是直接按照转换记录穿过路由器。同理，外部报文发送至内部时也如此。

总结 NAT 协议的特点，有以下几点：

1. NAT 协议通过对 WAN 屏蔽 LAN，有效地缓解了 IPv4 地址分配压力。
2. LAN 主机 IP 地址的变更，无需通告 WAN。
3. WAN 的 ISP 变更接口地址时，无需通告 LAN 内主机。
4. LAN 主机对 WAN 不可见，不可直接寻址，可以保证一定程度的安全性。

然而，NAT 协议由于其独特性，存在着一些争议。比如，可能你已经注意到了，**NAT 协议在 LAN 以外，标识一个内部主机时，使用的是端口号，因为 IP 地址都是相同的**。这种将端口号作为主机寻址的行为，可能会引发一些误会。此外，路由器作为网络层的设备，修改了传输层的分组内容（修改了源 IP 地址和端口号），同样是不规范的行为。但是，尽管如此，NAT 协议作为 IPv4 时代的产物，极大地方便了一些本来棘手的问题，一直被沿用至今。

<!-- @include: @article-footer.snippet.md -->
