Linux 下的 IP 地址伪装

作者:Chris Kostick

现在似乎每个人都想上网,而且理由充分。有大量的信息可以获取,人们可以发送电子邮件,网页可以浏览,软件可以下载。除此之外,企业正在找到可接受的广告方式,在某些情况下,还销售商品。但是,随着上网热潮的兴起,人们发现互联网地址不像以前那样容易获得了。一些网络管理员在许多环境中都遇到了这种情况;他们没有足够的网络地址来满足需求。

一些管理员没有经历获取另一个或两个 C 类地址块的繁琐过程,而是将一组未注册的地址隐藏在网络地址转换 (NAT) 设备之后。互联网为这些“私有”地址做好了准备,并为此目的保留了地址块。RFC 1597 规定地址 10.0.0.0 到 10.255.255.255、172.16.0.0 到 172.31.255.255 以及 192.168.0.0 到 192.168.255.255 在这些情况下使用。

RFC 强烈建议,如果您作为网络管理员要使用私有地址,则应从给定的范围中选择地址。一个特别重要的原因是,如果数据包碰巧通过 NAT 时其原始 IP 地址完好无损,则互联网上的骨干路由器将不会转发该数据包。相反,如果您使用的是其他人的有效 IP 地址,则可能会发生混淆。

许多防火墙,尤其是那些基于应用代理网关的防火墙,由于其功能方式,自然会隐藏地址。Linux 也支持通过所谓的“IP 地址伪装”来隐藏地址也就不足为奇了。在 Linux 下设置地址伪装并不十分困难,但有一些细微之处需要指出。

准备工作

如果您运行的是 1.2.x 版本的内核,则需要获取内核补丁以支持地址伪装。该补丁可从 ftp://ftp.eves.com/pub/masq 获取,或者您可以从 www.indyramp.com/masq/ 下载您需要的一切。1.3.x 内核版本支持 IP 地址伪装。对于本文,我运行的是 1.3.56 版本,所有示例都基于此版本。对于 FTP 支持(稍后会提到),您需要至少拥有 1.3.37 版本的内核。对于 1.2.x(其中 x >= 4)内核有一个补丁来支持 FTP,但我尚未对其进行测试。Indyramp 提供的 masqplus-0.4 “jumbo” 补丁修复了一些错误,并为 1.2.13 内核添加了对 FTP、RealAudio 和分片的支持。

在配置内核以支持地址伪装时,重要的是还要对防火墙和转发支持选择“是”。以下是我用于配置内核的参数

Network firewalls (CONFIG_FIREWALL) [Y/n/?] y
Network aliasing (CONFIG_NET_ALIAS) [Y/n/?] y
TCP/IP networking (CONFIG_INET) [Y/n/?] y
IP: forwarding/gatewaying (CONFIG_IP_FORWARD) [Y/n/?] y
IP: multicasting (CONFIG_IP_MULTICAST) [Y/n/?] y
IP: firewalling (CONFIG_IP_FIREWALL) [Y/n/?] y
IP: accounting (CONFIG_IP_ACCT) [Y/n/?] y
IP: tunneling (CONFIG_NET_IPIP) [Y/m/n/?] y
eP: firewall packet logging (CONFIG_IP_FIREWALL_VERBOSE) [Y/n/?] y
IP: masquerading (ALPHA) (CONFIG_IP_MASQUERADE) [Y/n/?] y

我选择了其他与地址伪装没有直接关系的项,例如多播和隧道,但我喜欢玩得开心。

请注意,IP 地址伪装软件仍被认为是 Alpha 质量。这意味着可能仍然存在一些错误。基本功能已经存在,但 TCP、UDP 和 IP 的所有细微之处以及应用程序协议尚未经过彻底测试。此外,随着开发的进行,接口可能仍会发生变化。

为了操作地址伪装规则集,您将需要 ipfw 软件 1.3.6-BETA3 版本,或者您可以从 ftp.eves.com 获取预编译的二进制文件。那些将 Linux 用作过滤防火墙并且还使用 ipfwadm 的人应注意,该软件尚不支持 IP 地址伪装,因此 ipfw 是必需的。[新增:ipfwadm 2.0beta2,现在可从 ftp://ftp.xos.nl/pub/linux/ipfwadm/ 获取,适用于 Linux 1.3.66 及更高版本,确实 支持地址伪装。此外,由于接口更改,必须将最新版本的 ipfwadm 与最新版本的内核一起使用—ED]

应用规则

让我们首先定义我们尝试实现的目标,并了解 IP 地址伪装在环境中的作用。图 1 显示了示例所基于的网络。deathstar 是采用地址伪装以隐藏网络 192.168.1.0 的 Linux 机器。

地址伪装在我们的架构中很有用,因为它为我们节省了一些管理上的麻烦。我部门中的许多人都有家庭局域网,并且通过他们的 PPP 连接,他们可以使用他们的其他机器连接到部门实验室。我们可以轻松运行路由协议(如 RIP)来使实验室网络上的机器了解家庭局域网,但这需要就谁拥有什么网络地址进行一些协调。使用地址伪装(对我们来说)更容易。

要隐藏网络,我们可以发出命令

# ipfw a m all from 192.168.1.0/24 to 0.0.0.0/0

此规则表明我们想要为 所有 协议(在这种情况下是指 TCP 和 UDP)加一个 址伪装规则。我们隐藏的网络是 192.168.1.0,我们隐藏的是去往任何网络 (0.0.0.0/0) 的连接。/24 表示我们正在应用 24 位网络掩码,即 255.255.255.0。由于我们将网络指定为 192.168.1.0,因此 deathstar 将为网络上的所有主机进行地址伪装。这就是我们需要做的全部。

如果我只想让 deathstar 为 enterprise 进行地址伪装,那么我将输入

# ipfw a m all from 192.168.1.2/32 to 0.0.0.0/0

但是,“为...进行地址伪装”到底是什么意思?好吧,让我们检查一下受影响的文件和内核表,以了解典型的地址伪装连接。我们将以 telnet 为例。

让我们验证规则是否已设置。我们需要查看 /proc/net 目录中的 ip_forward 文件。我们可以使用 ipfw 来执行此操作

# ipfw -n list forward
Type    Proto       From           To     Ports
(masqueradeall  192.168.1.0/24  anywhere

这很好。一些管理员错误地在 /proc/net/ip_masquerade 文件中查找规则,当他们没有看到它时,就会感到困惑。

对于我们的示例,我已启动从 warbird 到 enterprise 的 telnet 会话。此外,在 mccoy 上,我正在使用 tcpdump 程序来监视 20.2.51.0 上的流量,并在 sparcbook 上监视 192.168.1.0 上的流量。我们现在可以查看 ip_masquerade 文件以检查正在发生的事情(参见 列表 1)。

让我们解码这些东西。首先,最早的数据包在底部。它是从 192.168.1.2 到 20.2.51.2 的 DNS 请求(因此是 UDP)。在这种情况下,mccoy 是 warbird 的 DNS 服务器。“Masq”列向我们显示了 deathstar 上用于地址伪装的端口。对于第一个 DNS 请求,它是端口 60000 (EA60)。在 DNS 解析之后,TCP 连接在 60000 以上的下一个可用端口 60001 上建立。图 2 说明了 TCP 打开之前事件序列的协议时间线。

即使协议时间线显示了数据包的真实遍历方式,发送和接收节点也对此一无所知。因此,他们将其称为地址伪装。从 warbird 的角度来看,流量将完全符合预期。也就是说,来自 enterprise 的数据包由 deathstar 重新打包,看起来好像来自 enterprise。列表 2 显示了 telnet 会话在 192.168.1.0 网络上的流量的 tcpdump 输出。

列表 3 显示了 telnet 会话期间 20.2.51.0 网络上的协议流量。请注意,信息源自 deathstar,而不是 warbird。(您可能会注意到的另一件事是我没有很好地保持时钟同步。)

另一个重要的方面是维护 TCP 同步序号。为了使地址伪装正常工作,deathstar 必须保持同步正确。由 warbird 生成的 TCP 序列号由 deathstar 转发,而不是生成新的序列号。

关于 /proc/net/ip_masquerade 文件内容的最后一些观察结果与最后四个字段有关。Init-seqDeltaPDelta 字段处理 FTP 数据传输(稍后详细介绍)发生时的 TCP 同步序号,最后一个字段是地址伪装条目的过期计时器。时间以百分之一秒为单位保存;TCP 的时间为 90000 或 15 分钟,UDP 的时间为 300000 或 5 分钟。只要在两个通信主机之间为地址伪装端口传递流量,计时器就会保持更新。关于过期计时器的一个小细节与 FTP 传输有关。FTP 使用两个连接:一个用于命令的控制连接和一个用于文件传输的数据连接。当数据连接用于数据移动时,控制连接将处于空闲状态。如果传输时间超过 15 分钟,则地址伪装主机将关闭控制连接。数据连接将完成,但如果您想获取更多文件,则必须重新连接。这由文件 /usr/include/linux/ip_fw.h 中的定义控制。

#define MASQUERADE_EXPIRE_TCP     15*60*HZ
#define MASQUERADE_EXPIRE_TCP_FIN  2*60*HZ
#define MASQUERADE_EXPIRE_UDP      5*60*HZ

文件 /usr/include/linux/ip_fw.h 中。六个小时(360 分钟)似乎是一个相对可以接受的超时值,但您可以根据需要更改它。

问题

并非所有协议都适用于 IP 地址伪装。ICMP 消息(例如 ping 使用的消息)将不会通过地址伪装主机。此外,将地址传递给接收主机的应用程序协议将无法工作。talk 程序就是一个例子。

不工作的应用程序的主要例外是 ftp。IP 地址伪装软件已编写为处理从内核版本 1.3.37 开始的文件传输。FTP 客户端在正常操作下,会将服务器应连接以进行传输的地址和端口号发送给服务器。由于 talk 失败的相同原因,这不应与地址伪装一起工作。但是,IP 地址伪装软件将拦截 FTP PORT 命令,并伪装成等待服务器连接到它的客户端主机。

最大的问题是最微妙的问题:IP 分片。分片在 Internet 协议中自动发生。IP 始终希望将数据报适应其传输的网络链路的帧大小。大多数数据链路定义了一个最大传输单元 (MTU),用于在一个帧内容纳信息。如果要发送出去的 IP 数据报无法全部放入帧的 MTU 大小中,则会被分片。

携带 TCP 段的 IP 数据报的结构类似于 图 3 中的“原始数据报”图示。分片后,新的数据报出现(也显示在 图 3 中)。需要注意的最重要方面是 TCP 标头的位置。对于分片,它仅出现在第一个分片中,而不出现在后续分片中。如果没有标头,则进行地址伪装的主机无法确定是否应转发分片。这同样适用于分片的 UDP 数据包。

对于 TCP,由于 TCP 的 MSS(最大段大小)协商,此问题在很大程度上得到了避免。这并不是说它不会发生,但大多数时候它不会发生。但是,UDP 更容易受到此类行为的影响。作为管理员,您唯一的解决方案是小心控制 SLIP 或 PPP 网络上的 MTU 大小。

X 应用程序(返回 X 服务器的连接);RealAudio(但有补丁可用);和 rlogin(rlogind 需要特权端口)也存在其他问题。

真实世界的问题

对地址伪装问题进行实际故障排除并不总是像理顺规则那么容易。IP 地址伪装邮件列表(请参阅 侧边栏)的一位订阅者提出了一个有趣的问题。它通过简单的分析、代码知识和一个好的十六进制编辑器得到了解决。

问题

Greg Priem 向 IP 地址伪装邮件列表发送了一条消息,描述了一个问题,即他的 telnet 会话会冻结。他隔离了一系列重现该问题的事件——他将从 Linux 框后面的机器登录到他的服务提供商的主机,然后输入 ls -l

分析

Greg 进行了一些初步分析,并发布了他的发现。他使用的网络如图 图 4 所示。telnet 来自 Mac 到 ISP 以及互联网上的其他主机。他注意到从 Mac 到 Linux Box 的 telnet 工作正常,以及从 Linux Box 到 ISP 的 telnet 也工作正常。

来自 tcpdump 的输出显示正在发生分片。我跟进了一条消息,指出可能存在问题,并要求 Greg 检查 Linux Box 的每个接口上的 MTU 大小。

我认为奇怪的是,telnet 会话中会发生分片,因为 telnet 使用 TCP。如前所述,当 TCP 打开连接时,MSS 协商应该消除分片。

使用 tcpdump(一个方便的程序)进行的进一步调试表明,ISP 分配的 MTU 为 212。为了尝试消除分片,Greg 还为 SLIP 链路分配了 212 的 MTU。在查看连接的 MSS 协商时,Greg 发现从 Linux 框到 ISP,MSS 设置为 172,从 Mac 到 Linux 框也是如此。但是,从 Mac 到 ISP 的连接显示的 MSS 为 536。

解决方案

鉴于这些信息,我能够推断出问题并提出适当的解决方案。

连接场景在 图 5 中给出。

需要注意的一件事是,当 Mac 与 MTU 小于它的直接链路连接时,它通告的 MSS 为 536。有 BSD 经验的人会记得这个数字,因为网络代码通过查看目的地是在本地 LAN 还是远程 LAN 来为 TCP 的协商选择 MSS 值。代码大致如下所示

if dest_net == local_net
then
        mss = (link MTU) - 40
else
        mss = 536
        /* determined by 576 - 40 */
fi

如果目的地位于远程网络上,它将自动将 MSS 设置为 536。这是一个很好的数字,因为 IP 的 RFC 规定互联网互连的默认数据报大小为 576,这意味着每个设备都应该能够处理它而无需进一步分片。减去 40 是为了留出 IP 和 TCP 标头的空间。

第二个需要注意的事情是 Linux 框转发了 MSS 通告。人们可能会认为,由于连接是从 Linux 框作为地址伪装的结果建立的,因此 MSS 值将基于来自 Linux 框的网络链路,而不是来自发送主机的原始值。

顺便说一句,有一个无法解释的实例,即与 ISP 主机建立连接,而 ISP 发回的 MSS 为 1460,如图 5 底部所示。这很奇怪,因为它也连接到 MTU 为 212 的 PPP 链路。这可能归因于 ISP 网络方面缺乏知识。

由于双方都使用了大于任一链路的 MTU 的 MSS 值,因此即使对于 TCP 连接,也必然会发生分片。在正常情况下,这无关紧要,但它确实会使地址伪装感到困惑。

简单的解决方案是让 ISP 支持至少 576 的 MTU,并让 Greg 将 SLIP 链路设置为 576 或更大的 MTU。因此,不会发生分片。

Greg 通过电子邮件发送给他的 ISP 并等待回复。当没有收到回复时,他变得不耐烦了。由于他没有 Mac 上 TCP 代码的源代码,因此查看它的唯一方法是使用十六进制编辑器。他开始四处摸索,看看是否可以找到 BSD 类型的代码,该代码在其中决定了 MSS,并且果然,他找到了它。他将硬编码值 536 更改为 172(即 212-40),重新启动了他的 Mac,瞧,它奏效了——不再分片!(顺便说一句,ISP 后来确实更改了 MTU 大小。)他的方法比我本来的做法更大胆一些,但这似乎是 Linux 用户的本性,如果他们无法重新编译某些东西,就会修补现有的二进制文件。

结论

IP 地址伪装是一项有趣的技术,但更重要的是,它为许多互联网环境提供了一个非常有用的功能。它适用于 telnet、http 和 ftp 等常用服务,但并非支持所有服务。ICMP 消息、talk、远程 X 应用程序和 rlogin 不适用于地址伪装。幸运的是,该软件仍处于 Alpha 版本,并且正在进行更多开发。

Chris Kostick (ckostick@csc.com) 是 Computer Sciences Corporation 网络安全部门的高级计算机科学家。他喜欢使用 Linux,但认为自己是一个后来者,因为他从内核版本 1.1.18 开始使用 Linux。就计算机而言,他不确定调试 TCP/IP 问题还是射击 DOS 机器更有趣

加载 Disqus 评论