Writing a basic traceroute script in C
I have to write a trceroute script but I'm not sure if my attempts are correct.
Right now I'm doing it like that (please correct 开发者_运维百科me if I'm doing wrong or clumsy):
- Got an struct for ip- and udpheader
- A checksum function
- Opening 2 sockets: One for sending UDP-packets in SOCK_RAW mode (to manipulate ttl) and one to receive ICMP-answers from the routers.
- Using sendto() to send UDP packet
- Having no clue how to receive and process an ICMP answer
Are there any more comfortable ways to change the TTL than using sock_raw where I have to define all header stuff by myself? What parameters should I use for socket() when opening ICMP sock? How to receive the ICMP answer?
What platform are you targeting? Here's a BSD flavor from OpenBSD source:
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
err(5, "icmp socket");
if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
err(5, "raw socket");
On Linux, I believe, you need to use IP_RECVERR
and recvmsg(2)
with the MSG_ERRQUEUE
, see ip(7)
.
As far as setting the TTL is concerned, you can use setsockopt()
. Here's an extract from the iputils' source for ping
on Linux:
if (setsockopt(icmp_sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, 1) == -1) {
perror ("ping: can't set multicast time-to-live");
exit(2);
}
if (setsockopt(icmp_sock, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) == -1) {
perror ("ping: can't set unicast time-to-live");
exit(2);
}
I met the same problem and solved it. You need to
- create a new socket using ICMP protocol
- bind to a specific port like 33434
- receive ICMP reply.
I will show my code.
// ......create sending socket and fill the udp data...
// create socket to receive ICMP reply
SOCKET sock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,
WSA_FLAG_OVERLAPPED);
// from for receiving data about routing server
SOCKADDR_IN server_addr, from;
int fromlen = sizeof(from);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(33434);
// Set the receive and send timeout values to a second
timeout = 1000;
ret = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
sizeof(timeout));
if (ret == SOCKET_ERROR) {
printf("setsockopt(SO_RCVTIMEO) failed: %d\n", WSAGetLastError());
return -1;
}
timeout = 1000;
ret = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
sizeof(timeout));
if (ret == SOCKET_ERROR) {
printf("setsockopt(SO_SNDTIMEO) failed: %d\n", WSAGetLastError());
return -1;
}
// bind to the port 33434
int err = bind(sock, (SOCKADDR *)&server_addr, sizeof(SOCKADDR));
if (err != 0) {
fprintf(stderr, "bind with error: %d\n", WSAGetLastError());
return 3;
}
for (ttl = 1; ((ttl < maxhops) && (!done)); ttl++) {
int bwrote;
// Set the time to live option on the socket
set_ttl(sockRaw, ttl);
// Fill in some more data in the UDP header
((UdpHeader *)udp_data)->length = 8;
((UdpHeader *)udp_data)->dest_port = htons(33434);
((UdpHeader *)udp_data)->source_port = htons(33434);
((UdpHeader *)udp_data)->checksum =
checksum((USHORT *)udp_data, datasize);
// Send the UDP packet to the destination
bwrote = sendto(sockRaw, udp_data, datasize, 0, (SOCKADDR *)&dest,
sizeof(dest));
if (bwrote == SOCKET_ERROR) {
if (WSAGetLastError() == WSAETIMEDOUT) {
printf("%2d Send request timed out.\n", ttl);
continue;
}
printf("sendto() failed: %d\n", WSAGetLastError());
return -1;
}
// Read a packet back from the destination or a router along the way.
ret = recvfrom(sock, recvbuf, MAX_PACKET, 0, (struct sockaddr *)&from,
&fromlen);
if (ret == SOCKET_ERROR) {
if (WSAGetLastError() == WSAETIMEDOUT) {
printf("%2d Receive Request timed out.\n", ttl);
continue;
}
printf("recvfrom() failed: %d\n", WSAGetLastError());
return -1;
}
/* Decode the response to see if the ICMP response is from a router
* along the way or whether it has reached the destination. */
done = decode_resp(recvbuf, ret, &from, ttl);
Sleep(1000);
}
and it works on my computer. (Windows 10)
the result in my computer
精彩评论