Part VIII
仿真
第39章 仿真
这一章主要讲述NS的仿真功能。仿真是指把模拟器连到真实的网络上。模拟器中有专门的对象,它们有能力把真实的网络数据包引入到模拟器中并能够把处理过的数据包发回到真实的网络中。
关于仿真器的注意点:
● 尽管用户不希望下面的接口描述经常地改变,但是NS的仿真功能正在不断的开发,也在不断地进行实验和改进,因此接口肯定会发生变化。
● NS的仿真功能是在FreeBSD 2.2.5上开发的,我们并没有在其它系统上进行测试。
● 由于当前仿真功能的有限的可移植性,因此仿真功能被编译到nse中(使用“make nse”编译选项),而不是标准的ns中。
39.1 概述
仿真功能能分成两种模式:
1.不透明模式 – 模拟器认为网络数据包是不透明的整体。
2. 协议模式 – 模拟器能够解释和生成网络数据包。
在不透明模式中,模拟器认为网络数据包是不可理解的。特别是,真实网络中数据包的协议字段不能被模拟器直接的操作。在不透明模式中,真实网络数据包可能被模拟器丢弃、延迟、重新排序或者复制。但是因为没有执行协议处理过程,所以一些协议相关的,操作网络数据包的操作可能不会执行。在协议模式中,模拟器能够识别并且/或者能够生成网络数据包,这些数据包能够包含任意相关的字段。直到现在(1998.3),NS只实现了不透明模式的仿真功能。
NS中一些对象的集合包括tap agents 和network objects对象集提供能了模拟器和真实网络环境之间的接口。Tap agents把真实的网络数据包插入到模拟器的模拟数据包中,或者把真实的网络数据包从模拟器的模拟数据包中提取出来。Network objects 被安装在tap agents中,它们提供发送和接收真实网络数据的入口。Tap agents和Network objects都在下面的章节进行讲述。
当NS工作在仿真模式时,使用了一个特别版本的系统调度器:实时调度器(RealTime scheduler)。这个调度器使用了和标准的基于calendar-queue的调度器相同的底层结构,但是它能够实时的执行事件。实时调度器如下所述:
39.2 实时调度器
实时调度器实现了一个调度器事件在真实时间上运行的软件调度器。假使CPU的速度足够快,能与到来的包同步,那么模拟器的虚拟时间就会与真实时间和很接近。如果模拟器太慢而落后于真实时间,模拟器的虚拟时间就会与真实时间有一个偏差。当这个偏差大于某个预先设定的常量“溢出系数(slop factor,当前是10ms)”时,模拟器就会不断地产生警告。
调度器分发事件的主循环在文件scheduler.cc中的RealTimeScheduler::run()函数里。它本质上遵循下面的算法:
_
只要模拟器没有停止就执行下面的操作:
---获得当前的真实时间(“now”)。
---分发所有时间戳在当前时间之前的未处理的模拟器事件。
---如果有下一个(将来的)事件到来,就取得该事件。
---延迟直到下一个模拟器事件准备好或有一个Tcl事件发生。
---如果有一个Tcl事件发生,重新把下一个事件插入模拟器事件队列并且回到主循环开始处继续执行。
---否则,就分发模拟器事件,回到主循环开始处继续执行。
---如果没有将来事件,就检查Tcl事件并且回到主循环开始处继续执行。
实时调度器一定要与仿真功能一起使用。否则就会导致模拟器的虚拟时间快于真实时间。在这种情况下,网络数据包通过模拟网络时,就不会被延迟适当的时间。使用实时调度器需要在仿真脚本的开始处做如下说明:
set ns [new Simulator]
$ns use-scheduler RealTime
39.3 Tap Agents
TapAgent类是一个从基类 Agent派生的简单类。这样,它就能生成模拟器的数据包,同时在NS的数据包公共头中包含任意指定的值。Tap agent 处理数据包公共头中包大小字段和包类型字段。它把插入到模拟器中的包的类型字段指定为PT_LIVE。每个Tab agent 只能具有一个相关的网络对象,然而一个模拟器的节点中却可以具有多个Tab agent对象。
Tap agent对象能够从一个相关的网络对象接收数据包,也能够把数据包发到一个相关的网络对象。假设$netobj代表一个网络对象,一个 tap agent需要使用类的network方法进行配置:
set a0 [new Agent/Tap]
$a0 network $netobj
$a0 set fid_ 26
$a0 set prio_ 2
$ns connect $a0 $a1
要注意流ID和优先级的配置是由基类Agent处理的。在数据包公共头中设置流ID的目的是在真实数据的特定流中标识出属于它的数据包。针对这些数据包能够进行特别的处理,例如丢弃或重新排序,等等。Connect 方法要求代理$a0通过模拟器的网络拓扑的当前的路由发送数据包到代理$a1。
39.4 网络对象
网络对象能够访问真实的网络。根据要求访问真实网络的协议层的不同和运行模拟器主机的操作系统所能提供的服务的不同,网络对象分为几种不同的类别。要使用网络对象,需要具有特殊访问权限,这些权限都要事先设定好。一般说来,在一个真实网络的特定的协议层(例如,链路层, IP层 ,UDP等等),网络对象提供了一个入口点和特殊的访问模式(只读,只写,可读可写)。
网络对象提供了一些特殊的功能,如:对数据包进行过滤或者以混杂模式接收网络数据包(例如:pcap/bpf网络对象)或者在一组节点间进行通信 (例如UDP/IP的多播)的功能。C++类Network是所有派生的网络对象的基类。当前NS支持三种网络对象:pcap/bpf、raw IP和UDP/IP。
39.4.1 Pcap/BPF Network Objects
这些对象提供了LBNL包捕获程序库(libpcap)(更多信息请参考ftp://ftp.ee.lbl.gov/libpcap.tar)的扩展接口。这个库能在混杂模式下从网络接口驱动程序中捕获链路层的帧(例如:使用libpcap的程序也会得到数据包,这些数据包是从网卡驱动程序所获得数据包中复制的)。它也提供了以”tcpdump”格式读写数据包跟踪文件的功能。如果网络接口驱动程序允许,NS所提供的扩展接口,就能把帧通过网络接口驱动程写到真实的网络上。系统管理员可能会限制使用这个库来捕获和生成网络数据包。如果你使用这个库,你至少必须具有对系统包过滤服务的读的权限,这些权限都要系统管理员来指定。
这个包捕获库在多个基于UNIX的平台都能很好的运行。它特别针对Berkeley 包过滤器(BPF)[26],进行了优化,同时包捕获库也提供了一个对BPF伪机器码的过滤器编译器。由于大多数系统支持BPF,一段驻留内核的BPF代码处理过滤条件,并对接收到的帧进行模式匹配。匹配过滤条件的帧通过BPF接收,不符合条件的帧不受影响。BPF 也能够发送链路层的帧。但一般不建议这么做,这是因为,一个正确初始化的帧一定要在被传递给BPF之前被创建。这就会导致无法为下一个目标地址指定正确的链路层头部信息。一般来说,用“原始IP“(raw IP)网络对象发送IP包比较好,因为系统路由功能能够为链路层头部指定正确的信息。
配置:Pcap网络对象可以被配置成与真实网络相关,或者与跟踪文件相关。如果与真实网络相关,则要指明将要用到的特定的网络接口,也要指明混杂模式标志。对所有的网络对象,可以用读或写的方式打开网络对象。例如:
set me [exec hostname]
set pf1 [new Network/Pcap/Live]
$pf1 set promisc_ true
set intf [$pf1 open readonly]
puts "pf1 configured on interface $intf"
set filt "(ip src host foobar) and (not ether broadcast)"
set nbytes [$pf1 filter $filt]
puts "filter compiled to $nbytes bytes"
puts "drops: [$pf1 pdrops], pkts: [$pf1 pkts]"
在第一行本地计算机名被取出来用作构造BPF/libpcap过滤器的条件。第二行new Network/Pcap/Live 生成了一个pcap网络对象,此对象可以捕获真实网络数据包。第三行 promisc标志告诉包过滤器把网络接口设置成混杂模式(如果底层网络接口支持混杂模式)。第四行调用网络对象$pf1的方法open激活包过滤器。open的参数可以指定为readonly、writeonly 或者 readwrite。open方法返回包过滤器相关的网络接口的名字。对open方法也能传递额外的参数(我们没有展示),指明想要使用的网络接口的名称。这在一个主机有多个接口的情况下使用。
filter方法创建一个BPF兼容的包过条件,这个条件被加载到底层的BPF模块中。filter方法返回过滤条件所使用的字节数。pdrops 和 pkts 方法用来统计包的个数。它们分别返回由于过滤器队列溢出而丢弃的包的总数和到达过滤器的数据包的总数(不是满足过滤条件的包的数量)。
39.4.2 IP 网络对象
这些对象具有IP协议的原始访问能力,并且能够处理完整规范的IP数据包 (包括数据包头)。这些对象使用raw socket来实现。在大多数的Unix系统上,要访问这些sockets,需要具有超级用户的权限。另外,raw sockets的接口不如其它类型的sockets标准。类Network/IP实现了原始IP的功能,并具有Network基类所具有的功能。所有实现更高层协议的网络对象都是从Network基类派生的。
配置:原始的IP网络对象的配置是相当简单的。它不必要和任何特定的网络接口有关联。在需要发出的数据包时,可以使用系统的IP路由功能。在这些数据包的头部中包含了目的地址的网络接口的物理地址。下面是配置一个IP对象的例子:
set ipnet [new Network/IP]
$ipnet open writeonly
...
$ipnet close
IP 网络对象只支持open 和 close方法。
39.4.3 IP/UDP网络对象
这些对象封装了系统UDP协议实现的功能,也提供了IP组播功能。这个对象正在开发中。
39.5 一个例子
下面的代码展示了一个很小但很完整的模拟脚本,在脚本中使用了BPF和IP网络对象,可以使用这个脚本来进行仿真测试。NS在一个多接口的主机上运行,它具有路由能力,它能够从一个接口读取数据包,并使这些数据包通过模拟网络,最后使用原始的IP网络对象把它们再发到真实的网络上。
set me "10.0.1.1"
set ns [new Simulator]
$ns use-scheduler RealTime
#
# we want the test machine to have ip forwarding disabled, so
# check this (this is how to do so under FreeBSD at least)
#
set ipforw [exec sysctl -n net.inet.ip.forwarding]
if $ipforw
puts "can not run with ip forwarding enabled"
exit 1
#
# allocate a BPF type network object and a raw-IP object
#
set bpf0 [new Network/Pcap/Live]
set bpf1 [new Network/Pcap/Live]
$bpf0 set promisc_ true
$bpf1 set promisc_ true
set ipnet [new Network/IP]
set nd0 [$bpf0 open readonly fxp0]
set nd1 [$bpf1 open readonly fxp1]
$ipnet open writeonly
#
# try to filter out weird stuff like netbios pkts, arp requests, dns,
# also, don’t catch stuff to/from myself or broadcasted
#
set notme "(not ip host $me)"
set notbcast "(not ether broadcast)"
set ftp "and port ftp-data"
set f0len [$bpf0 filter "(ip dst host bit) and $notme and $notbcast"]
set f1len [$bpf1 filter "(ip src host bit) and $notme and $notbcast"]
puts "filter lengths: $f0len (bpf0), $f1len (bpf1)"
puts "dev $nd0 has address [$bpf0 linkaddr]"
puts "dev $nd1 has address [$bpf1 linkaddr]"
set a0 [new Agent/Tap]
set a1 [new Agent/Tap]
set a2 [new Agent/Tap]
puts "install nets into taps..."
$a0 network $bpf0
$a1 network $bpf1
$a2 network $ipnet
set node0 [$ns node]
set node1 [$ns node]
set node2 [$ns node]
$ns simplex-link $node0 $node2 10Mb 10ms DropTail
$ns simplex-link $node1 $node2 10Mb 10ms DropTail
$ns attach-agent $node0 $a0
$ns attach-agent $node1 $a1
$ns attach-agent $node2 $a2
$ns connect $a0 $a2
$ns connect $a1 $a2
puts "okey"
$ns run
39.6 相关命令
下面是一些仿真相关的命令:
$ns_ use-scheduler RealTime
这个命令把系统的事件调度器指定为实时调度器,注意,实时调度器应该总是和仿真功能一起使用,否则就会导致模拟网络的虚拟时间比真实时间快。
set netob [new Network/<network-object-type>]
这个命令创建一个网络对象。我们使用网络对象来访问真实网络。目前NS中提供了几种网络对象,分别为:Network/Pcap/Live、Network/IP、Network/IP/UDP。关于网络对象得详细信息请参考39.4节。