愿意是模仿trace route,利用原始套接字,发送一个ICMP回显请求,随着TTL递增,获得途经路由。
但是呢,程序一直不能recvfrom到icmp差错报文,照常是会收到超时类型的差错报文的,但却始终收不到。
程序是已管理员身份运行的。
而程序中回显应答确实可以被正常recvfrom到。
也尝试用了wireshark在本地抓包,发现是有ICMP差错报文反馈回来的,并不是远程路由等的问题。
很头疼,网上也查不到类似错误,希望大家能指点一二。void ConnectToHost(char* strHost)
{

m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (m_sockRaw == INVALID_SOCKET)
{
cerr << "WSASocket() failed " << WSAGetLastError() << endl;
ExitProcess(1);
} //绑定
SOCKADDR_IN localaddr;
ZeroMemory(&localaddr, sizeof(SOCKADDR_IN));
localaddr.sin_family = AF_INET;
localaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
bind(m_sockRaw, (sockaddr*)&localaddr, sizeof(SOCKADDR_IN)); //设置套接字接收超时选项
int ret = setsockopt(m_sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&m_nTimeout, sizeof(m_nTimeout));
if (ret == SOCKET_ERROR)
{
cerr << "setsockopt(SO_RCVTIMEO) failed " << WSAGetLastError() << endl;
return;
} //设置套接字发送超时选项
m_nTimeout = 10000;
ret = setsockopt(m_sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&m_nTimeout, sizeof(m_nTimeout));
if (ret == SOCKET_ERROR)
{
cerr << "setsockopt(SO_SNDTIMEO) failed " << WSAGetLastError() << endl;
return;
} //填充目的地址
ZeroMemory(&m_addrDest, sizeof(m_addrDest));
m_addrDest.sin_family = AF_INET;
if ((m_addrDest.sin_addr.S_un.S_addr = inet_addr(strHost)) == INADDR_NONE)
{
hostent *host = NULL;
host = gethostbyname(strHost);
if (host != NULL)
{
CopyMemory(&(m_addrDest.sin_addr), host->h_addr_list[0], host->h_length);
}
else
{
cerr << "gethostbyname() failed " << WSAGetLastError() << endl;
return;
}
} //设置不路由选项
bool bopt = true;
if (setsockopt(m_sockRaw, SOL_SOCKET, SO_DONTROUTE, (char*)&bopt, sizeof(bool)) == SOCKET_ERROR)
{
cerr << "setsockopt(SO_DONTROUTE) failed " << WSAGetLastError() << endl;
return;
} //为发送缓冲区和接收缓冲区申请内存,这里确保是足够的。
m_nDatasize = DEF_PACKET_SIZE;
m_nDatasize += sizeof(IcmpHeader);
m_ICMPData = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PACKET);
m_RecvBuf = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PACKET);
if (!m_ICMPData || !m_RecvBuf)
{
cerr << "HeapAlloc() failed "  << endl;
return;
} //往发送缓冲区中填充ICMP数据
ZeroMemory(m_ICMPData, MAX_PACKET);
FillICMPData(m_ICMPData, m_nDatasize);
string str;
if (inet_addr(strHost) == INADDR_NONE)
{
str = strHost;
str += " [";
str += inet_ntoa(m_addrDest.sin_addr);
str += "]";
}
else
str = strHost;
cout << "通过最多 " << m_nMaxhops << " 个跃点跟踪到 " << str.c_str() << " 的路由:" << endl;

//递增ttl发送ICMP回显请求
for (m_nTTL = 1; m_nTTL <= m_nMaxhops && !m_bDone; ++m_nTTL)
{
int ret;
SetTTL(m_sockRaw, m_nTTL); ((IcmpHeader*)m_ICMPData)->checksum = 0;
((IcmpHeader*)m_ICMPData)->seq = m_Seqno++;
m_dwCurrentTick = GetTickCount();
((IcmpHeader*)m_ICMPData)->timestamp = m_dwCurrentTick;
((IcmpHeader*)m_ICMPData)->checksum = checksum((unsigned short*)m_ICMPData, m_nDatasize);

ret = sendto(m_sockRaw, m_ICMPData, m_nDatasize, 0, (SOCKADDR*)&m_addrDest, sizeof(SOCKADDR_IN));
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "发送超时" << endl;
continue;
}
else
{
cerr << "sendto() failed " << WSAGetLastError()<< endl;
return;
}
}
int fromlen = sizeof(SOCKADDR_IN);
ret = recvfrom(m_sockRaw, m_RecvBuf, MAX_PACKET, 0, (SOCKADDR*)&m_addrFrom, &fromlen);
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "接收超时" << endl;
continue;
}
else
{
cerr << "recvfrom() failed " << WSAGetLastError() << endl;
return;
}
}
//解析接收到的ICMP差错报文或者回显应答报文
m_bDone = DecodeRecv(m_RecvBuf, ret, &m_addrFrom, m_nTTL);
Sleep(1000);
}
}

解决方案 »

  1.   

    是接收的问题。
    我后面又尝试了用WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED)创建一个原始套接字,
    然后修改io控制WSAIoctl(RCVALL)选项。
    发现除开ICMP差错报文,其它的数据包都是可以被recvfrom到的。
    所以很困惑。
    去codeproject上找了一个源码,编译之后也是相同结果。
    你说会不会是系统限制?但是系统的tracert可以正常运行啊
      

  2.   

    想知道大家平时都是用recvfrom就可以直接接受到ICMP差错报文吗?
      

  3.   

    我和你遇到了一样的问题,抓包可以看到收到差错报文,但是socket用recvfrom方法得不到差错报文的数据包,不过我是用python来实现。
      

  4.   

    我的问题解决了,原因是windows自带的防火墙拦截了,所以python没有收到差错报文,正常的reply报文是没有拦截的,关闭windows防火墙或者放通策略就行了。