在 IBM AIX 上模拟丢弃的 TCP/IP 数据包

商业软件产品通常是在本地高速、高容量的网络上开发和测试的,这样的网络上很少会产生拥塞或丢失数据包。部署环境中的网络条件可能不是那么理想。网络设备上的严重拥塞或硬件错误可能会导致丢失数据包,从而严重影响产品性能。本文介绍如何使用和定制一个现有的实用工具以便在 IBM® AIX® 上模拟丢弃的传输控制协议/Internet 协议 (TCP/IP) 数据包,从而让您能够预期一个产品在不太理想的网络条件下的行为如何。

Jerry Stevens, 软件开发人员,Service Registry 团队, IBM

Jerry Stevens 是位于英国 Hursley 市的 IBM 软件实验室的软件开发人员,他在 Service Registry 团队工作。他于 1997 作为 IBM AIX 顾问加入 IBM UK Global Services,并于 2001 年调动到 Hursley 实验室从事 WebSphere MQ 的性能分析和软件开发,在 2009 年又调动到 Service Registry 团队。在加入 IBM 之前 Jerry 是壳牌石油公司的高级系统工程师,职务是技术顾问和软件开发,经常和各种开放系统平台和架构打交道。它毕业于 Exeter University 并取得数学学位。



2013 年 6 月 06 日

简介

本文提供:

  • 一个 AIX 内核扩展,用于允许随机丢弃指定百分比的往返于指定主机的 TCP/IP 数据包,以模拟糟糕的网络条件。
  • 一个实用工具,用于加载、激活和卸载该内核扩展。
  • C 和 Java™ 实用工具,用于监视到目标主机的总数据包吞吐量以及实际丢弃的数据包。
  • 该内核扩展的完整源代码,它带有 C 和 Java 实用工具以便可以根据本地要求构建、定制和开发该软件。

本文以一个可下载的压缩文件的形式提供构建和运行此数据包丢弃模拟器所需的所有组件。阅读 如何按原样构建和使用内核扩展 小节,您可以快速入门并使用该扩展。

该内核扩展设计为在单个源节点上激活,它将在这个节点上有选择地丢弃往返于目标节点的数据包。没有必要在目标节点上安装数据包丢弃模拟器。目标节点本身甚至不需要基于 AIX,而只需要支持 TCP/IP 协议。

本文首先提供 TCP/IP 性能的一些背景以及为何丢弃的数据包会对应用程序性能影响较大。然后,介绍如何按原样构建和使用内核扩展。这样您就可以快速入门并运行该扩展了。

提供的这个功能非常简单,这是有意为之的。它允许您为往返于指定主机的 TCP/IP 数据包设置一个给定的随机丢弃百分比,而不影响所有其他主机和协议,并且不保留任何历史记录。因为,实际上您可能需要根据自己的需要向该内核扩展中添加更多功能,您还需要了解有关该扩展是如何工作的以及如何对它进行定制的详细信息。如果您有以下需求,那么您可以决定这样做:

  • 允许往返于多个主机的数据包丢弃。
  • 对于这些主机中的每个主机,支持不同的数据包丢弃比率。
  • 创建在这两个方向接收、传输和丢弃的总数据包的历史记录日志。
  • 在编写软件时使用的不同版本的 AIX 或 C 编译器上实现数据包丢弃模拟器。

可能会进一步开发该内核扩展,以便模拟其他可能会引发性能问题(如数据包损坏、到达的数据包顺序混乱以及抖动)的网络问题。

建议您仅在非生产系统上使用该内核扩展。


背景

首先,让我们看一些基本术语,这些术语在考虑网络性能、带宽、延迟以及拥塞时非常重要。

带宽是网络的容量,即可以推送数据通过的速率。它的计量单位通常是每秒兆字节 (Mbps),其中一兆就是 10^6 位。可以将它视为网络管道的宽度。

延迟是数据穿越网络所需的时间。它的计量单位通常是毫秒。可以将它视为网络管道的长度。

图 1 中显示了带宽和延迟之间的差别。

图 1. 延迟和带宽以及网络管道
延迟和带宽以及网络管道

拥塞是过度使用网络的效果。它会导致传播数据时发生延迟、因数据包丢失而导致数据包重新传输,以及无法连接到网络。数据包丢失通常以百分比为单位进行计量。TCP 通常可处理高达 0.1% 的丢失,这对数据传输没什么直接影响。如果丢失超过这个比例,那么后果可能会比较严重。

网络延迟、带宽和拥塞都可能会对应用程序性能产生严重影响。例如,一个系统上的前端应用程序通过网络对另一个系统进行频繁调用(后者托管有应用程序所使用的数据库)。穿过网络与数据库的通信可能包含很多簇小数据,或者可能包含几组相当大的数据传输。无论怎样,网络能够可靠、快速地传输此数据是客户感知到的应用程序端对端性能中的重要因素。

应用程序开发团队通常访问的是独立、高速的网络,与部署应用程序的环境相比,这里的网络使用量要小得多。这可能意味着在应用程序的开发和测试环境中很少能观察到糟糕的网络性能对应用程序的影响。反过来,这意味着在客户环境中观察到的应用程序性能可能与测试环境中观察到的性能大大不同。

本文重点介绍丢弃的数据包对具体应用程序性能的影响。由于前端和后端之间传输的数据包可能会很多并且传输这些应用程序数据包的模式特定于前端和后端应用程序的性质,因此了解对应用程序影响的最简单方法是模拟数据包的丢弃。

在系统之间传输数据时可能会丢弃数据包的原因主要有两个:

  • 过度的网络利用并导致拥塞
  • 网络硬件或连接器故障

TCP 设计为能够对网络上丢弃数据时进行反应。当一个数据包成功地传递到它的目标时,目标系统向源系统发送回一个确认消息。如果在一定的时间间隔内没有收到此确认消息,那么这可能是因为目标系统从未收到此数据包或者是因为包含此确认消息的数据包本身丢失。任何情况下,如果源系统在给定的时间内未收到此确认消息,那么源系统会假定目标系统从未收到此消息并重新传输此消息。我们容易看出,如果网络性能较差,那么首先会丢失数据包,然后因这些重新传输消息而增加的负载又会进一步增加网络上的负载,这意味着会丢失更多的数据包。这种行为可能会在网络上非常快速地造成严重情况。

有一些免费提供的复杂软件产品,这些产品可以帮助模拟各种网络性能的不同特征。这些软件产品包括:

WANem 从基于 Knoppix 的 CD 运行,可以模拟各种不同的网络特征。它能够使用筛选选项丢弃任意数量的数据包,但需要在另一个位于源系统和目标系统之间的单独系统上运行。这可能非常不方便,例如,当源系统和目标系统安装在专用的高速网络上时。

尽管 ipfilter 看起来非常有前途,但它只是部分移植到了 AIX 5.3,因此很明显,编写一个提供所需的最低功能的 bespoke 实用工具可能比将整个 ipfilter 移植到 AIX 6.1 和 7.1 速度更快。

dummynet 是 FreeBSD 操作系统的一个组件,它也已经移植到其他平台,包括 Linux 和 Microsoft® Windows®。使用此组件的缺点是,若要模拟源自 AIX 主机的数据包的丢弃,必须使用 dummynet 配置和设置一个中间的 FreeBSD/Linux 主机。

ALTQ 是另一个队列框架,它可帮助提供带宽控制,大多是在 BSD 路由器上。

也有其他商用的程序包,如 LANforge ICE,但由于成本的原因,我们排除了这些程序包。LANforge 也需要专用的硬件。


如何按原样构建和使用内核扩展

要求

数据包丢弃模拟器是在以下环境中构建和测试的:

  • 带有 gcc V4.7.2-1 的 AIX V7.1.4.0
  • 带有 gcc V4.2.0 的 AIX V6.1.2.0
  • 带有 gcc V4.2.0 的 AIX V6.1.4.0

该内核扩展以及关联的 C 程序是使用 gcc 编译器编译的。使用 AIX C 编译器应该非常简单,但某些编译标记可能需要更改。

请注意,构建该内核扩展有必要安装 AIX bos.adt.syscalls 文件集。该文件集位于您的 AIX 安装媒体上。在只部署该内核扩展的系统上将不需要该文件集,只要 AIX 级别与构建系统相同即可。如果两个系统之间的 AIX 级别有任何差别,那么每个系统上都应该构建该内核扩展。

该内核扩展、用于加载和激活它的 kctrl 实用工具以及 C 示例控件和监视器都是使用 AIX make 实用工具构建的。可下载的 zip 文件中包含用于构建所有这些组件的 makefile。

注意事项

使用该实用工具之前,应该记住两个重要因素:

1) 开发和测试任何内核扩展时都应该多加小心。通常来说,很多错误都会导致系统发生故障。理想情况下,应该使用一个专用的测试系统,但避免使用同时用于其他用途或其他用户使用的系统。如果系统位于远程数据中心,那么还应该确保在需要时可以重新启动该系统(如果不自动重启)。

2) 避免使用泛洪 ping 在共享网络上测试该实用工具,因为这会严重影响其他用户。

构建内核扩展和实用工具

首先,确保您的开发系统上满足了上述的所有要求。

接下来,将可下载的附件解压缩到您在目标 AIX 系统上选择的工作目录。

下载中不包含预先构建的二进制文件,以避免在不兼容的操作系统级别上运行它的风险。因此,需要在解压缩之后构建该软件。为此,将目录更改为您在上面创建的工作目录,然后按照如下方式运行 make 命令:

# make
        gcc -Wall -maix64 -ffreestanding -msoft-float   -o pdrop_kernex64.o -c
        pdrop_kernex.c
        ld -b64 -o pdrop_kernex64 pdrop_kernex64.o -e pdrop_init -bI:
        /home/jerry/kernext/kernex.exp -bI:/home
        /jerry/kernext/netinet.exp -bE:/home/jerry/kernext/pdrop_syscall.exp -lsys -lcsys
        rm -f pdrop_kernex
        ar -X64 -r -v pdrop_kernex pdrop_kernex64
        ar: Creating an archive file pdrop_kernex.
        a - pdrop_kernex64
        gcc -Wall -maix32 -o pdrop_ccm32  .c -Xlinker -bI:
        /home/jerry/kernext/pdrop_syscall.exp
        gcc -Wall -maix64 -o pdrop_ccm64 pdrop_ccm.c -Xlinker -bI:
        /home/jerry/kernext/pdrop_syscall.exp
        gcc -maix64 -o kctrl kctrl.c
        gcc -Wall -I/usr/java6/include -I. -shared -fPIC pdrop_jni.c -o libpdrop_jni.so 
        -Xlinker -bI:/home/jerry/kernext/pdrop_syscall.exp
Target "all" is up to date.
#

成功运行 make 命令之后,使用该数据包丢弃实用工具时所需的所有 C 组件就全部就绪了。

请注意,Java 控件和监视器应用程序需要单独构建,如 控件和监视器应用程序 小节所述。

加载和激活内核扩展

后续步骤中将构建的一个二进制文件是 kctrl 程序。该程序允许加载、激活和卸载该内核扩展。若要加载和激活内核扩展,请继续执行以下命令:

# ./kctrl /home/jerry/kernext/pdrop_kernex64

 Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
l
Extension Successfully loaded, kmid is 1353523200

 Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
i
 Extension Initialized

 Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
e
#

此时,内核扩展将被加载和激活,但在使用 C 或 Java 控件和监视器实用工具之前,不会丢弃任何到目标主机的数据包。

启动数据包丢弃和监视

加载和初始化该内核扩展之后,您现在可以看到如何使用提供的 C 实用工具 pdrop_ccm 来控制和监视到目标主机的数据包的设置。

调用 pdrop_ccm,用参数指定构建该内核扩展的位置、目标主机名称和丢弃比率。

# ./pdrop_ccm64 /home/jerry/kernext/pdrop_kernex64
Usage: ./pdrop_ccm64 <kernel_extension> <TargetIP> <drop_percentage>
#

例如,您可以在主机名为 fred、丢弃比率为 0.5% 的情况下使用该命令,如下所示:

# ./pdrop_ccm64 /home/jerry/kernext/pdrop_kernex64 fred 0.5
Kernel extenstion is loaded.
Setting drop ip to: x.y.z.l
Drop rate:  0.50.

Total In In dropped  PC in dropped  Total Out  Out dropped  PC out dropped
 0         0           0.00             0          0           0.00
10120     57           0.56           10174       53           0.52
22224    134           0.60           22340      116           0.52
#

在本例中,启动 pdrop_ccm 后不久,我们就开始向目标主机发送 IP 通信。pdrop_ccm 实用工具将一直运行,直到被 ctrl+c 中断,并且每 10 秒钟就会输出一个更新行。

请注意,第一个命令行参数,即该内核扩展名称,必须指定为用 kctrl 加载时的名称。

丢弃百分比参数是可选的。如果未提供该参数,pdrop_ccm 将仅显示当前的丢弃比率,不会重置该比率。

还提供了一个基于 Java 的控件和监视器实用工具,控件和监视器应用程序小节对此进行了描述。

这就是开始使用该内核扩展的全部内容。

本文的其余部分提供了有关该内核扩展如何工作、工作得如何以及为个人用途而开发和定制控件和监视器应用程序的详细信息。


内核扩展的工作原理

AIX 内核扩展是采用 C 编程语言编写的。该内核函数通过一组系统调用来提供对它的访问。C 和 Java 控件和监视器应用程序利用这些系统调用来驱使内核扩展以给定的比率丢弃数据包以及往返于特定主机。对于 Java 环境,JNI 包装器类提供从 Java 对该内核扩展系统调用的访问。

该内核扩展本身包含:

  • 一组系统调用,用于控制数据包的丢弃以及数据包统计信息的收集
  • 在该内核的网络层中有两个函数。其中一个函数用于入站通信,另一个函数用于出站通信。每当接收或发送数据包时就会调用这些函数,以控制数据包的丢弃。

系统调用由用户级别的代码进行访问,用于设置入站和出站数据包应该丢弃到的远程系统以及应该丢弃的数据包百分比。这些系统调用还包含函数来检索确定入站和出站方向数据包丢弃数量的计数器,还包含一个用于重置计数器的函数。

对于 C 环境,这些系统调用直接用于示例控件和监视器应用程序 pdrop_ccm.c 中,我们在上面的 启动数据包丢弃和监视 小节使用了此示例。对于 Java 环境,可以通过使用 JNI 来使用系统调用。然后定制 Java 程序可以使用 JNI 包装器来控制和监视数据包的丢弃。而且,本文还提供了一个示例 Java 应用程序作为下载的资源。该应用程序名为 pdrop_jcm.java。有关该示例 Java 实用工具的详细信息,请参阅 Java 控件和监视器 小节。

图 2 演示了内核扩展的不同组件以及通过该扩展的数据流。下面两小节讨论通过该扩展的数据包流、要发送到目标节点的出站数据以及从该扩展接收的入站数据。

出站通信流

首先,考虑出站通信的情况,其中要测试的应用程序会提交要通过网络传输的数据。请参阅图 2 中的点 A。应用程序向内核提交一组数据,以便发送到网络上的远程节点。TCP 子系统将此数据分为很多个数据包,通常会将这些数据包发送到相应的网络设备驱动程序以便通过网络进行发送。但使用内核扩展时,挂钩 outbound_fw 是 IP 层中设置的,这会导致每次发送数据包时都会调用内核扩展的出站筛选器函数 pdrop_outbound_filter。该操作会从数据包中检索 IP 标头并将该标头与要丢弃数据包的目标节点相匹配。如果该节点不匹配,则会调用函数 ip_output_post_fw,该函数会将数据包路由到主机上相应的接口。如果节点匹配,则随机决定是否让数据包通过、如何根据要丢弃的数据包数量进行加权。如果这个决定是应该发送数据包,则会调用 ip_output_post_fw 函数并让数据包通过。否则,便会释放包含数据的 mbuf,出站筛选器返回且不发送数据包。

入站通信流

对于入站通信,当设备驱动程序从网络接收数据时会应用相应的操作。请参阅图 2 中的点 B。设备驱动程序将数据的数据包转发到 TCP/IP 子系统。现在,这会调用入站筛选器,该筛选器由内核扩展通过将入站挂钩 inbound_fw 分配给 pdrop_inbound_filter 函数重新进行了设置。该函数做出是否让数据包通过的决定。如果数据包不是来自要丢弃数据包的目标节点,则会通过调用 ipintr_noqueue_post_fw 函数自动通过。如果数据包来自目标节点,则再次做出一个有关是否根据所需丢弃的数据包百分比让数据包通过的随机决定。如果该决定是发送数据包,则会调用ipintr_noqueue_post_fw 函数并让数据包通过。否则,会释放包含数据的 mbuf,入站筛选器返回且数据包不会向上传递到较高的层。

IBM AIX 7.1 信息中心介绍了上面所述的 AIX IP 筛选挂钩和内核服务

图 2. 数据包丢弃内核扩展
数据包丢弃内核扩展

点击查看大图

图 2. 数据包丢弃内核扩展

数据包丢弃内核扩展

入站筛选器

这是入站筛选器函数:

/*
** This function is the filter for incoming traffic
*/
void pdrop_inbound_filter(ifp, m, args)
struct ifnet *ifp;
struct mbuf *m;
inbound_fw_args_t *args;
{
  struct ip *ip_in;

  if (PDROP_TRUE == amDropping) 
  {
     ip_in = mtod(m, struct ip *);

     if ( (ip_in->ip_p == IPPROTO_TCP) &&
          (dropip.s_addr == ip_in->ip_src.s_addr))
     {
         fetch_and_addlp((atomic_l) &total_in, 1);
         if (dropMod != 0)
        {
         if ( (pdrop_random() % dropMod) == 1)
         {
             fetch_and_addlp((atomic_l) &nin_dropped, 1);
             if (m != NULL)
             {
                m_freem(m);
             }
             return;
         }
     }
  }
}
ipintr_noqueue_post_fw(ifp, m, args);
return;
}

该筛选器函数包含一个 mbuf 参数,它包含接收到的数据。mtod 宏用于将指向 mbuf 的指针转换为指向接收到的数据的指针,该指针的地址就是一个 IP 标头。为统计从目标地址接收到的数据包总数设有一个计数器。

然后,该函数会将为丢弃选择的 IP 地址与接收到的数据包中指定的源地址进行比较。如果这些地址匹配,则会调用 pdrop_random() 函数,该函数使用模函数选择所需的丢弃数据包百分比。如果要丢弃该数据包,则会增加丢弃数据包的内部计数器。如果不丢弃该数据包,则会调用 ipint_noqueue_post_fw 函数以将该数据包传递到 TCP 进行处理。

调用 fetch_and_addlp 系统函数来增加接收到和丢弃的数据包的总数计数。这些保证了计数保持原子性,从而确保了线程安全。

从本例可以看出,有可能会指定要筛选的协议。本例中,我们只丢弃针对 TCP 协议的数据包。为了进行测试,最好让它使用其他协议,如 Internet 控制消息协议 (ICMP),以便可以将数据包丢弃程序与 ping 之类的命令一起使用。为此,上面测试中的 IPPROTO_TCP 常量将更改为 IPPROTO_ICMP。

注意:上面代码段中的 amDroppingdropipdropMod 都被定义为全局变量。

出站筛选器

这是出站筛选器函数:

/*
** This function is the filter for outbound network traffic
*/
int pdrop_outbound_filter(ifp, m, args)
struct ifnet *ifp;
struct mbuf *m;
outbound_fw_args_t *args;
{
  struct ip *ip_out;

  if (PDROP_TRUE==amDropping) 
  {
     ip_out = mtod(m, struct ip *);

     if  ( (ip_out->ip_p == IPPROTO_TCP) &&
           (dropip.s_addr == ip_out->ip_dst.s_addr))
     {
       fetch_and_addlp((atomic_l) &total_out, 1);
       if (dropMod != 0)
      {
       if ( (pdrop_random() % dropMod ) == 1)
       {
         fetch_and_addlp((atomic_l) &nout_dropped, 1);
         if (m != NULL)
         {
            m_freem(m);
         }
         return 0;
       }
    }
  }
}

ip_output_post_fw(ifp, m, args);
return 0;
}

该函数的工作方式与入站筛选器非常相似。为目的地是目标 IP 地址的数据包总数和丢弃的出站数据包总数设有计数器。

如果要丢弃该数据包,则释放指向 mbuf 的指针。

注意:上面代码片段中的 amDroppingdropipdropMod 都被定义为全局变量。

随机选择要丢弃的数据包

对于该内核扩展来说,随机选择用于丢弃的数据包是非常重要的。如果发送了一定数量的数据包之后再丢弃数据包,那么必然不是对过度使用的网络上所发生情况的真实模拟。而且,测试的软件以固定和非固定的时间间隔丢失数据包时,其行为可能会不相同。

不能在内核扩展中使用标准的 C 库 rand() 调用来生成随机数。这是因为在可重入的内核环境中使用这类函数不安全。如果您尝试使用该函数,那么当从内核扩展调用该函数时系统可能会发生故障。

因此,我们使用一个简单的函数来模拟随机调用。该函数根据以下输入生成一个 long 型的随机数:

  • 当前时间中的秒字段
  • 当前时间中的纳秒字段
  • 随机函数调用的次数
  • 自从系统启动处理器触发的次数

这是该内核扩展中使用的随机函数的源代码。

long pdrop_random()
{
  static long calls = 0;
  struct timestruc_t ts;
  long x = 0;

  curtime(&ts);

  // lbolt is the number of clock ticks since boot, see HZ in /usr/include/sys
     /m_param.h for number of ticks/sec

  fetch_and_addlp((atomic_l) &x,
      (long) ts.tv_nsec + (long) ts.tv_sec +
      (long) lbolt +
      calls++);

  return x;
}

当然,这不是真正的随机函数,而是一种足够好的机制,可以让人确信是以非固定时间间隔丢弃数据包。由于 calls 变量是静态的,因此它会自动随 fetch_and_addlp 内核服务一起递增。

数据包丢弃内核扩展中提供的系统调用

本小节将向您介绍该内核扩展中提供的各种系统调用的详细信息。如果您需要编写自己的控件和监视器应用程序或者定制该内核扩展,那么您可能会对这些内容感兴趣。

设置目标地址:int pdrop_set_drop_address(struct in_addr *block_addr)

该函数以指向 in_addr struct 的指针作为参数。可能会通过基于字符串的主机名从应用程序级别来调用该方法,如下所示:

int setDropAddress(char *hostname)
{
   int status = 0;
   struct in_addr block_addr;

   if (1 == inet_aton(hostname, &block_addr))
   {
      if (PDROP_TRUE == pdrop_set_drop_address(&block_addr))
         status = 1;
   }
   return status;
}

检索目标地址:int pdrop_get_drop_address()

该函数以 32 位无符号整数的形式返回目标地址。

设置丢弃数据包的比率:extern void pdrop_setDropMod(long m);

该函数获取一个 long 型的输入值,该值表示应该丢弃数据包的频率。这种方法的工作方式是,内核扩展生成一个随机数,然后使用 long 型值获取该随机数的模数。如果结果是 1,则丢弃数据包,否则传递该数据包。

之所以采用这种方法设计此调用,是因为在该内核扩展中不可能执行浮点操作。

下面是一个根据 double 型的百分比值从应用程序级别调用 pdop_setDropMod() 的简单方法:

void setDropPC(double d)
{
        long x = 0;
        x = 100.0 /d;
        pdrop_setDropMod(x);
}

因此,如果需要丢弃 0.1% 的数据包,那么 d 将为 0.1,传递到 pdrop_setDropMod 的 long 型值将为 1000。

检索丢弃数据包的比率:extern long pdrop_getDropMod();

该函数返回应该丢弃数据包的比率的 long 型表示,它的表示方法与 pdrop_setDropMod(); 调用的表示方法相同。

激活数据包的丢弃:void pdrop_startDropping()

内核扩展启动后,要调用 pdrop_startDropping() 函数才会开始丢弃数据包。您应该在设置目标地址以及应该丢弃数据包的比率之后调用此函数。

停止数据包的丢弃:void pdrop_stopDropping()

该函数停止数据包的丢弃。可以根据需要调用 pdrop_startDropping()pdrop_stopDropping() 函数,以启用和禁用数据包丢失。

查询是否正在丢弃数据包: int pdrop_amDropping()

如果内核扩展当前正在丢弃数据包,则该函数返回 1,否则返回 0。

检索内核扩展计数器:void pdrop_getstats(struct pdrop_stats *p)

该函数返回一个结构,该结构包含以下详细信息:

  • 从目标地址接收的数据包总数
  • 从目标地址接收的丢弃的数据包总数
  • 发送到目标地址的数据包总数
  • 发送到目标地址的丢弃的数据包总数。

所使用的结构的定义在头文件 kernext_pdrop.h 中。

下面是如何从应用程序级别接收计数器的示例:

pdrop_stats_t j;
pdrop_getstats(&j);

printf("Total in: %d.\n",  j.total_in);
printf("In dropped: %d.\n", j.nin_dropped;);
printf("Total out: %d.\n",  j.total_out);
printf("Out dropped: %d.\n", j.nout_dropped);

重置内部内核扩展计数器:void pdrop_reset_counters()

该函数用于将 pdrop_getstats 返回的计数器重置为零。


配置内核扩展

如何按原样构建和使用内核扩展 小节提供有关如何构建内核扩展及其实用工具的信息。本节介绍:

  • 有关构建过程的详细信息
  • 有关 AIX 6.1 和 AIX 7.1 构建问题的信息
  • 有关将内核扩展中提供的系统调用导出到应用程序级别的详细信息
  • 有关加载和激活内核扩展的详细信息
  • 有关使用系统日志来记录何时加载和卸载扩展的信息

AIX 6.1 和 AIX 7.1 构建问题

从 AIX 6.1 开始,AIX 操作系统就通过仅提供 64 位的内核简化了它的内核环境。正如上面所规定的,AIX 6.1 和 AIX 7.1 保持了与 AIX 早期版本的应用程序二进制文件兼容性,但无法在 AIX 6.1 或 AIX 7.1 上构建仅 32 位版本的设备驱动程序和内核扩展。

由于本文提供的内核扩展适合 AIX 6.1 和 AIX 7.1,因此该扩展是在 64 位模式下构建的。

构建过程

本文提供的压缩文件附带一个 Makefile 文件。该文件可以用来构建内核扩展以及与其关联的应用程序层程序。请注意,这个 Makefile 是为了与 AIX make 实用工具一起使用而编写的,如果您想使用 gnu make,则需要对其进行修改。

内核扩展本身是使用以下命令构建的:

gcc -maix64 -ffreestanding -msoft-float -o pdrop_kernex64.o -c pdrop_kernex.c
 
ld -b64 -o pdrop_kernex64 pdrop_kernex64.o -e pdrop_init -bI:/usr/lib/kernex.exp -bI:
/usr/lib/netinet.exp -bE:/home/jerry/kernext/pdrop_syscall.exp -lsys -lcsys

正如前面所述,内核扩展被构建为 64 位二进制文件。-ffreestanding-msoft-float 选项用来防止使用浮点指令来操作特定的数据结构。在 AIX 7.1 上需要这么做,但在 AIX 6.1 上不需要。

ld 命令指定到内核扩展的入口点,在本例中该入口点是 pdrop_init() 函数。当激活内核扩展时,调用该函数。

ld 命令还引用文件 kernex.exp 和 netinet.exp。这些文件是与 bos.adt.syscalls 文件集一起提供的。该文件集可能没有安装在您的测试系统上,这种情况下,您可能需要请系统管理员来安装该文件集。

请注意,必须对 /usr/include/sys/socketvar.h 头文件进行一个小小的修改,gcc 才能满意地编译内核扩展。您还应该保留该文件的一个安全副本并且找到以下行:

extern struct free_sock_hash_bucket free_sock_hash_table[];

这一行应该更改为:

extern struct free_sock_hash_bucket * free_sock_hash_table;

该内核扩展在源代码中进行此 #define

#define _MSGQSUPPORT 1

必须防止进行对内核扩展不可用的 fd_select() 系统调用。使用 #definefd_select() 调用更改为对内核扩展可用的原始 select() 调用。

导出所提供的系统调用

pdrop_syscall.exp 文件定义导出到应用程序级别的内核扩展系统调用。该文件的内容如下所示:

#!/unix
pdrop_set_drop_address syscall3264
pdrop_get_drop_address syscall3264
pdrop_setDropMod syscall3264
pdrop_getDropMod syscall3264
pdrop_startDropping syscall3264
pdrop_stopDropping syscall3264
pdrop_amDropping syscall3264
pdrop_getstats syscall3264
pdrop_reset_counters syscall3264

如果您需要定制内核扩展以包含其他系统调用,那么您将需要修改此文件。位于每一行结尾的 syscall3264 标识符使系统调用可用于 32 位和 64 位过程。该标志还可以设置为 syscall32 以支持来自 32 位过程的调用,或设置为 syscall64 以便仅支持 64 位过程。如果没有为正确的目标过程环境设置该标志,那么当进行系统调用时该过程将由于某个分段故障而失败。有关如何设置该标识符的详细信息,请参阅 导出内核服务和系统调用 主题。

使用 kctrl 控制内核扩展的加载

如前面所述,加载和卸载内核扩展是通过所提供的 kctrl 程序进行的。应该使用内核扩展的完整路径名称调用该程序。然后,采用交互的方式接受以下命令:

  • q – 检查内核扩展是否已加载
  • l – 加载内核扩展
  • I – 初始化内核扩展
  • t – 终止内核扩展
  • u – 卸载内核扩展
  • e – 退出实用工具

下面是使用 kctrl 查询、加载、初始化、终止、卸载以及退出实用工具的一个示例:

# ./kctrl /home/jerry/kernext/pdrop_kernex

 Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
q
 Extension is not loaded

 Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
l
Extension Successfully loaded, kmid is 1353052160

 Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
i
 Extension Initialized

 Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
q
 Extension is loaded, with kmid   1353052160

 Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
e
#

您还可以使用 AIX genkex 命令来检查内核扩展是否已加载,例如:

# genkex  | grep -i kern
f1000000c02be000     2000 /home/jerry/kernext/kernext_hello
#

在本例中,kctrl 可执行文件处理交互式用户输入。修改 kctrl 以将之作为命令行上的另一个参数应该非常简单,因此它很容易合并到系统启动和关闭脚本中。但是,出于此实用工具的特性考虑,通常不建议这么做。

内核扩展中的日志记录

内核扩展使用 syslogd 后台程序记录扩展何时被加载和卸载。这对于调试或审核非常有用。若要启用此日志记录,请执行以下操作:

  • 确保在 /etc/syslog.conf 中启用日志记录。例如,可以将以下行附加到此文件:
*.debug              /var/log/syslog.out     rotate size 100k files 4
  • 确保在 /etc/syslog.conf 中启用的日志文件存在。
  • 刷新 syslogd 子系统 (refresh -s syslogd)

使用其他 IP 协议

尽管该扩展主要考虑 TCP/IP,但将其更改为使用其他基于 IP 的网络协议也非常简单。内核扩展的工作原理 小节中对此进行了介绍。


控件和监视器应用程序

我们提供了两个示例应用程序,它们可以使用内核扩展提供的系统调用。一个示例应用程序用于 C 环境,另一个示例应用程序用于 Java 环境。

这些应用程序介绍如何编写您自己的定制应用程序来控制数据包丢弃。如果您需要编写您自己的自动测试框架以模拟不同的网络条件,那么您可能会决定这样做。

应该注意的是,只有直接从内核扩展收集统计信息的应用程序才能提供有意义的有关已丢弃数据包的统计信息。操作系统提供的报告已丢弃数据包的实用工具将不包含通过内核扩展丢弃的数据包的详细信息。这是因为扩展丢弃数据包是在数据包通过 TCP/IP 子系统以便进行分发或传递之前进行的。

C 控件和监视器

在本文的下载部分提供了测试应用程序 pdrop_ccm.c。该应用程序是在命令行上使用目标主机名和丢弃百分比进行调用的。该应用程序会将目标主机和丢弃比率的详细信息传递给内核扩展,然后每 10 秒钟监视到目标系统的入站和出站数据包。

既可以从 32 位应用程序,也可以从 64 位应用程序调用数据包丢弃系统调用。提供的 makefile 通过构建 32 位版本和 64 位版本的控件和监视器应用程序说明了这一情况。这些应用程序的名称分别为 pdrop_ccm32 和 pdrop_ccm64。

pdrop_ccm 显示了输入和输出数据包的总数、丢弃的输入和输出数据包的数量以及丢弃的数据包百分比。它以内核扩展的名称、目标主机的名称作为参数,还有一个可选值指定目标丢弃比率。如果未在命令行指定这第三个参数,则不会对丢弃比率进行任何更改。下面是正在使用的该应用程序的一个示例:

# ./pdrop_ccm /home/jerry/kernext/pdrop_kernex64 fred 1.0
 Extension is loaded, with kmid   1353052160
Official name is: fred
    IP addresses: 9.20.XXX.YYY
Target IP address: 9.20.XXX.YYY.

Total In   In dropped       PC in dropped   Total Out    Out dropped     PC out dropped
0               0                0.00           0               0                0.00
0               0                0.00           0               0                0.00
13138           138              1.05           13283           144              1.08
29158           275              0.94           29460           302              1.03
43738           423              0.97           44206           467              1.06
58320           564              0.97           58952           632              1.07
72653           721              0.99           73437           784              1.07
87227           872              1.00           88153           926              1.05
#

从这里我们可以看出,到目标主机 fred 的数据包将被丢弃 1.0%。然后以 10 秒钟的时间间隔显示有关总数据包和丢弃的数据包的状态更新。

该测试应用程序调用 pdrop_reset_counters() 方法重置内核扩展计数器。10 秒钟之后,您可以看到到目标系统的网络活动开始了并且显示了数据包统计信息。

请注意,该示例仅控制一个目标系统。如果您再次运行 pdrop_ccm 命令并指定不同的目标主机名,那么将不再丢弃往返于原始主机的数据包。

Java 控件和监视器

我们还提供一个示例 Java 应用程序 PDrop_jcm.java,该应用程序演示了如何在 Java 环境中使用数据包丢弃模拟器。

PDrop_jcm.java 使用 JNI 来访问用于控制和监视内核扩展的 C 环境。共享库 libpdrop_jni.so 提供可以从 Java 环境调用的原生方法。该共享库从源代码 pdrop_jni.c 中构建为前面所述的构建过程的一部分。

JNI 包装器在 Java 级别提供以下原生方法,这些方法映射到共享库中的函数:

        public native boolean isExtensionLoaded(String extName);
        public native boolean setDropAddress(String hostname);
        private native String getDropAddress();
        public native boolean amDropping();
        public native int setDropping(boolean doDrop);
        public native void resetCounters();
        public native void setDropHostName();
        public native void setDropPC(float d);
        public native float getDropPC();
        public native long getTotalIn();
        public native long getTotalOut();
        public native long getInDropped();
        public native long getOutDropped();

调用这些原生方法时,将会调用共享库中相应的函数,结果将返回到 Java 环境。

若要构建和使用这个测试 Java 应用程序,首先要确保您将 PATH 正确地设置为 Java SDK 环境,例如:

export PATH=/usr/java6/bin:${PATH}

接下来,设置 LIBPATH 环境变量以引用内核扩展所在的目录,以便可以解析共享库 libpdrop_jni.so

export LIBPATH=/home/jerry/kernext:/usr/lib

现在,编译 PDrop_jcm.java 应用程序,例如:

 javac -d . PDrop_jcm.java

或者,如果您需要重新生成 JNI 头文件,请运行以下命令。除非您定制了内核扩展并且更改或添加到原生方法,否则不需要运行此命令:

 javah -d . PDrop_jcm

如果您重新生成了该头文件,那么您将需要使用前面所述的过程重新构建共享库。

然后,使用内核扩展的路径、目标主机名以及可选的新的丢弃比率作为参数运行这个 Java 应用程序,如上面的 C 示例中所示:

# java PDrop_jcm /home/jerry/kernext/pdrop_kernex64 fred 1.0
Ext name: /home/jerry/kernext/pdrop_kernex64.
 Extension is loaded, with kmid   1353052160
Drop address is: 9.20.XXX.YYY
Drop PC: 1.0
Dropping enabled
Total In   In dropped      PC in dropped    Total Out       Out dropped     PC out dropped
0               0                0.0            0               0                0.0
0               0                0.0            0               0                0.0
13189           145              1.09           13307           118              0.88
28515           295              1.03           28782           267              0.92
44306           461              1.04           44701           395              0.88
59216           609              1.02           59753           537              0.89
72979           756              1.03           73688           709              0.96
88606           910              1.02           89455           849              0.94
102550          1054             1.02           103567          1017             0.98
104522          1071             1.02           105551          1029             0.97
104522          1071             1.02           105551          1029             0.97

在本例中可以看到,监视开始时发送到目标主机或从目标主机接收的数据包为零,但后来网络活动导致在这两个方向丢弃数据包。在本例中,丢弃比率被配置为 1%。


度量非模拟数据包丢失

前面已提到,不应该使用操作系统实用工具来监视通过使用内核扩展丢弃的数据包。但是,当您需要访问网络上实际丢弃的数据包时,则需要使用这些工具。本节提供了使用这些实用工具的一些有用的提示和技巧。

ping 实用工具显示通过网络发送的 ICMP 数据包上的数据包丢失。您可以在上面的示例中看到数据包丢失统计信息,其中该丢失为零。但是,这一测量值仅基于往返于 ping 实用工具传输的这些数据包,并不测量穿越网络的任何其他数据包。

在某些系统上,ping 还支持一个将 ICMP 数据包尽快写入到网络上的选项,并在结尾处还是为您提供丢失统计信息。这就是所谓的泛洪 ping。您应该小心使用这一选项,因为它在运行时有可能会影响常规网络性能。应该只运行它几秒钟,并且仅在测试条件下运行。

也可以使用 netstat 实用工具来查看在网络上丢弃的数据包数量。下面是 AIX 上的一个 netstat -D 示例输出。

使用 netstat 报告数据包丢弃 (AIX):

# netstat -D

Source                         Ipkts                Opkts     Idrops     Odrops
-------------------------------------------------------------------------------
ent_dev0                    26820670             16079610          0          0
                ---------------------------------------------------------------
Devices Total               26820670             16079610          0          0
-------------------------------------------------------------------------------
ent_dd0                     26820670             16079610          0          0
                ---------------------------------------------------------------
Drivers Total               26820670             16079610          0          0
-------------------------------------------------------------------------------
ent_dmx0                    26820664                  N/A          6        N/A
                ---------------------------------------------------------------
Demuxer Total               26820664                  N/A          6        N/A
-------------------------------------------------------------------------------
IP                          50335154             44013816    1110616     147099
IPv6                            1473                 1473          0          0
TCP                         43110957             40986657       6235          0
UDP                          6105902              2045877    5022268          0
                ---------------------------------------------------------------
Protocols Total             99552013             87046350    6139119     147099
-------------------------------------------------------------------------------
en_if0                      26820664             16079528          0          0
lo_if0                      27970975             27975366       4652          0
                ---------------------------------------------------------------
Net IF Total                54791639             44054894       4652          0
-------------------------------------------------------------------------------
NFS/RPC Client                    23                  N/A          0        N/A
NFS/RPC Server                     0                  N/A          0        N/A
NFS Client                     14949                  N/A          8        N/A
NFS Server                         0                  N/A          0        N/A
                ---------------------------------------------------------------
NFS/RPC Total                    N/A                14977          8          0
-------------------------------------------------------------------------------
(Note:  N/A -> Not Applicable)
#

在 AIX 上,netstat -Zs -p tcp 命令可以用来在运行提升活动之前重置协议统计信息。

如果数据包丢弃总是超过 0.1%,那么您应该向您的网络管理员提起此事。

可以使用以下命令查看重新传输的数据包:

$ netstat -s -p tcp | grep retrans

该命令的输出中重要的统计信息如下:

  • 发送的数据包
  • 数据包
  • 重新传输的数据包
  • 接收的数据包
  • 完全重复的数据包
  • 重新传输超时

结束语

本文提供了构建、使用以及定制一个简单的实用工具以模拟在 AIX 上丢弃 TCP 数据包的参考资料和说明。当编写跨网络软件以建模它在非理想网络条件下的行为时,此类实用工具非常有用。

可以根据需要对该工具进行改编,并且可以轻松改进该工具以支持对其他可能导致性能问题(如数据包损坏、到达的数据包无序和抖动)的网络问题的模拟。


致谢

作者非常感谢帮助作者审阅本文的下列人员:

  • 美国 IBM 系统解决方案开发 IBM 系统和技术组的 Kavitha Baratakke。
  • 英国 IBM WebSphere Service Registry and Performance 的 Joshua Carr。
  • Oracle 公司的高级质量保证工程师 Sukesh Chulliyote
  • 英国 IBM 系统和技术组销售 IBM 销售和分发 Nigel Griffiths。
  • 英国 IBM WebSphere MQ Performance 的 Paul Harris。

下载

描述名字大小
Packet drop sourceaix_pdrop.tar.gz8 KB

参考资料

学习

  • ipfilter Darren Reed 编写的一个数据包筛选工具。
  • 有关 开发 AIX 内核扩展 的 IBM developerWorks 文章
  • 关注 JNI developerWorks 文章
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • AIX and UNIX 下载中心:在这里你可以下载到可以运行在 AIX 或者是 UNIX 系统上的 IBM 服务器软件以及工具,让您可以提前免费试用他们的强大功能。
  • IBM Systems Magazine for AIX 中文版:本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。IBM Systems Magazine 的内容都是由十分资深的业内人士撰写的,包括 IBM 的合作伙伴、IBM 的主机工程师以及高级管理人员。所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用 IBM 系统时有一个更好的认识。

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=932891
ArticleTitle=在 IBM AIX 上模拟丢弃的 TCP/IP 数据包
publish-date=06062013