快乐虾
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获取:
再修改一下目标机的IP,运行示例:
轻松搞定!!!