Linux 高级数据包数据测试

作者:Wesley Erhart

当您在 Linux Journal 网站上思考复杂的在线文章时,您的 56K 调制解调器在做什么?什么都没做。在您连接到互联网的大部分时间里,您的调制解调器都处于空闲状态。但是,您的电话呼叫至少占用了电话网络中的一条专用电路。此电路带宽的未使用容量和潜在收入永远丢失了。

电话公司运营商和制造商认识到,通过切换固定容量电路来形成数据路径是低效的。交互式数据通信以突发形式传输。您下载一个网页,然后随意地转到下一个网页,而不用担心您占用的电路带宽。

同样的问题也存在于今天的蜂窝系统中。大多数先进的第二代数字蜂窝系统在进行数据连接时都会预留一个用于语音传输的信道。这种稀缺且浪费的带宽是从世界各国政府购买或租赁的,对于客户阅读网页来说,这是一种非常昂贵的损失。

词汇表

如果您知道您口袋里那部时尚、光滑的手机下面发生了什么,您会感到惊讶。您会惊叹于传递一个简单的拨号音需要付出多少努力——更不用说在您从意大利边境进入法国边境时,同时从奶奶那里转发的电话会议您的叔叔,同时下载最新的 Linux 内核了。

GSM

Advanced Packet Data Testing with Linux

图 1. GSM 射频信道

如果您从意大利旅行到法国,您的手机很可能是一部全球移动通信系统 (GSM) 蜂窝电话。GSM 是全球第二代数字蜂窝标准。GSM 提供许多语音和短消息服务,例如呼叫线路 ID、呼叫阻止、呼叫转移和费用建议。如图 1 所示,GSM 的射频 (RF) 接口由 200KHz 带宽载波组成,分为八个时隙。一个时隙是一个信道,可以以 13Kbps 的速率承载数字压缩语音。在数据呼叫中,同一时隙可以承载高达 14.4Kbps 的电路交换数据。

GSM 标准机构认识到电路交换数据固有的低效率,并在 GSM 标准中添加了一项名为通用分组无线服务 (GPRS) 的服务。GPRS 是 GSM 网络的包覆层。对于承载和传递数据,分组交换技术的效率大大超过电路交换技术。

在分组交换蜂窝网络中,移动电话与小区站点范围内的其他移动电话共享 RF 数据包信道。GPRS 移动电话不是为整个会话在基站载波之一上预留特定的时隙或信道,而是使用一个或多个数据包信道以突发方式发送或接收数据。一旦移动电话完成带宽使用,它就会释放数据包信道,使它们可供其他移动电话使用。GPRS 移动电话的峰值数据吞吐量达到 120Kbps,而电路交换 GSM 移动电话的数据连接的最大吞吐量为 14.4Kbps。

Nortel Networks 致力于提供质量最高的集成语音和数据解决方案。随着我们引入 GPRS 等新技术,GPRS 开发人员的挑战也随之而来。开发人员如何验证他刚刚完成的协议层的接口是否正常工作?当还没有使用该协议的移动电话存在时,开发人员如何完成端到端呼叫?本文作者开发了测试系统,使 GSM GPRS 开发人员能够验证我们的 GPRS 产品是否以高水平的质量和可靠性运行。

GPRS

Advanced Packet Data Testing with Linux

图 2. 简单 GPRS 网络系统架构

GPRS 系统由多个网络节点组成,包括移动电话、基站系统、服务 GPRS 服务节点 (SGSN) 和网关 GPRS 服务节点 (GGSN)。这些节点如图 2 所示。当移动电话激活 Internet 协议 (IP) 数据包数据会话时,基站会通知其 SGSN。SGSN 使用 GGSN 激活会话,称为分组数据协议 (PDP) 上下文。如有必要,GGSN 会为移动电话的上下文分配一个动态 IP 地址。当 GGSN 确认上下文创建成功时,将在 SGSN 和 GGSN 之间创建使用 GPRS 隧道协议 (GTP) 的 IP 隧道。隧道 ID (TID) 在 GGSN 和 SGSN 都与该移动电话的上下文相关联。

Advanced Packet Data Testing with Linux

图 3. GPRS 传输平面

跟踪通过 GPRS 节点的移动电话发起的 IP 数据包(忽略 RF 接入细节)揭示了一些测试挑战。当移动电话发送 IP 数据时,数据包由子网相关会聚协议 (SNDCP) 压缩和分段,并向下发送到逻辑链路控制 (LLC) 层,遍历图 3 所示的堆栈。移动电话的 LLC 层确保数据包按顺序到达,并重新发送 SGSN 未确认的任何数据包。LLC 层通过 RF 堆栈的无线电特定部分、通过 RF 链路将数据包发送到基站和 SGSN。在 SGSN,对等 LLC 层确认接收到的数据包,并撤销 SNDCP 压缩和分段。

如图 3 所示,生成的 IP 数据包被封装在 GTP UDP/IP 消息中,消息头包含移动电话的上下文 TID。SGSN 将 GTP 数据包发送到 GGSN 的 GTP 端口。GGSN 提取 IP 数据包并将其放置在 Gi 接口上。IP 的 Gi 接口示例是 Internet。对于移动电话绑定数据包,以相反方向重复类似的过程。

自动化测试

Advanced Packet Data Testing with Linux

图 4. GAP 系统架构

我们典型的自动化测试解决方案方法是在每个要测试的接口上放置一个测试服务器,如图 4 所示。然后,GSM 开发人员使用“测试服务”应用程序编程接口 (API) 编写测试用例客户端,以通过测试服务器发送和接收消息。为了响应这些测试用例请求,服务器对被测系统 (SUT) 执行操作。在基于消息的协议中,服务器通常将测试用例的消息发送到 SUT。当从 SUT 接收到消息时,服务器会识别消息所属的测试用例客户端,并将消息路由和转发到测试用例。

这种测试范例是由 Nortel Networks 的各个小组共同开发的,并被命名为测试工具框架 (TTF)。我们的自动化团队采用了 TTF 范例,并在其基础上构建了 GSM 自动化平台 (GAP)。虽然最初是为 HP-UX 和 VxWorks 编写的,但 TTF 和 GAP 已移植到 Linux 以实现更大的灵活性。

当自动化组编写协议栈时,此系统运行良好。但是,GPRS 系统需要整个 IP 堆栈,包括 TELNET、FTP 和 HTTP 等应用程序。我们很快意识到,如果不重写 IP、UDP、TCP 和应用程序堆栈,我们将不可能生成真正的 TCP/IP 流量。

Linux 的开放和不断发展的 IPv4 堆栈非常有用。在测试协议时,我们需要跳出规范,不仅要测试合规性,还要测试错误场景。例如,如果路径最大传输单元设置为 1499,我们将需要发送一个 1500 字节的数据包,看看会发生什么。Linux 的开源内核和网络代码使这成为可能。

Advanced Packet Data Testing with Linux

图 5. 移动应用程序数据路径

在图 5 所示的布置中,在 Linux 测试用例机器上运行的测试用例使用 GPRS 信令路径建立隧道。建立隧道后,测试用例机器会为测试用例的移动 IP 创建 IP 别名。测试用例机器在用户空间 Gb LAN 和 Gi LAN 上是多宿主的。测试用例使用 IP 应用程序或套接字通过 Gb 测试服务器到 SGSN,通过 Gn 测试服务器,到 Gi LAN,并可能到测试用例机器的 Gi 接口生成真实的 IP 流量。

测试 Gn 接口

要回答的第一个问题是如何重用 Linux TCP/UDP/IP 堆栈。在我们的 GPRS 测试系统中,Gn 测试服务器在 UDP 套接字上接收 GTP 隧道数据包,从信封中取出数据包,并将其发送到另一个 LAN(即 Internet)上。在另一个方向上,Gn 测试服务器充当移动电话的网关,从 Internet 接收数据包,将其封装到隧道数据包中,并将其发送到 SGSN 的 GTP UDP 端口。

在 Linux 系统中,内核将数据包从 Gi LAN 以太网卡传输到封装的传出 UDP 消息。起初,我们集思广益,想出在内核空间中使用 UDP 套接字的方法;但是,我们最终决定采用不同的方法。

Advanced Packet Data Testing with Linux

图 6. USN 设备和路由配置

受 Alessandro Rubini 的著作 Linux 设备驱动程序 (请参阅资源 3)的启发,我们勾勒出一个双设备驱动程序。设备驱动程序的一侧将显示为 Linux 内核的以太网卡,而另一侧将是字符设备,如图 6 所示。应用程序可以通过从字符设备侧读取来读取从该设备的以太网侧发送的数据包。同样,应用程序可以通过写入字符文件将数据包发送到设备的以太网侧。我们称此设备为用户空间网络 (USN) 设备。实际上,USN 设备允许内核从打开 USN 字符设备文件的任何用户空间应用程序发送和接收数据包。

Gn 测试服务器所在的 Linux 机器充当移动电话 IP 子网的网关。路由表配置为将移动电话绑定 IP 数据包路由到 USN 设备。当内核将数据包发送到此设备时,我们的用户空间守护程序 (gtpd) 会触发并从 USN 字符设备文件(例如,/dev/usn0)读取数据包。然后,我们的守护程序将它们放入封装的 GTP 消息中,并将消息发送到 SGSN 的 GTP 端口。在开始此开发项目之前,我们花了很多时间欣赏我们的才华。

但是,随着我们的驱动程序开发继续进入我们认为是未知的领域,当我们发现 2.0 内核的 netlink 设备(请参阅资源 4)时,我们开始注意到 Alan Cox 的足迹。netlink 设备正是我们设备驱动程序的一半。它提供了一个字符设备和 shell,用于处理 sk_buff 结构。此时,我们所要做的就是编写驱动程序的以太网设备一半。在开玩笑说我们一半的工作已经完成的同时,我们想知道我们的想法有多么原创。

在我们完成设备驱动程序前后,我们获得了 Linux 2.2 内核。我们将其加载到我们的一台开发机器上,并发现了 Alan Cox 的 ethertap 设备。ethertap 设备正是我们的 USN 设备。它在隧道和数据包操作方面具有有趣的潜力。您的路由器是否只允许 HTTP 数据包通过?ethertap 设备可用于通过您的用户空间应用程序路由 Quake 数据包,该应用程序将它们封装到看起来无害的 HTTP 数据包中。

由于在新内核上认证 TTF 和 GAP 需要时间,并且可能为了支撑我们受挫的自尊心,我们决定继续使用我们的 USN 设备,而不是迁移到 Linux 2.2 内核和 ethertap 设备。

此时,Gn 测试服务器的工作进展顺利,我们开始专注于拼图的其他部分。

测试 Gb 接口

在移动性世界中,数据传输通常是危险的。Gb 协议栈(参见图 3)旨在支持多种网络寻址方案(例如,IP 或 X.25)的可靠数据传输,为分组数据网络中无数的应用程序提供服务。

拼图的另一大块是开发一个测试服务器和测试服务来测试 Gb 协议栈。这项开发工作大约在 GGSN 测试服务器开发工作的中途开始。自然地,Linux 是我们的主要开发环境,我们计划再次重用 USN 设备和 Linux UDP/TCP/IP 堆栈。但是,这一次它们将用于通过模拟虚拟移动电话、小区和基站来测试 Gb 堆栈。

Gb 测试接口的服务器部分是一个分布式应用程序,由两个主要组件组成。一个组件位于 VME 机箱中装有的 PowerPC 单板计算机 (SBC) 上,运行商业 RTOS。此组件驱动在 PCI(外围组件互连)夹层卡上运行的帧中继接口,该夹层卡支持多个 T1 或 E1 载波。第二个组件执行资源管理,代理帧中继服务,并实现 Gb 协议栈的 BSSGP(基站系统 GPRS 协议)和 NS(网络服务)层。由于 TTF 的多样性,此组件可以在运行 Linux 2.0 的 PC 或运行 RTOS 的 PowerPC SBC 目标上运行。

Advanced Packet Data Testing with Linux

图 7. Gb 测试系统

在图 7 所示的测试用例中,开发人员使用 Gb 测试服务创建移动电话、小区和基站的实例,所有这些实例都充当 Gb 测试服务器的客户端。这些测试服务通过为每个虚拟移动电话实现 LLC、SNDCP 和 GMM(GPRS 移动性管理)Gb 协议层来完成 Gb 堆栈。

预留移动电话、小区和基站后,开发人员可以在其测试用例中启动模拟呼叫。一旦呼叫建立并且 PDP 上下文建立,开发人员就可以使用基本的 UDP 或 TCP 套接字发送和接收数据。数据由 Linux UDP/TCP/IP 堆栈封装,并路由到 USN 设备。从那里,测试服务从 USN 设备检索数据,将其传递到 Gb SNDCP、虚拟移动电话的 LLC 层,然后传递到 Gb 测试服务器。在服务器上,数据通过 BSSGP 和 NS 协议层,然后通过帧中继传输到 SGSN。此过程在接收方向上反转。

所有这些都非常巧妙,但是一个更大的问题迫在眉睫。如果我们想做一些“正常”的事情怎么办?也许是一些可以给 SUT 施压的事情。启动几个模拟呼叫并使用 Netscape 通过它们浏览 Internet 怎么样?这意味着将应用程序的执行环境绑定到特定的 PDP 上下文。

内核修改

测试用例机器同时位于用户空间内部 Gb LAN 和 Gi LAN 上。在测试用例有两个移动电话(每个移动电话都有一个活动上下文)的示例中,几个路由问题立即变得显而易见。在这种情况下,用户可能想要启动两个 Netscape 实例,将每个实例“绑定”到不同的移动电话上下文(即 IP 地址)。如果其中一个 Netscape 会话打开到 Gi LAN Web 服务器的 TCP 套接字,则内核有三个路由 TCP 数据包的选择:内核可以将数据包发送到两个移动电话的 USN 设备之一,或者发送到 Gi LAN 的真实以太网设备。

对于测试用例开发人员打开的 UDP 或 TCP 套接字,开发人员可以通过调用 setsockopt 来使用 SO_BINDTODEVICE 选项。绑定到特定设备会强制在该套接字上发送的所有数据包使用绑定的设备;但是,FTP、TELNET 和 Netscape 等应用程序不使用此选项。

在仔细观察 Linux 虚拟专用网络软件包之后,我们注意到 TELNET 和 Netscape 等程序将其所有 IP 流量路由出 VPN 隧道。在提供的共享库上使用 nm,我们发现共享库包含套接字 API 调用的完整替换。

由于 Web 浏览是移动数据服务的主要推动力,因此我们必须提供一种通过移动电话上下文浏览 Web 的方法。在花费大量时间查看 Linux 网络代码以避免重写整个套接字 API 之后,我们决定挂钩到 IPv4 套接字创建中。我们的挂钩搜索当前任务的环境表以查找环境变量 (ND),如果存在,则将其绑定到 ND 变量指定的设备。

挂钩

我们希望将挂钩作为可加载模块进行。但是,我们发现我们必须修改 Linux 2.0.36 网络代码。我们修改了内核以全局导出 inet_proto_ops 结构,并调整了 sock_registersock_unregister 例程以正确地打开和关闭中断。Linux 2.2 内核似乎使这两个更改都不必要,并为本机 IP 堆栈操作的可加载模块“过度绑定”打开了机会。

当我们的模块加载时,我们通过调用 sock_register 并将 inet_proto_ops 结构的 create 函数指针指向我们的套接字 create 函数来挂钩套接字创建过程。从那时起,每当创建套接字时,都会调用我们的例程。

当我们在内核中时,我们应用了内核补丁,以提供串行控制台和微秒分辨率的时间戳,用于接收到的以太网数据包。我们还修改了 netlink 驱动程序以增加其次要设备的数量。

搜索环境

如前所述,当挂钩就位时,我们的套接字创建函数会搜索当前任务的环境表以查找 ND 变量,如果找到,则将套接字绑定到该设备。

该函数首先在物理内存(内核在其中运行)中查找环境表,但环境表内存位置存储为任务结构下的用户内存地址(current-->mm-->env_start 到 current-->mm-->env_end)。我们使用 fs/proc/array.c 中的 get_phys_addr 将用户地址转换为物理地址。使用此地址转换函数,我们的代码在当前任务的环境中搜索 ND=

如果找到 ND 变量,我们的代码会使用 SO_BINDTODEVICE 选项和指定的设备调用 sock_setsockopt。但是,sock_setsockopt 会将 ifreq 结构从用户空间复制到内核空间。由于我们的代码位于内核空间中,因此我们必须通过自己设置 sk->bound_device 来实现设备绑定。

测试用例执行环境

一旦我们将 USN 设备的 /dev 条目和 SO_BINDTODEVICE 机制编译到内核中,我们就准备好开始测试我们的概念了。最初的测试非常简单;我们使用 ifconfig 配置 USN 设备,然后将新的内核支持的 ND 环境变量设置为该设备。从那时起,我们在该环境中运行的任何应用程序都将其出站 IP 数据包发送到我们的 USN 设备。为了实际观看数据包,我们在我们的 USN 设备上(在本例中为 usn0)执行了 tcpdump -i

ping 程序是我们知道的开始发送 IP 流量的最简单方法,因此我们首先执行了它。一切都证明以我们设想的方式工作。ping 数据包开始在 tcpdump 下显示。当时没有人读取设备文件,因此没有对 ICMP 请求做出响应。但就我们的目的而言,一切都按计划进行。

然后我们尝试了 ftptelnet,这是另外一对生成 IP 流量的 IP 应用程序。我们看到 ftp 和 telnet 流量出现在我们的 USN 设备上,这足以向我们证明我们的想法是可靠的——直到我们决定打开一个 xterm。

自然地,对于任何 X 应用程序,要查看 xterm,我们必须将 DISPLAY 环境变量设置为我们桌面机器的主机名。设置显示很简单,但是当我们尝试启动 xterm 时,我们并没有完全预料到接下来会发生什么——什么都没发生。

不幸的是,我们的内核修改和 USN 设备运行得有点太好了。启动 xterm 后,看一眼 tcpdump 输出就清楚地表明发生了什么:我们所有的数据包,包括 X 数据包,都被重新路由到 USN 设备。虽然这对 ping、FTP 和 TELNET 来说是一件非常好的事情,但现在却是一件非常糟糕的事情。X 数据包被写入 USN 设备,永远不会到达主机上的 X 服务器。

至少可以说,我们感到沮丧。当时我们有几个选择。第一个想法是注意正在使用的传输协议 (TCP) 和 X 端口号,因为数据包流经内核,并将这些数据包通过以太网设备路由。由于其复杂性和缺乏优雅性,这个想法很快就被否决了。

第二个解决方案是创建一个虚拟 X 用户环境,测试人员可以打开该环境,而不是使用 telnet 或 rlogin 访问测试用例机器。VNC(虚拟网络计算)软件包创建的一些有趣的可能性最近的介绍促成了这个概念。VNC 的能力令人印象深刻,因为它具有跨平台性。不幸的是,VNC 方法被证明太慢了。

这个概念是合理的,并且具有讽刺意味的是,它比让测试人员通过 telnet 登录、导出其显示器等更可取。就所有意图和目的而言,我们可以让测试人员从 Windows PC 登录并打开到 Linux 盒子的 VNC 会话。但是,由于测试社区基于在 HP-UX 上运行的 X,为什么我们不能求助于允许嵌套的 X 服务器呢?

Xnest X 服务器既是 X 客户端又是 X 服务器。它是主机显示器(在本例中为 HP)上 X 服务器的客户端,也是您在远程机器(Linux 盒子)上执行的 X 应用程序的服务器。启动并运行 Xnest 会话的过程非常简单。首先,您登录到远程机器,将您的显示器导出回主机显示器,然后使用本地显示字符串运行 Xnest。然后,此显示字符串用于在 Xnest 会话中运行远程应用程序。

结论

通过使用我们的 USN 设备、ND 环境变量和我们的 Xnest 用户环境,我们为 Nortel Networks 的 GPRS 开发人员提供了一个桌面测试环境,他们可以在其中练习最新的 GPRS 节点,模拟移动网络冲浪者和 Internet。

Linux 的开放网络代码使我们所有的工作成为可能。通过使用 Alessandro Rubini 的 Linux 设备驱动程序 (请参阅资源 3)和 Linux 源代码作为指南,我们在很短的时间内开发了一个设备驱动程序,即使我们以前从未编写过设备驱动程序。Rubini 的书在我们的内核修改期间也帮助了我们。很难想象能够在如此短的时间内使用闭源操作系统完成如此复杂的事情。

比较不同版本的 Linux 内核很有趣。内核的网络代码变得更加模块化,并集成了 IPv6。使用最新的内核版本,我们所有的设备驱动程序和内核修改都将是不必要的。除了 Rubini 等书籍和开源之外,Linux 还是任何网络应用程序的理想选择,因为它支持标准以及 Linux 开发社区提供的不断改进。

资源

Wes Erhart (erhart@nortelnetworks.com) 在德克萨斯 A&M 大学主修电气工程。他于 1993 年加入 Nortel Networks,并已管理 GSM 自动化开发团队两年。韦斯不是一个特别好的程序员,并且对所有计算机都怀有非理性的不信任感,但他确实有足够的理智娶了一位女神,现在有一个可爱的女儿 godette 充实了他的生活。

Joseph Bell (jobell@nortelnetworks.com) 是德克萨斯 A&M 大学计算机工程专业毕业生,自大学二年级以来一直在 Nortel Networks 工作。他喜欢所有与 Linux 相关的事物以及任何可以编程的事物。不编程时,他可以在德克萨斯州北部的农场放牧。

Marc Hammons (hammons@nortelnetworks.com) 是德克萨斯大学计算机科学专业毕业生,自 1994 年以来一直在 Nortel Networks 工作。他已经完全失去了对虚无主义的信仰,并且是一位重生的 Linux 爱好者。在业余时间,他喜欢深入研究源代码、喝一杯好咖啡和玩桌上足球。

Mark Mains (mmains@nortelnetworks.com) 毕业于 LSUS,获得物理学、数学和计算机科学三个学位。他于 1997 年开始在 Nortel Networks 工作,自 1996 年以来一直使用 Linux。他的大部分业余时间都花在电脑前,制造电路或修理他的 Z28。

加载 Disqus 评论