NS3网络仿真(8): 实时仿真

快乐虾

http://blog.csdn.net/lights_joy/

欢迎转载,但请保留作者信息

NS3支持实时仿真,且可以将NS3模拟生成的数据包通过主机上真实的网卡发送出去,本节尝试运行NS3中自带的fd-emu-ping示例,使用NS3模拟一台设备,再通过主机上的网口ping一台网络上的真实设备。

// Allow ns-3 to ping a real host somewhere, using emulation mode
//
//   +----------------------+    
//   |          host        |
//   +----------------------+    
//   |    ns-3 simulation   |                                      
//   +----------------------+                  
//   |      ns-3 Node       |                 
//   |  +----------------+  |                 
//   |  |    ns-3 TCP    |  |              
//   |  +----------------+  |              
//   |  |    ns-3 IPv4   |  |                 
//   |  +----------------+  |                 
//   |  |   FdNetDevice  |  |                
//   |--+----------------+--+     
//   |       | eth0 |       |                
//   |       +------+       |    
//   |          |           |
//   +----------|-----------+ 
//              |
//              |         +---------+
//              .---------| GW host |--- (Internet) -----                             
//                        +---------+ 

这个模型很清楚地表达了NS3实现这个功能的层次结构。


NS3最后的实际收发包操作是由FdNetDevice这个类来完成的,其实现方式为raw socket,但在windows下,raw socket只支持ip层以上的包收发,而不支持mac层数据的收发,因此NS3的代码无法直接在windows下使用。


观察FdNetDevice完成的操作可以发现,它只需要将NS3生成的包发送出去,再将收到的包通过下面的函数扔给NS3就可以了:


FdNetDevice::ReceiveCallback (uint8_t*buf, size_t len)


因此我们引入winpcap库完成收发包的操作,放弃rawsocket方式。

首先在StartDevice时创建winpcap的接收线程:

void
FdNetDevice::StartDevice (void)
{
......

  if (m_fd < 1000)
  {
	  m_fdReader = Create();
	  // 22 bytes covers 14 bytes Ethernet header with possible 8 bytes LLC/SNAP
	  m_fdReader->SetBufferSize(m_mtu + 22);
	  m_fdReader->Start(m_fd, MakeCallback(&FdNetDevice::ReceiveCallback, this));
  }
  else
  {
	  // use pcap
	  pcap_t* ph = (pcap_t*)m_fd;
	  m_hPcapThread = CreateThread(NULL, 65536, PcapThread, this, 0, NULL);
  }

}

在此接收线程中循环调用pcap的接收函数:
DWORD WINAPI FdNetDevice::PcapThread(_In_  LPVOID lpParameter)
{
	FdNetDevice* pDev = (FdNetDevice*)lpParameter;
	pDev->m_bPcapRun = true;
	pcap_t* pc = (pcap_t*)pDev->m_fd;
	while (pDev->m_bPcapRun)
	{
		pcap_dispatch(pc, 1, (pcap_handler)packet_handler, (u_char*)pDev);
	}
	return 0;
}

然后在收到数据包后调用ReceiveCallback就可以了:
/* Callback function invoked by libpcap for every incoming packet */
void FdNetDevice::packet_handler(void *_param, const void *_header, const void *_pkt_data)
{
	const struct pcap_pkthdr *header = (const struct pcap_pkthdr *)_header;
	FdNetDevice* dev = (FdNetDevice*)_param;
	uint8_t* pkt = (uint8_t*)malloc(header->len);
	memcpy(pkt, _pkt_data, header->len);
	dev->ReceiveCallback(pkt, header->len);

}

当有数据包需要发送的时候,相应地改用pcap发送:
bool
FdNetDevice::SendFrom (Ptr packet, const Address& src, const Address& dest, uint16_t protocolNumber)
{
.....

  size_t written = 0;
  if (m_fd < 1000)
	written = cyg_write(m_fd, buffer, len);
  else
  {
	  pcap_t* ph = (pcap_t*)m_fd;
	  pcap_sendpacket(ph, buffer, len);
	  written = len;
  }
......

  return true;
}


貌似很简单的样子。


当我们在Linux下运行此示例时使用的是eth0这样的网络名称,在windows下我们需要使用一长串的名称,不过这个名称很容易用winpcap获取:

NS3网络仿真(8): 实时仿真_第1张图片


再修改一下目标机的IP,运行示例:

NS3网络仿真(8): 实时仿真_第2张图片


轻松搞定!!!










你可能感兴趣的:(通信仿真)