比治山日記

比治山スカイウォーカーです

ns-3で指定したパケットをロスさせる

ns-3で実験をするときにパケットを確率的にロスさせることができますが、ロスさせたいパケットを指定して落とすこともできるとわかったのでメモしておきます。

ロスするパケットを指定するにはエラーモデルのうちReceiveListErrorModelもしくはListErrorModelを使用します。前者は受信側で指定した順番のパケットを落とすのに対して、後者はパケットのUidを指定してロストさせるモデルです。前者との違いはUidを指定するため、受信側でパケットの到着順序が前後しても落とせるという点にあるようです。今回は前者のやりかたのメモです。

ロスの指定の方法は簡単で、ReceiveListErrorModelのインスタンスに対し、ロスさせたいパケット番号が入ったリストを設定します。そうしたらそのインスタンスを設定したいNetDeviceContainerの受信側ノードの"ReceiveErrorModel"Attributeに指定するだけです。

//ロスパケットの指定
//受信側で10,100,200,300番目に到着したパケットをロストさせる
std::list<uint32_t> packetErrorList = {10, 100, 200, 300};
Ptr<ReceiveListErrorModel> em = CreateObject<ReceiveListErrorModel> ();
em->SetList (packetErrorList);
//受信側ノードにエラーモデルを設定
devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em));

2ノード間をPointToPointネットワークで接続するシンプルなモデルで、受信側でパケットをロストさせる例を以下に示します。

#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include <list>

#define PORT 10

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("ReceiveListErrorScript");

int num_pac = 0;

void inline MacRx (Ptr<const Packet> p){
  num_pac++;
};

void inline PhyRxDrop (Ptr<const Packet> p){
  std::cout << "Drop"
            << " " << num_pac++ << std::endl;
};

int main (int argc, char *argv[]){

  Time::SetResolution (Time::NS);
  LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_ERROR);
  LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_ERROR);

  NodeContainer nodes;
  nodes.Create (2);

  PointToPointHelper pointToPoint;
  pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("10Mbps"));
  pointToPoint.SetChannelAttribute ("Delay", StringValue ("10ms"));
  
  NetDeviceContainer devices;
  devices = pointToPoint.Install (nodes);
  
  //ロスパケットの指定
  std::list<uint32_t> packetErrorList = {10, 100, 200, 300};
  Ptr<ReceiveListErrorModel> em = CreateObject<ReceiveListErrorModel> ();
  em->SetList (packetErrorList);
  devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em));
  
  InternetStackHelper stack;
  stack.Install (nodes);
  
  Ipv4AddressHelper address;
  address.SetBase ("10.1.1.0", "255.255.255.0");
  Ipv4InterfaceContainer interfaces = address.Assign (devices);
  
  UdpEchoServerHelper echoServer (PORT);
  ApplicationContainer serverApps = echoServer.Install (nodes.Get (1));
  serverApps.Start (Seconds (1.0));
  serverApps.Stop (Seconds (10.0));
  
  UdpEchoClientHelper echoClient (interfaces.GetAddress (1), PORT);
  echoClient.SetAttribute ("MaxPackets", UintegerValue (1000));
  echoClient.SetAttribute ("Interval", TimeValue (MilliSeconds (10)));
  echoClient.SetAttribute ("PacketSize", UintegerValue (1024));
  
  ApplicationContainer clientApps = echoClient.Install (nodes.Get (0));
  clientApps.Start (Seconds (2.0));
  clientApps.Stop (Seconds (10.0));
  
  //パケット受信・ロスのトレース
  std::string prefix = "/NodeList/1";
  std::string _macrx = prefix + "/DeviceList/*/$ns3::PointToPointNetDevice/MacRx";
  std::string _phyrxdrop = prefix + "/DeviceList/*/$ns3::PointToPointNetDevice/PhyRxDrop";
  
  Config::ConnectWithoutContext (_macrx, MakeCallback (&MacRx));
  Config::ConnectWithoutContext (_phyrxdrop, MakeCallback (&PhyRxDrop));
  
  Simulator::Run ();
  Simulator::Destroy ();
  
  return 0;
}

実行すると指定したパケットのときに指定したコールバック関数PhyRxDropが呼ばれます。

一つのNetDeviceに対してロスモデルの併用は不可能のようなので、こういったケースを模擬したい場合にはロスパケットを指定するモードにして、併用したケースを想定したパケットロスのリストを与えるのが楽だと思います。