Writing a basic traceroute script in C - 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

Related

facing isue in UDP multicasting after changing ip of the device

we have a requirement for UDP multicasting in our project using Linux 4.1 kernel
with static ip address.
basic UDP multicasting using sendto function to send data is working fine with device static ip 10.13.204.100, issue comes when i change ip of the device to 10.13.204.101 or any other ip in the same series, the udp multicasting is showing an error
sendto: network unreachable
im initializing the UDP as shown below
int udp_init()
{
char multicastTTL = 10;
// Create UDP socket:
memset(&socket_desc, 0, sizeof(socket_desc));
socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (socket_desc < 0)
{
perror("socket");
return 1;
}
udp_socket_fd = socket_desc;
printf("udp_socket_fd=>%d\nsocket_desc==>%d\n", udp_socket_fd, socket_desc);
/* Set the TTL (time to live/hop count) for the send */
// if (setsockopt(socket_desc, IPPROTO_IP, IP_MULTICAST_TTL, &multicastTTL, sizeof(multicastTTL)) < 0)
if (setsockopt(socket_desc, SOL_SOCKET, SO_REUSEADDR, &multicastTTL, sizeof(multicastTTL)) < 0)
{
perror("setsockopt");
exit(1);
}
memset(&server_addr, 0, sizeof(server_addr)); /* Zero out structure */
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(EXAMPLE_GROUP); // INADDR_ANY;
server_addr.sin_port = htons(EXAMPLE_PORT); // htons(udp_port);
// bind to receive address
//
if (bind(socket_desc, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("bind");
printf("line %s-->%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
return 1;
}
}
once the ip is changed im closing the UDP socket using
close(socket_desc)
once again im using the udp_init function to initialize the UDP then im sending using sendto function to transmit the data but im get sendto:network unreachable
thanks in advance
"sendto: network unreachable" means you do not have a route to the new address, add it or change the mask for the .100 route

Transfer large packets in UDP

Hi thanks for reviewing my question, I am trying to transfer large packets using simple UDP sockets in C. OS is windows 10; IDE Visual Studio.
System build-up:
UDP client - Send 6250 packets of 8K.
UDP server - receive 8K packets count them, and print to the screen.
When I run the server and start to receive packets, I succeed to receive between 1500-2000 packets the rest are seen in WireShark but my program lost them.
I know that it can be that the sender side is writing much faster than the server reads but it looks like a large amount of packet loss.
How can I improve or change my server-side code to be able to stand the rate?
Server code:
SOCKET s;
struct sockaddr_in server, si_other;
int slen, recv_len;
uint8_t buf[BUFLEN] = {0} //8200B buffer;
WSADATA wsa;
slen = sizeof(si_other);
int countwrite = 0;
printf("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
printf("Failed. Error Code : %d", WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("Initialised.\n");
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
printf("Could not create socket : %d", WSAGetLastError());
}
printf("Socket created.\n");
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
//Bind
if (bind(s, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
{
printf("Bind failed with error code : %d", WSAGetLastError());
exit(EXIT_FAILURE);
}
puts("Bind done");
while (1)
{
printf("Waiting for data...");
fflush(stdout);
//clear the buffer by filling null, it might have previously received data
memset(buf, 0, BUFLEN);
//try to receive some data, this is a blocking call
if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr*)&si_other, &slen)) == SOCKET_ERROR)
{
printf("recvfrom() failed with error code : %d", WSAGetLastError());
exit(EXIT_FAILURE);
}
countwrite += 1;
printf("Packet Number : %d\n", countwrite);
}
Thank you for helping.

TCP server that listen on two different ports

I have a TCP server that is listening on two different ports . I created two different sockets one on port 8888 and one on port 6634. I listen on those ports and then i add the two sockets in FD_SET and pass them to select() function ...
When a socket is ready to read i check with FD_ISSET to see on which port i have message to read .
any way when i connect to port 8888 the conception is successful and i can send towards the server and receive ... when i ctrl+c the client the select function is returning again 1 and now my accept() fails ...
when i do the same thing on port 6634 everything is ok... the code stops at the select() and waits for a socket to be ready to read!
can anyone tell me WHY is this happening ?
take a look at my code in attachment
int main()
{
SOCKET conn_request_skt; /* socket where connections are accepted */
char buf[RBUFLEN], buf1[RBUFLEN]; /* reception buffer */
uint16_t lport_n, lport_h, lport_n1, lport_h1; /* port where the server listens (net/host byte ord resp.) */
int bklog = 2; /* listen backlog */
SOCKET s,s1;
int result, n;
socklen_t addrlen;
struct sockaddr_in saddr, caddr; /* server and client address structures */
int optval,childpid,i; /* flag value for setsockopt */
int connectcnt; /* number of connection requests */
fd_set readfds;
/* Initialize socket API if needed */
SockStartup();
/* input server port number */
lport_h=6634;
lport_n = htons(lport_h);
lport_h1=8888;
lport_n1 = htons(lport_h1);
/* create the socket */
printf("Creating first socket\n");
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
err_fatal("socket() failed");
printf("done, socket number %u\n",s);
/* bind the socket to any local IP address */
saddr.sin_family = AF_INET;
saddr.sin_port = lport_n;
saddr.sin_addr.s_addr = INADDR_ANY;
showAddr("Binding to address first socket", &saddr);
result = bind(s, (struct sockaddr *) &saddr, sizeof(saddr));
if (result == -1)
err_fatal("bind() failed");
printf("done.\n");
printf("Creating second socket\n");
s1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s1 == INVALID_SOCKET)
err_fatal("socket() failed");
printf("done, socket number %u\n",s1);
/* bind the socket to any local IP address */
saddr.sin_port=lport_n1;
showAddr("Binding to address second socket", &saddr);
result = bind(s1, (struct sockaddr *) &saddr, sizeof(saddr));
if (result == -1)
err_fatal("bind() failed");
printf("done.\n");
/* listen */
printf ("Listening at socket %d with backlog = %d \n",s,bklog);
result = listen(s, bklog);
if (result == -1)
err_fatal("listen() failed");
printf("done.\n");
printf ("Listening at socket %d with backlog = %d \n",s1,bklog);
result = listen(s1, bklog);
if (result == -1)
err_fatal("listen() failed");
printf("done.\n");
for (;;)
{
FD_ZERO(&readfds); /* initialize the fd set */
FD_SET(s, &readfds);
FD_SET(s1, &readfds); /* add socket fd */
printf("here \n");
printf("result bifore select is %d \n", result);
result=select(s1+1, &readfds, 0, 0, 0);
printf("result after select is %d \n", result);
if(result<0)
{
err_fatal("select() failed");
}
if(result>0)
{
if(FD_ISSET(s,&readfds))
{
conn_request_skt=s;
/* accept next connection */
addrlen = sizeof(struct sockaddr_in);
s = accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
if (s == INVALID_SOCKET)
err_fatal("accept() failed");
showAddr("Accepted connection from", &caddr);
printf("new socket: %u\n",s);
/* serve the client on socket s */
for (;;)
{
n=recv(s, buf, RBUFLEN-1, 0);
if (n < 0)
{
printf("Read error\n");
closesocket(s);
printf("Socket %d closed\n", s);
break;
}
else if (n==0)
{
printf("Connection closed by party on socket %d\n",s);
//closesocket(s);
break;
}
else
{
printf("Received line from socket %03d :\n", s);
buf[n]=0;
printf("[%s]\n",buf);
if(writen(s, buf, n) != n)
printf("Write error while replying\n");
else
printf("Reply sent\n");
}
}
}
if(FD_ISSET(s1,&readfds))
{
conn_request_skt=s1;
/* accept next connection */
addrlen = sizeof(struct sockaddr_in);
printf("bifore accept! \n");
s1 = accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
if (s1 == INVALID_SOCKET)
err_fatal("accept() failed");
showAddr("Accepted connection from", &caddr);
printf("new socket: %u\n",s1);
/* serve the client on socket s */
for (;;)
{
n=recv(s1, buf, RBUFLEN-1, 0);
if (n < 0)
{
printf("Read error\n");
closesocket(s1);
printf("Socket %d closed\n", s1);
break;
}
else if (n==0)
{
printf("Connection closed by party on socket %d\n",s1);
//closesocket(s);
break;
}
else
{
printf("Received line from socket %03d :\n", s1);
buf[n]=0;
printf("[%s]\n",buf);
if(writen(s1, buf, n) != n)
printf("Write error while replying\n");
else
printf("Reply sent\n");
}
}
}
}
}
}
The first listener socket is created with:
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
And then the data socket is accepted with:
conn_request_skt=s;
s = accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
See? The next loop, when you are going to select over the listener socket, s no longer holds that socket, but the (closed) data socket.
The solution is to use different variables for the listener socket and the data socket (conn_request_skt is just obfuscating the issue).
You are overwriting your socket variable s1 with the result of the accept() call. So s1 contains now the descriptor of the socket you are actually reading from. Then you close that socket. But in the next pass of the main loop, you check for readability on that (now closed) descriptor, which does not work.
I believe it would be better not to reuse variables in this case. Use a new variable for the actual connection socket, so that you retain the original listening socket in s1.

Multiple servers with single client

Scenerio: several servers are listening, and a single client will send a UDP broadcast to all machines on the network and the servers will reply back. (goal: get all the ip addresses of the servers)
Here is the client code:
int main()
{
struct sockaddr_in connectedSocket;
int length=sizeof(connectedSocket);
int iResult = 0, iOptVal = 0, nOptiontValue = 1;
SOCKET Socket;
char receiveBuffer[1000];
char message[1000];
//Clear the buffer by filling null, it might have previously received data
memset(receiveBuffer,'\0', 1000);
WSADATA wsa;
//Initialise winsock
printf("\nInitialising Winsock...\n");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("\nFailed. Error Code : %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("\n.........Initialised.\n");
//Create socket
Socket = socket(AF_INET, SOCK_DGRAM, 0);
if (Socket == SOCKET_ERROR)
{
printf("Create a UDP socket failed with error = %d\n" , WSAGetLastError());
exit(EXIT_FAILURE);
}
//Set socket options to broadcast
iResult = setsockopt(Socket, SOL_SOCKET, SO_BROADCAST,(char *) &iOptVal, sizeof (iOptVal));
if(iResult == SOCKET_ERROR)
{
printf("Set socket options failed with error = %d\n" , WSAGetLastError());
exit(EXIT_FAILURE);
}
//Setup address structure
memset((char *) &connectedSocket, 0, sizeof(connectedSocket));
connectedSocket.sin_family = AF_INET;
connectedSocket.sin_port = htons(PORT);
connectedSocket.sin_addr.s_addr = INADDR_BROADCAST;
while(1)
{
printf("\n\n\nEnter message : ");
gets(message);
//send the message
if (sendto(Socket, message,sizeof(message) , 0 , (struct sockaddr *) &connectedSocket, sizeof(connectedSocket)) == SOCKET_ERROR)
{
printf("\nsendto() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("\nMessage Successfully sent to Server");
// fflush(stdout);
if (recvfrom(Socket, receiveBuffer, 1000, 0, (struct sockaddr *) &connectedSocket,&length) == SOCKET_ERROR)
{
printf("\nrecvfrom() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("\nServer Says : ");
printf(receiveBuffer,sizeof(receiveBuffer));
}
closesocket(Socket);
WSACleanup();
return 0;
}
When I run this, I get
sendto() failed with error code : 10013
I look up the winsock error and it says
An attempt was made to access a socket in a way forbidden by its access permissions. An example is using a broadcast address for sendto without broadcast permission being set using setsockopt(SO_BROADCAST).
But I am setting the sockopt to SO_BROADCAST. Can anyone tell me why this is happening?
The relevant parts of the code you posted are here:
int iResult = 0, iOptVal = 0, nOptiontValue = 1;
...
//Set socket options to broadcast
iResult = setsockopt(Socket, SOL_SOCKET, SO_BROADCAST,(char *) &iOptVal, sizeof (iOptVal));
if(iResult == SOCKET_ERROR)
{
printf("Set socket options failed with error = %d\n" , WSAGetLastError());
exit(EXIT_FAILURE);
}
Note that the variable iOptVal has been initialized 0. It's not modified anywhere else. Then you pass that variable to the setsockopt() function.
This sets the SO_BROADCAST option to FALSE. (Which is the default value, so actually it doesn't change the value).
It's not sufficient to set it. You have to set it to the correct value. Which would be TRUE.
You can do this by initializing the variable to 1 instead of 0.

socket is set with O_NONBLOCK, but it seems the calling thread still gets into sleep when sending data sometimes

Client
In fact, my client doesn't recv and process data send from server, just connects to my server.
int netif_msg_client_socket_create(char *sockpath)
{
int addrlen, retval;
int sockfd;
struct sockaddr_un serv;
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0) {
PR_ERROR(NETIF_MSG_M, " fatal failure, client msg socket, error is %s, %s %u\n", strerror(errno), __FILE__, __LINE__);
return -1;
}
/* Make client socket. */
memset (&serv, 0, sizeof (struct sockaddr_un));
serv.sun_family = AF_UNIX;
strncpy (serv.sun_path, sockpath, strlen(sockpath));
addrlen = sizeof (serv.sun_family) + strlen(serv.sun_path);
retval = connect(sockfd, (struct sockaddr*)&serv, addrlen);
if(retval < 0)
{
PR_ERROR(NETIF_MSG_M, " fatal failure, client msg connect, error is %s, %s %u\n", strerror(errno), __FILE__, __LINE__);
close(sockfd);
return -1;
}
fcntl(sockfd, F_SETFL, O_NONBLOCK);
return sockfd;
}
2.Server
But my server will try to send some data to the client continuously.
int netif_msg_server_socket_create(char *sockpath)
{
int addrlen, retval;
int sockfd;
struct sockaddr_un serv;
/* First of all, unlink existing socket */
unlink (sockpath);
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0)
return -1;
fcntl(sockfd, F_SETFL, O_NONBLOCK);
/* Make server socket. */
memset (&serv, 0, sizeof (struct sockaddr_un));
serv.sun_family = AF_UNIX;
strncpy (serv.sun_path, sockpath, sizeof(serv.sun_path)-1);
addrlen = sizeof (serv.sun_family) + strlen(serv.sun_path);
//printf("sizeof(serv) == %d, addrlen == %d.\r\n", sizeof(serv), addrlen);
retval = bind (sockfd, (struct sockaddr *) &serv, addrlen);
if (retval < 0)
{
close (sockfd); /* Avoid sd leak. */
return -1;
}
retval = listen (sockfd, 20);
if (retval < 0)
{
close (sockfd); /* Avoid sd leak. */
return -1;
}
return sockfd;
}
My server uses select and accepts the connection from my client successfully.
After my server sent 412 packets(96 Bytes each), it seems the server sleeps on send.
Key codes:
printf("Try to send packet(%d bytes) to clientfd %d.\n", MSGCB_DLEN(msgcb), client->acpt_fd);
retval = send(client->acpt_fd, msgcb->data_ptr, MSGCB_DLEN(msgcb), 0);
if(retval != MSGCB_DLEN(msgcb))
{
printf("Send netif notify msg failed[%d].\n", retval);
} else {
printf("Send netif notify msg succeeded.\n");
}
After 412 packets sent to my client and "Try to ..." outputed, nothing goes on, neither "...failed" nor "...succeeded" outputs.
I use getsockopt to fetch the SO_RCVBUF and SO_SNDBUF, there are about 100000Bytes for each of them.
I don't know why, need your help, thanks!
If you want the server socket that is connected to the client to be non-blocking, then you must specifically set the new socket that is returned from accept() to be non-blocking. Your code only sets the listening socket to non-blocking.
You can perform non-blocking I/O with send using the MSG_DONTWAIT flag in the last parameter.
retval = send(client->acpt_fd, msgcb->data_ptr, MSGCB_DLEN(msgcb),
MSG_DONTWAIT);
When performing non-blocking I/O, you need to detect when the return value is signalling you to retry the operation.
if (retval < 0) {
if (errno == EAGAIN) {
/* ... handle retry of send laster when it is ready ... */
} else {
/* ... other error value cases */
}
}

Resources