Tim Titus
asked on
Winsock can't set TOS field on ICMP packets
I'm trying to create a simple PING program in C to transmit ICMP packets with specific TOS bits set.
I followed KB248611 (http://support.microsoft.com/kb/248611) and set the registry correctly. Microsoft's "ping" program is able to set the TOS bit correctly now.
My program uses Winsock and setsockopt to set the TOS field, but the resulting packets still don't have the TOS field set (I use a sniffer to verify). Does anyone have any experience doing this with Winsock and setsockopt?
I followed KB248611 (http://support.microsoft.com/kb/248611) and set the registry correctly. Microsoft's "ping" program is able to set the TOS bit correctly now.
My program uses Winsock and setsockopt to set the TOS field, but the resulting packets still don't have the TOS field set (I use a sniffer to verify). Does anyone have any experience doing this with Winsock and setsockopt?
ASKER
I have confirmed that the registry entry is changed on my computer because the native "ping" program is now able to set the TOS field. It was unable to do this before I applied the registry change. Thus, this confirms that the registry is set correctly.
The problem is that I cannot seem to get winsock and setsockopt to work correctly.
The problem is that I cannot seem to get winsock and setsockopt to work correctly.
In the packet IP header, you will see Differentiated Service Field, but not Type Of Service.
"This design change is because the former ToS and precedence bits specified in Request For Comment (RFC) 1349 have been made obsolete by RFCs 2474 and 2475. These RFCs replaced ToS with Differentiated Services (DiffServ)."
Do you get "Differentiated Service Field"?
Do you get "Differentiated Service Field"?
ASKER
Sorry, I see TOS and DiffServe as the same field (which it is, but I used the "old" term). I want to set the DiffServe field, but have not been able to do this programatically with setsockopt. I'm interested in hearing from anyone who has used setsockopt to set the DiffServe field successfully.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ip.h:
...
#define IPTOS_TOS_MASK 0x1E
#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK)
#define IPTOS_LOWDELAY 0x10
#define IPTOS_THROUGHPUT 0x08
#define IPTOS_RELIABILITY 0x04
#define IPTOS_MINCOST 0x02
...
...
#define IPTOS_TOS_MASK 0x1E
#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK)
#define IPTOS_LOWDELAY 0x10
#define IPTOS_THROUGHPUT 0x08
#define IPTOS_RELIABILITY 0x04
#define IPTOS_MINCOST 0x02
...
I have set the DiffServ to 0x02 successfully according to the code.
Ping:
int CPing::Ping(LPCTSTR pszHostName, CPingReply& pr, int nPings, UCHAR nTTL, DWORD dwTimeout, int nPacketSize, UCHAR nTOS, BOOL bDontFragment)
{
pr.nError = -1;
pr.minRTT = ULONG_MAX;
pr.maxRTT = 0;
pr.avgRTT = 0;
//For correct operation of the T2A macro, see TN059
USES_CONVERSION;
//Use the High performace counter to get an accurate RTT
LARGE_INTEGER Frequency;
Frequency.QuadPart = 0;
if (!QueryPerformanceFrequenc y(&Frequen cy))
{
TRACE(_T("Failed to get the high performance counter frequency\n"));
return -1;
}
__int64 nTimerFrequency = Frequency.QuadPart;
//Resolve the address of the host to connect to
sockaddr_in dest;
memset(&dest,0,sizeof(dest ));
LPSTR lpszAscii = T2A((LPTSTR) pszHostName);
unsigned long addr = inet_addr(lpszAscii);
if (addr == INADDR_NONE)
{
//Not a dotted address, then do a lookup of the name
hostent* hp = gethostbyname(lpszAscii);
if (hp)
{
memcpy(&(dest.sin_addr),hp ->h_addr,h p->h_lengt h);
dest.sin_family = hp->h_addrtype;
}
else
{
TRACE(_T("CPing::PingUsing Winsock, Could not resolve the host name %s\n"), pszHostName);
CString temp;
temp.Format("Could not resolve host name \"%s\" ", pszHostName);
AfxMessageBox(temp);
return -1;
}
}
else
{
dest.sin_addr.s_addr = addr;
dest.sin_family = AF_INET;
}
//Create the raw socket
SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
if (sockRaw == INVALID_SOCKET)
{
TRACE(_T("CPing::PingUsing Winsock, Failed to create a raw socket\n"));
return -1;
}
SOCKADDR_IN sockLocalAddress;
ZeroMemory(&sockLocalAddre ss, sizeof(sockLocalAddress));
sockLocalAddress.sin_famil y = AF_INET;
sockLocalAddress.sin_port = htons((u_short)0);
LPHOSTENT lphost;
lphost = gethostbyname(NULL);
if (lphost != NULL)
sockLocalAddress.sin_addr. s_addr = ((LPIN_ADDR)lphost->h_addr )->s_addr;
else
{
return -1;
}
if (bind(sockRaw, (sockaddr*) &sockLocalAddress, sizeof(sockLocalAddress)) == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing Winsock, Failed to bind to specified address\n"));
return -1;
}
//Set the TTL on the socket
int nTempTTL = nTTL;
if (setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*) &nTempTTL, sizeof(nTempTTL)) == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing Winsock, Failed to set the TTL value on the socket\n"));
return -1;
}
//Set the TOS on the socket
int nTempTos = nTOS;
if (setsockopt(sockRaw, IPPROTO_IP, IP_TOS, (char*) &nTempTos, sizeof(nTempTos)) == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing Winsock, Failed to set the Tos value on the socket\n"));
return -1;
}
//Set the Don't Fragment flag on the socket
if (bDontFragment)
{
if (setsockopt(sockRaw, IPPROTO_IP, IP_DONTFRAGMENT, (char*) &bDontFragment, sizeof(bDontFragment)) == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing Winsock, Failed to set the Don't Fragment value on the socket\n"));
return -1;
}
}
//Allocate the ICMP packet
int nBufSize = nPacketSize + sizeof(ICMP_HEADER);
char* pICMP = new char[nBufSize];
FillIcmpData((LPICMP_HEADE R) pICMP, nBufSize);
//Get the tick count prior to sending the packet
LARGE_INTEGER TimerTick;
VERIFY(QueryPerformanceCou nter(&Time rTick));
__int64 nStartTick = TimerTick.QuadPart;
for (int i=0; i<nPings; i++)
{
//Send of the packet
int nWrote = sendto(sockRaw, pICMP, nBufSize, 0, (sockaddr*)&dest, sizeof(dest));
if (nWrote == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing Winsock, sendto failed\n"));
delete [] pICMP;
DWORD dwError = GetLastError();
closesocket(sockRaw);
SetLastError(dwError);
return -1;
}
//allocate the recv buffer
char* pRecvBuf = new char[MAX_ICMP_PACKET_SIZE] ;
BOOL bReadable;
sockaddr_in from;
int nFromlen = sizeof(from);
int nRead = 0;
//Allow the specified timeout
if (IsSocketReadible(sockRaw, dwTimeout, bReadable))
{
if (bReadable)
{
//Receive the response
nRead = recvfrom(sockRaw, pRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &nFromlen);
}
else
{
TRACE(_T("CPing::PingUsing Winsock, timeout occured while awaiting recvfrom\n"));
closesocket(sockRaw);
delete [] pICMP;
delete [] pRecvBuf;
//set the error to timed out
SetLastError(WSAETIMEDOUT) ;
return -1;
}
}
else
{
TRACE(_T("CPing::PingUsing Winsock, IsReadible call failed\n"));
delete [] pICMP;
delete [] pRecvBuf;
DWORD dwError = GetLastError();
closesocket(sockRaw);
SetLastError(dwError);
return -1;
}
//Get the current tick count
QueryPerformanceCounter(&T imerTick);
//Now check the return response from recvfrom
if (nRead == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing Winsock, recvfrom call failed\n"));
delete [] pICMP;
delete [] pRecvBuf;
DWORD dwError = GetLastError();
closesocket(sockRaw);
SetLastError(dwError);
return -1;
}
//Decode the response we got back
pr.nError = DecodeResponse(pRecvBuf, nRead, &from);
pr.Address = from.sin_addr;
ULONG rtt = (ULONG) ((TimerTick.QuadPart - nStartTick) * 1000 / nTimerFrequency);
pr.avgRTT += rtt;
if (pr.maxRTT < rtt)
pr.maxRTT = rtt;
if (pr.minRTT > rtt)
pr.minRTT = rtt;
delete [] pRecvBuf;
}
pr.avgRTT /= nPings;
//Don't forget to release out socket
closesocket(sockRaw);
//Free up the memory we allocated
delete [] pICMP;
//return the status
return pr.nError;
}
int CPing::Ping(LPCTSTR pszHostName, CPingReply& pr, int nPings, UCHAR nTTL, DWORD dwTimeout, int nPacketSize, UCHAR nTOS, BOOL bDontFragment)
{
pr.nError = -1;
pr.minRTT = ULONG_MAX;
pr.maxRTT = 0;
pr.avgRTT = 0;
//For correct operation of the T2A macro, see TN059
USES_CONVERSION;
//Use the High performace counter to get an accurate RTT
LARGE_INTEGER Frequency;
Frequency.QuadPart = 0;
if (!QueryPerformanceFrequenc
{
TRACE(_T("Failed to get the high performance counter frequency\n"));
return -1;
}
__int64 nTimerFrequency = Frequency.QuadPart;
//Resolve the address of the host to connect to
sockaddr_in dest;
memset(&dest,0,sizeof(dest
LPSTR lpszAscii = T2A((LPTSTR) pszHostName);
unsigned long addr = inet_addr(lpszAscii);
if (addr == INADDR_NONE)
{
//Not a dotted address, then do a lookup of the name
hostent* hp = gethostbyname(lpszAscii);
if (hp)
{
memcpy(&(dest.sin_addr),hp
dest.sin_family = hp->h_addrtype;
}
else
{
TRACE(_T("CPing::PingUsing
CString temp;
temp.Format("Could not resolve host name \"%s\" ", pszHostName);
AfxMessageBox(temp);
return -1;
}
}
else
{
dest.sin_addr.s_addr = addr;
dest.sin_family = AF_INET;
}
//Create the raw socket
SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
if (sockRaw == INVALID_SOCKET)
{
TRACE(_T("CPing::PingUsing
return -1;
}
SOCKADDR_IN sockLocalAddress;
ZeroMemory(&sockLocalAddre
sockLocalAddress.sin_famil
sockLocalAddress.sin_port = htons((u_short)0);
LPHOSTENT lphost;
lphost = gethostbyname(NULL);
if (lphost != NULL)
sockLocalAddress.sin_addr.
else
{
return -1;
}
if (bind(sockRaw, (sockaddr*) &sockLocalAddress, sizeof(sockLocalAddress)) == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing
return -1;
}
//Set the TTL on the socket
int nTempTTL = nTTL;
if (setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*) &nTempTTL, sizeof(nTempTTL)) == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing
return -1;
}
//Set the TOS on the socket
int nTempTos = nTOS;
if (setsockopt(sockRaw, IPPROTO_IP, IP_TOS, (char*) &nTempTos, sizeof(nTempTos)) == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing
return -1;
}
//Set the Don't Fragment flag on the socket
if (bDontFragment)
{
if (setsockopt(sockRaw, IPPROTO_IP, IP_DONTFRAGMENT, (char*) &bDontFragment, sizeof(bDontFragment)) == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing
return -1;
}
}
//Allocate the ICMP packet
int nBufSize = nPacketSize + sizeof(ICMP_HEADER);
char* pICMP = new char[nBufSize];
FillIcmpData((LPICMP_HEADE
//Get the tick count prior to sending the packet
LARGE_INTEGER TimerTick;
VERIFY(QueryPerformanceCou
__int64 nStartTick = TimerTick.QuadPart;
for (int i=0; i<nPings; i++)
{
//Send of the packet
int nWrote = sendto(sockRaw, pICMP, nBufSize, 0, (sockaddr*)&dest, sizeof(dest));
if (nWrote == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing
delete [] pICMP;
DWORD dwError = GetLastError();
closesocket(sockRaw);
SetLastError(dwError);
return -1;
}
//allocate the recv buffer
char* pRecvBuf = new char[MAX_ICMP_PACKET_SIZE]
BOOL bReadable;
sockaddr_in from;
int nFromlen = sizeof(from);
int nRead = 0;
//Allow the specified timeout
if (IsSocketReadible(sockRaw,
{
if (bReadable)
{
//Receive the response
nRead = recvfrom(sockRaw, pRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &nFromlen);
}
else
{
TRACE(_T("CPing::PingUsing
closesocket(sockRaw);
delete [] pICMP;
delete [] pRecvBuf;
//set the error to timed out
SetLastError(WSAETIMEDOUT)
return -1;
}
}
else
{
TRACE(_T("CPing::PingUsing
delete [] pICMP;
delete [] pRecvBuf;
DWORD dwError = GetLastError();
closesocket(sockRaw);
SetLastError(dwError);
return -1;
}
//Get the current tick count
QueryPerformanceCounter(&T
//Now check the return response from recvfrom
if (nRead == SOCKET_ERROR)
{
TRACE(_T("CPing::PingUsing
delete [] pICMP;
delete [] pRecvBuf;
DWORD dwError = GetLastError();
closesocket(sockRaw);
SetLastError(dwError);
return -1;
}
//Decode the response we got back
pr.nError = DecodeResponse(pRecvBuf, nRead, &from);
pr.Address = from.sin_addr;
ULONG rtt = (ULONG) ((TimerTick.QuadPart - nStartTick) * 1000 / nTimerFrequency);
pr.avgRTT += rtt;
if (pr.maxRTT < rtt)
pr.maxRTT = rtt;
if (pr.minRTT > rtt)
pr.minRTT = rtt;
delete [] pRecvBuf;
}
pr.avgRTT /= nPings;
//Don't forget to release out socket
closesocket(sockRaw);
//Free up the memory we allocated
delete [] pICMP;
//return the status
return pr.nError;
}
Forced accept.
Computer101
EE Admin
Computer101
EE Admin
Sorry I'm late on this, but FYI..
setsockopt works up to 2000.. WinXP you have to set a registry key to turn it on. Vista, this API has been removed completely.
setsockopt works up to 2000.. WinXP you have to set a registry key to turn it on. Vista, this API has been removed completely.
Make sure you restart your computer and you'd better to modify the registry in programming .
Good luck,
Charles