《TCP/IP 详解 卷1:协议》第7章:防火墙和网络地址转换
防火墙
随着终端系统漏洞频发,保障其安全的压力越来越大。网络安全的重点逐渐转向如何通过防火墙来过滤网络流量,从而在进入终端系统前进行安全控制。
当前主要的防火墙类型包括:
- 包过滤防火墙(Packet-Filter Firewall)
- 代理防火墙(Proxy Firewall)
这两类防火墙的主要区别在于工作层级:
- 包过滤防火墙工作在网络层或传输层
- 代理防火墙工作在应用层
包过滤防火墙
包过滤防火墙通常是配置在网络边缘的路由器,通过设定的过滤规则(filters) 或 访问控制列表(ACL) 来决定哪些数据包允许通过,哪些应被丢弃。
常见过滤条件包括:源/目的 IP 地址,协议类型(如 TCP、UDP、ICMP)
,源/目的端口号,TCP 标志位(如 SYN、ACK)。
- 无状态防火墙:每个数据包独立处理,无法识别连接关系
- 有状态防火墙:能够跟踪连接状态,识别属于同一连接的数据包,处理 TCP 流或 IP 分片更可靠
但是其不检查应用层内容,难以检测复杂攻击。无状态防火墙容易被 IP 分片等机制绕过。
代理防火墙
代理防火墙并非简单路由器,而是具备多个网络接口的应用层网关(ALG),能够在应用层终止和中继 TCP/UDP 连接。客户端并不直接访问目标服务器,而是将请求发送给防火墙,由防火墙代理转发。
防火墙不启用 IP 转发,客户端需要特殊配置,明确代理地址和协议支持。内部接口通常使用私有 IP,外部接口使用公网 IP。
优点:
- 安全性更高,可深入检查应用层协议
- 易于实现访问控制和内容过滤
- 支持缓存(Web 代理)和内容加速
缺点:
- 灵活性差,每种协议都需实现代理模块
- 部署复杂,新增服务需要额外配置
- 对应用程序兼容性要求高(如需支持 SOCKS)
HTTP 代理防火墙(Web Proxy) :支持 HTTP 和 HTTPS,可作为 Web 缓存,减少带宽消耗,支持内容过滤(如黑名单)。
SOCKS 防火墙:支持多种应用层协议(不止 Web),应用程序需支持 SOCKS 协议,并进行配置。
网络地址转换
NAT(Network Address Translation)是一种机制,允许在互联网的不同位置重复使用相同的私有 IP 地址集合。其本质是通过重写数据包中的地址信息,实现内部私有地址与外部公共地址之间的映射。
NAT 的常见使用场景:一个站点仅有一个公网 IP,却有多台内部主机需要访问互联网。所有出入流量统一经过一个 NAT 设备,从而将内部网络与公网隔离。内部主机使用私有地址(如 192.168.x.x、10.x.x.x),通过 NAT 访问互联网。
NAT 的缺点:
-
服务提供困难:私有地址的主机无法被公网直接访问,部署 Web 或 FTP 服务需做额外配置(如端口映射)。
-
会话依赖:所有双向通信的数据包必须通过同一个 NAT 设备,否则状态无法同步,连接失败。
-
协议干扰:某些协议(如 FTP、SIP)会在应用层载荷中嵌入 IP 地址,NAT 无法自动识别,需特殊处理(如 ALG)。
-
违反互联网设计哲学:传统互联网设计理念是“哑中间,智能边缘”,而 NAT 属于“智能中间”,破坏了端到端模型。
-
状态依赖性高:NAT 设备必须记录每个连接的状态,对性能和稳定性提出更高要求。
-
校验和处理复杂:改写 IP 后,还必须更新传输层(TCP/UDP)的校验和(涉及伪头部字段)。
许多 NAT 同时具备包过滤功能(如只允许内部发起的连接)。动态状态表决定是否允许一个数据包通过(如判断是否是已建立连接的响应包)。
传统的NAT:基本NAT和NAPT
基本 NAT:仅重写 IP 地址,需要多个公网地址,节省 IP 效果不佳。
NAPT(网络地址端口转换):重写 IP 和端口,通过端口号区分内部主机,支持大规模主机共享少量公网 IP,是最常用的 NAT 形式。
NAT 与 TCP
TCP 连接通过两端的 IP 和端口唯一标识。典型的三次握手(SYN → SYN-ACK → ACK)必须完整穿越 NAT。NAT 在检测到出站的 SYN 包后,会创建一个地址与端口映射,用于将私有地址转换为公网地址并转发数据包。响应到达时,NAT 再据此进行反向转换,称为“端口保留”。
为管理连接状态,NAT 会设定会话计时器。在连接空闲时,可能通过发送探测包确认连接是否仍存活。若接收 RST 包或探测无响应,NAT 会清除会话。RFC5382 要求 NAT 至少保留正常连接状态 2 小时 4 分钟,半开连接至少保留 4 分钟。
点对点应用中常使用“同时发起连接”,即双方几乎同时发送 SYN 包。许多 NAT 无法处理此类同步建立连接的场景,因此 RFC 要求 NAT 必须支持此类合法的 TCP 报文交换,并在外部 SYN 到达后等待至少 6 秒以便内侧主机发起连接。
NAT 不仅转换地址和端口,还必须理解 TCP 状态,保障连接的建立、维持与终止过程的正确性。
NAT 与 UDP
- UDP 无连接,缺乏类似 TCP 的 SYN/FIN 标志,NAT 难以识别连接起止。
- NAT 使用 映射计时器(mapping timer) 清理不活跃状态。[RFC4787] 要求最小超时时间为 2 分钟,推荐值为 5 分钟。
- NAT 的映射刷新方式:
- 对外刷新行为(outbound refresh):内部发送数据时刷新,强制要求。
- 对内刷新行为(inbound refresh):外部发送数据时刷新,可选。
- 分片问题:UDP/IP 分片中,仅首片包含端口信息,后续分片无法供 NAT/NAPT 正确处理。同样影响 TCP 和 ICMP 的分片处理。
NAT 与 DCCP/SCTP
- DCCP(Datagram Congestion Control Protocol):为数据报提供拥塞控制服务。
- SCTP(Stream Control Transmission Protocol):
- 提供多宿主可靠传输。
- NAT 处理需考虑地址绑定与路径管理。
NAT 与 ICMP
- ICMP 包括两类报文:
- 错误报文(如端口不可达):通常携带原始数据包副本,NAT 需进行 ICMP fix-up,即修正嵌入数据包中的地址。
- 信息报文(如 Echo 请求):包含 查询 ID 字段,NAT 可识别请求,设置计时器等待响应。
- [RFC5508] 明确了 ICMP 报文在 NAT 中的处理行为。
NAT 与 隧道数据包
- 隧道协议(如 PPTP 中的 GRE)通过 NAT 时,需要同时处理封装头部。
- GRE 中的 Call-ID 可能与其他连接冲突,需 NAT 正确映射。
- 多层封装增加 NAT 解析和转换的复杂度。
NAT 与 组播
- 虽然主要用于单播,NAT 也可以配置支持 组播。
- [RFC5135] 定义了 NAT 支持组播的行为要求。
- 支持组播需启用 IGMP 代理(见 [RFC4605])。
- NAT 内部主机发送组播时:
- 目的地址和端口不修改;
- 源地址和端口可能会根据单播 UDP 的规则进行 NAT 转换。
NAT 与 IPv6
- 在 IPv6 中使用 NAT 是有争议的(见 [RFC5902]):
- IPv6 地址充足,无需地址节省;
- NAT 增加协议复杂度;
- 替代方案为 本地网络保护(LNP)(见 [RFC4864]),可提供防火墙、隐私保护、拓扑隐藏。
- IPv6 中的替代 NAT 机制:NPTv6(Network Prefix Translation v6):
- [RFC6296] 提出,基于前缀算法将地址从一个 IPv6 范围映射到另一个;
- 保留 TCP/UDP 校验和,不需访问端口信息;
- 无需维护状态,降低复杂性;
- 不提供包过滤功能,需额外防火墙支持;
- 应用需借助 ALG 或 NAT 穿越机制访问外部资源。
地址和端口转换行为
NAT 的地址和端口映射行为多种多样,IETF 的 BEHAVE 工作组提出了标准化建议,以促进互操作性。
- 表示形式:
X:x → Y:y
- X:x:内部私有地址与端口(例如来自 RFC1918)
- Y:y:远程公网地址与端口
- 映射后 NAT 使用:
X':x'
- 若连接多个外部主机(如 Y1:y1 → Y2:y2),可能使用相同的 X’,端口 x’ 若复用即为 端口保留(port preservation)
- 若端口冲突,NAT 必须进行端口映射调整
过滤行为
当NAT为一个TCP连接、UDP关联或各种形式的ICMP流量创建一个绑定,不仅要创建地址和端口映射,作为一个防火墙,还必须确定返回流量的过滤行为,这是非常常见的情况。
行为名称 | 过滤行为 | 说明 |
---|---|---|
独立于端点 | 只要 X1':x1' 存在,允许任意对端返回 | 最宽松 |
依赖于地址 | 仅允许曾通信的 IP 地址返回 | 中等严格 |
依赖于地址和端口 | 仅允许曾通信的 IP+端口 返回 | 最严格 |
位于NAT之后的服务器
NAT 的一大局限是 外部主机无法直接访问 NAT 内部的服务器,主要原因:
- 路由限制:公网主机无法路由到私有地址(如 10.0.0.3)。
- NAT控制:NAT 默认不转发目的为私有地址的入站流量。
为解决上述问题,端口转发(又称端口映射)被引入。其将 NAT 的某个外部端口映射到内部主机的地址和端口,允许公网用户通过 NAT 的公网地址访问 NAT 内部的私有服务。通常需要手动配置(静态映射)。
发夹和NAT环回
当一台主机希望通过 NAT 映射的公网地址 访问 同一 NAT 私网内的另一主机时,就需要 NAT 支持发夹或环回功能。
假设 NAT 内部有两台主机:
- X1 想连接到 X2
- X1 只知道 X2 的外部地址信息(X2′:x2′),而非私网地址
此时,X1 向 X2′:x2′ 发起连接请求,NAT 必须将该连接 “转回来” 给内部的 X2:x2 —— 这就是 发夹转发。
为完成连接,NAT 必须:
- 识别目标地址 属于 NAT 自己的映射。
- 将请求回转 到 NAT 私有网络中的对应主机(X2:x2)。
- 决定源地址使用何者:
- X1′:x1′(外部映射地址) → 推荐
- X1:x1(私网地址) → 不推荐
NAT穿越
传统在 NAT 设备中嵌入 ALG 或 NAT 编辑器的方式非常复杂,因此很多应用程序开始 主动尝试实现 NAT 穿越,以确保位于 NAT 后的终端能够直接通信。
NAT 穿越的基本目标
- 获取自身的外部地址与端口映射
- 协调位于不同 NAT 后的客户端之间建立连接
- 避免使用中继服务器转发数据,降低负载与延迟
如果实在无法建立直连,一种替代方法是:服务器作为 数据中继 —— 不推荐,但有时不得不为之(如 TURN)。
针孔和打孔
当一个客户端向外部建立连接时,NAT 会动态地为这条流量创建一个映射(IP + 端口对)。映射是临时存在,并且通常只允许该对端返回流量。 这个映射就像在 NAT 上开了一个“小洞”。
基于针孔映射,让两个位于 NAT 后的客户端尝试彼此通信。一般由一个 协调服务器辅助完成地址信息交换。
UDP 打孔流程(也适用于部分 TCP 场景):
- 客户端 A 向服务器 S1 建立连接,NAT N1 创建映射。
- 客户端 B 向 S1 建立连接,NAT N2/N3 创建映射。
- S1 记录 A 与 B 的外部地址(如 A → 192.0.2.201,B → 203.0.113.100)。
- S1 将彼此的地址告知双方。
- A 与 B 尝试通过对方的外部地址直接通信。
- 若 NAT 行为允许,则打孔成功。
STUN协议
STUN 是一种轻量级的客户端/服务器协议,主要用于协助客户端识别其位于 NAT 后的 公网 IP 和端口,并维持 NAT 映射。
⚠️ STUN 不是万能方案,在复杂 NAT 或对称 NAT 下可能失效。常与 ICE、TURN 联合使用。
每个 STUN 报文包含:
- 报文类型(前2位固定为0)
- 报文长度(不包含头部的20字节)
- 魔术 cookie(固定值
0x2112A442
) - 96 位事务 ID
- 零个或多个 TLV 格式的属性
假设客户端位于 NAT 后,地址为 Xx
,它通过 STUN 服务器 Y1:y1
请求其公网地址:
- 服务器在响应中添加
MAPPED-ADDRESS
或XOR-MAPPED-ADDRESS
属性 - 客户端据此获得自己的公网地址
X1':x1'
(即“反射地址”)
为什么使用 XOR-MAPPED-ADDRESS?
- 避免 ALG(Application-Level Gateway)劫持或修改 STUN 报文中的 IP 地址
- XOR 加密使 ALG 无法识别或干预地址字段,提高协议鲁棒性
利用中继NAT穿越
TURN(Traversal Using Relays around NAT) 是一种用于在 NAT 后主机之间通信的最终手段。 使用中继服务器转发数据,即便双方都处于不同 NAT 后也能通信。
工作机制:
- 位于 NAT 后的客户端连接公网 TURN 服务器。
- TURN 服务器为客户端分配:
- 中继传输地址(Relayed Transport Address)
- 反射地址(Reflexive Address)
- 客户端和 peer 使用 TURN 地址交换信息,但 TURN 本身不负责地址交换(需借助 ICE 等机制)。
分配与连接控制:
- 客户端使用
Allocate
方法申请中继地址并通过 STUN 长期认证机制验证身份。 - 分配的生命周期默认 10 分钟,可通过
LIFETIME
属性修改。 - 客户端使用
Refresh
方法刷新分配状态;生命周期为0
时释放分配。 - TURN 的5元组标识:客户端 IP/端口 + 服务器 IP/端口 + 协议
客户端使用 CreatePermission
方法创建允许通信的 peer 地址权限。权限默认有效期为 5 分钟,未刷新则自动删除。TURN 服务器只转发来源地址匹配的 peer 的数据。
方式一:Send / Data(STUN 封装):使用 STUN 报文传输数据。开销较大,适用于通用场景。
方式二:Channel / ChannelBind(隧道):建立轻量隧道(channel),使用 4 字节报头。延迟更低、效率更高,适用于实时通信(如 VoIP)。每个分配最多支持 16K 个隧道。
TURN通过6种方法、9个属性以及6个错误响应代码增强STUN。这些大致可以分为支持建立和维护分配、认证以及操作隧道。6种方法和它们的方法号如下:分配(Allocate)(3),刷新(Refresh)(4),发送(Send)(6),数据(Data)(7),创建权限(CreatePermission)(8),隧道绑定(ChannelBind)(9)。前两种方法用于建立并保持分配存活。Send和Data使用STUN报文封装从客户端发送到服务器的数据,反之亦然。
GreatePermission用于创建或刷新一个权限,ChannelBind通过一个16位的隧道号与一个特定的对等客户端相关联。错误报文表明与TURN功能相关的问题,如认证失败或资源耗尽(例如,隧道数)。
交互连接建立
ICE(RFC 5245) 是一种通用机制,用于帮助处于 NAT 后的 UDP 应用主机建立连接。是一种启发式的 NAT 穿越方法,统筹利用 STUN 和 TURN 协议。最初用于 SIP/SDP 通信中的 媒体流穿越,但也被扩展到其他应用(如 XMPP)。可扩展支持基于 TCP 的应用连接建立。
在存在多个候选地址(主机地址、反射地址、中继地址)时: 优先尝试直连(主机、反射), 保底使用中继(TURN)。通过“连接性检查(connectivity checks)”找到最优的传输路径。最终形成一个可达的“候选对(candidate pair)”。
ICE 关键流程:
-
候选地址发现
- 类型包括:
- 主机地址(host)
- 反射地址(server reflexive)
- 中继地址(relayed)
- 对等反射地址(peer reflexive)
- 使用 STUN 和 TURN 协议获取各类地址。
- 类型包括:
-
优先级分配
- 启发式算法将低延迟、直连路径排在前面。
- 本地代理生成本地候选地址优先级列表。
-
交换 SDP
- 使用 SDP(如 SIP 协议)将候选地址和优先级发送给对等代理。
-
候选对生成
- 双方各自形成候选地址后,进行两两组合,得到候选对集合(candidate pair set)。
-
连接性检查
- 通过 STUN 绑定请求(带地址信息)验证每对地址是否可达。
- 响应成功的对成为“可用的候选对”。
-
选定候选对
- 控制代理(controlling agent) 负责最终选择。
- 可选择:
- 常规选定(regular nomination):测试所有候选后决定。
- 积极选定(aggressive nomination):一旦找到可用对就立即确定。
-
路径优化与地址更新
- 如果在检查过程中发现新的 NAT 路径,会生成新的 peer reflexive candidate 并更新列表。
所有检查均通过 STUN 绑定请求/响应 完成。使用 STUN 的:短期认证机制 保证请求可信。FINGERPRINT 属性 校验消息完整性。STUN 响应中可暴露新 NAT 地址(生成对等反射候选)。
配置包过滤防火墙和NAT
NAT 通常配置较少,除非使用端口转发(Port Forwarding)。
防火墙 需要明确指定匹配条件和对应动作,配置工作较复杂。
防火墙规则
防火墙通过 ACL 来配置规则,控制哪些流量被允许或阻止。每条规则包含:
- 匹配条件:IP 地址、端口、协议、ICMP 类型、方向等。
- 动作(Action):如
ACCEPT
,DROP
,LOG
,QUEUE
等。
不同方向(流入/流出)可以使用不同的规则集。有的系统可在 路由决策之前/之后 应用规则。匹配顺序通常是:自上而下遍历规则,匹配到第一个规则后立即执行对应动作。
iptables
是 Linux 系统中常见的防火墙配置工具。基于 Netfilter 内核框架。支持无状态(stateless)与有状态(stateful)过滤。
-
表(table):
filter
:基础包过滤nat
:NAT 操作mangle
:对数据包字段进行修改
-
链(chain):
filter
表默认链:INPUT
:目标是本机的包FORWARD
:转发通过的包OUTPUT
:本机发送的包
nat
表默认链:PREROUTING
、OUTPUT
、POSTROUTING
mangle
表链:支持额外字段修改
-P INPUT DROP ,默认丢弃所有进入的包(除非另有规则)
-i lo -j ACCEPT ,接受本地 loopback 通信
DHCP 规则 ,接受来自 0.0.0.0:67 到 255.255.255.255:68 的 DHCP 广播
TCP Flags 过滤 ,过滤掉标志字段全为0的可疑 TCP 报文(通常为扫描/异常流量)
NAT规则
在多数简单的家用路由器中,NAT 与防火墙功能通常集成。
- Windows 系统中 NAT 被称为 ICS(Internet Connection Sharing)。
- Linux 系统中称为 IP 伪装(IP Masquerading)。
启用 ICS 后系统会自动完成以下配置:
- 将运行 ICS 的主机设置为:
192.168.0.1
- 启动 DHCP 服务器:为其他主机分配地址(默认网段:
192.168.0.0/24
) - 启动 DNS 代理服务,ICS 主机作为网关和 DNS