Reputation: 60
I've created a ping utility on Windows. I'm using a raw socket with a ICMP protocol. I'm local administrator of my computer.
As there a lot of code, I don't want to paste it here but I found an example that is really similar to mine winsock2advancedrawsocket11bhttp://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedrawsocket11b.html
I download it, test I conclude that it has the same problem as mine. I'm not receiving response when TTL expired (in IP header). I though that using RAW socket let me read that ?
Let's say I want to force a "ttl expired" on a ping, so I send a ping to "google.com" with a ttl of 2.
ping -i 2 -n 1 google.com
This give me the following result
Reply from 204.80.6.137: TTL expired in transit.
Reply from 204.80.6.137: TTL expired in transit.
Using my application I send the same ping request and see what I received in Wireshark. I got one ICMP packet sent to google and another packet from my router telling me that TTL has expired. So, why a raw socket on Windows is not receiving this message too ? Is there an option to force reading ip header even if the TTL is expired ?
So I presume that Windows ping.exe
utility is filtering packet better/different than what we can do with the Winsock API ?
As reference, this is how the socket is created :
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mstcpip.h>
#include <windows.h>
#include <stdint.h>
#include <vector>
#include <algorithm>
struct IPV4_HDR
{
unsigned char ip_header_len : 4;
unsigned char ip_version : 4;
unsigned char ip_tos;
unsigned short ip_total_length;
unsigned short ip_id;
unsigned char ip_frag_offset : 5;
unsigned char ip_more_fragment : 1;
unsigned char ip_dont_fragment : 1;
unsigned char ip_reserved_zero : 1;
unsigned char ip_frag_offset1;
unsigned char ip_ttl;
unsigned char ip_protocol;
unsigned short ip_checksum;
unsigned int ip_srcaddr;
unsigned int ip_destaddr;
};
struct ICMP_HDR
{
BYTE type;
BYTE code;
USHORT checksum;
USHORT id;
USHORT seq;
};
unsigned short compute_checksum(unsigned short* buffer, int size)
{
unsigned long cksum = 0;
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if (size)
{
cksum += *(char*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (unsigned short)(~cksum);
}
void send_receive_ping(SOCKET icmp_sock)
{
std::vector<char> receive_buffer;
receive_buffer.resize(65536);
std::fill(receive_buffer.begin(), receive_buffer.end(), 0);
char *Buffer = receive_buffer.data();
int recv_bytes = 0;
DWORD start_time = GetTickCount();
bool first_time_in_loop = true;
do
{
if ( (first_time_in_loop == true
|| GetTickCount() - start_time > 5000))
{
OutputDebugString(L"Sending an ICMP packet....\n");
send_icmp_packet(icmp_sock);
first_time_in_loop = false;
start_time = GetTickCount();
}
recv_bytes = recvfrom(icmp_sock, Buffer, 65536, 0, 0, 0);
if (recv_bytes > 0)
{
// Handle received packet
}
else
{
break;
}
} while (recv_bytes > 0);
}
void send_icmp_packet(SOCKET icmp_sock)
{
sockaddr_in sockaddr_in_dst = {};
sockaddr_in_dst.sin_family = AF_INET;
sockaddr_in_dst.sin_port = 0;
sockaddr_in_dst.sin_addr.s_addr = inet_addr("184.150.168.247"); // google.com
std::vector<char> send_buffer;
send_buffer.resize(sizeof(IPV4_HDR) + sizeof(ICMP_HDR));
std::fill(send_buffer.begin(), send_buffer.end(), 0);
IPV4_HDR *ipv4_header = (IPV4_HDR *)send_buffer.data();
ipv4_header->ip_header_len = 5;
ipv4_header->ip_version = 4;
ipv4_header->ip_tos = 16;
ipv4_header->ip_total_length = htons( send_buffer.size() );
ipv4_header->ip_id = htons(0);
ipv4_header->ip_ttl = 64;
//ipv4_header->ip_ttl = 2;
ipv4_header->ip_protocol = IPPROTO_ICMP;
ipv4_header->ip_srcaddr = dest.sin_addr.s_addr;
ipv4_header->ip_destaddr = sockaddr_in_dst.sin_addr.s_addr;
ipv4_header->ip_checksum = compute_checksum((unsigned short *)ipv4_header, sizeof(IPV4_HDR));
static unsigned short seq = 0;
ICMP_HDR *icmp_header = (ICMP_HDR *)(send_buffer.data() + sizeof(IPV4_HDR));
icmp_header->type = 8;
icmp_header->seq = seq++;
icmp_header->id = 888;
icmp_header->checksum = compute_checksum((unsigned short *)icmp_header, sizeof(ICMP_HDR));
ret = sendto(icmp_sock, (char *)send_buffer.data(), send_buffer.size(),
0, (sockaddr *)&sockaddr_in_dst, sizeof(sockaddr_in_dst));
}
int main()
{
WSADATA ws;
WSAStartup(MAKEWORD(2, 2), &ws);
SOCKET icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
char hostname[256];
gethostname(hostname, sizeof(hostname));
hostent *local = gethostbyname(hostname);
sockaddr_in source;
memset(&source, 0, sizeof(source));
memcpy(&source.sin_addr.s_addr, local->h_addr_list[0], sizeof(source.sin_addr.s_addr));
source.sin_family = AF_INET;
source.sin_port = 0;
bind(icmp_sock, (sockaddr *)&source, sizeof(source));
int recv_all_opt = 1;
int ioctl_read = 0;
WSAIoctl(icmp_sock, SIO_RCVALL, &recv_all_opt, sizeof(recv_all_opt), 0, 0, (LPDWORD)&ioctl_read, 0, 0);
int ip_header_include = 1;
setsockopt(icmp_sock, IPPROTO_IP, IP_HDRINCL, (char *)&ip_header_include, sizeof(ip_header_include));
send_receive_ping(icmp_sock);
closesocket(icmp_sock);
WSACleanup();
return 0;
}
The preceding code seems to work pretty well but I'm still unable to get the expired TLL from IP message. It's like the OS steals the package. I bet is because I ask to read ICMP message and the TLL is in the IP header. So if the IP header is faulty than the OS discard the message and my socket can't read it. So I tried using a socket IPPROTO_IP :
sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
I'm still not getting TTL expired message, worst I'm loosing packet sometime. I saw them in Wireshark but I'm not getting them on my socket. Is someone having an idea why ?
Upvotes: 1
Views: 4929
Reputation: 1
You should simply allow ICMP in windows firewall. Create inbound rule, select ICMP in protocol and set 'allow'.
Upvotes: 0
Reputation: 236
It appears to be exactly what you suspect, Windows is diverting the TTL Exceeded responses and they don't arrive on the raw socket. I'm experiencing the same issue, and the best explanation I've found so far is this discussion of porting the MTR traceroute-style utility to Windows.
What's really sad is that IT USED TO WORK on Windows 7 back in 2012-2013 when I first started working with raw sockets in the Windows environment. A few years went by and all of a sudden the same code on the same machine no longer receives TTL Exceeded messages.
Depending on your application, you may be able to workaround by calling ICMP.DLL directly as discussed in the link above. This code from the P2PScrapper project might be a good place to start.
Upvotes: 2