I've written my own traceroute application which sets the IP header TTL field and as it increments the TTL, I get ICMP responses back from the router hops along the way. However, I've recently found out that I need to check the packet to make sure it is indeed an icmp_type of ICMP_TTL_EXPIRE and is a response (from a router) to an ICMP_ECHO that I sent out. I always set the icmp_id field to my getpid() process id in the ICMP_ECHO packet that I send out.
When I send an ICMP_ECHO and the packet arrives at the end-station and it responds with an ICMP_ECHOREPLY , I am able to do the following:
// can't check icmp_id because it is always empty -- likewise for the sequence
The above works great and I get my response.
However, when I set the IP Header TTL field to 1, for example, my nexthop (default) router subtracts one from the TTL value, which makes the value zero, and the router generates an ICMP_TTL_EXPIRE packet. I receive these packets fine. I only check to ensure the icmp_type is set to ICMP_TTL_EXPIRE and if it does, I accept this as the response I was looking for. The reason I do this is because the icmp_id and icmp_seq fields are zero value. So I figured that the nexthop routers do not look at my icmp_id and do not put the value in their EXPIRE reply.
This was all working ok to my app got deployed a bit further and I've now found a bug. There must be a way to identify the packet as a response to my ECHO because I'm now receiving ICMP_TTL_EXPIRE from another process which is causing my application to malfunction because it counted on this being a reply from a given router so many TTL hops away.
To make a long story short, how can I determine if the reply is to my ECHO in the case of ICMP_TTL_EXPIRE replies?
I do the following to position my pointer to the ICMP packet -- again this seems to work because I get the icmp_type 11 fine and simple pings and ping responses work ok.:
numberOfBytesReceived = recvfrom(socketConnectionToHost, pingReplyBuffer,
sizeof(pingReplyBuffer), 0,
(struct sockaddr *) &remoteHost,
(unsigned int *)&sizeOfRemoteHost);
packetInterpetedAsIPPacket = (struct ip *)pingReplyBuffer;
ipHeaderLength = packetInterpetedAsIPPacket->ip_hl << 2;
icmpPacket = (struct PingICMPPacket*)(pingReplyBuffer + ipHeaderLength);
From looking at /usr/include/netinet/ip_icmp.h, it looks like the icmp structure is a union. When the response packet is an ICMP_TTL_EXPIRE, the union structure icmp_dun is filled out and the icmp_hun is completely zeroed. The icmp_dun has no seq or id that I can reference. The icmp_dun struct has an id_data array which looks to always have a value of E8.
To top it all off, when I go search on Google for ICMP_TTL_EXPIRE, all I get is a bunch of winsock stuff. Since when did windows socket and network programming become so big? Doesn't MS still live at layer two -- just joking.
Any help would be appreciated, I've spent many hours trying to figure this out.
Thanks
Dalton Hamilton