C - result of recvfrom unexpected - c

I have a simple code to send and receive ICMP packets like ping, everything works : my packet is sent and I received a result.
I have a problem in the recvfrom result, the ip dest/src in my buffer, isn't the server dest ip.
Result
$> ./my_ping qwant.com
IPv4: hdr-size=20 pkt-size=56 protocol=1 TTL=254 src=10.0.2.2 dest=172.17.0.2
But with the real ping:
$> ping qwant.com
PING qwant.com (194.187.168.99): 48 data bytes
56 bytes from 194.187.168.99: icmp_seq=0 ttl=61 time=101.742 ms
It's not the same TTL and ip
Code
Init struct addrinfo :
struct addrinfo hints;
struct addrinfo *a_info;
bzero(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_ADDRCONFIG;
getaddrinfo(host, NULL, &hints, &a_info)
Init socket :
sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
setsockopt(sock, SOL_IP, IP_TTL, (void *)&val, sizeof(val);
Send & Receive while :
size_t i;
t_ping pack;
int count = 1;
struct iphdr buff;
while (TRUE)
{
bzero(&pack, sizeof(pack));
pack.head.type = 8;
pack.head.code = getpid();
pack.id = count++;
while (i < 15)
{
pack.seq[i] = i + '0';
i++;
}
pack.seq[i] = 0;
pack.head.chk = checksum(&pack, sizeof(pack));
ft_bzero((void *)&buff, sizeof(buff));
if (sendto(sock, &pack, sizeof(pack), 0, a_info->ai_addr, a_info->ai_addrlen) < 0)
perror("sendto");
if (recvfrom(sock, (void *)&buff, sizeof(buff), 0, a_info->ai_addr, &a_info->ai_addrlen) < 0)
perror("recvfrom");
display((void *)&buff);
sleep(1);
}
And finally my display func :
struct iphdr *ip = buff;
char src[INET6_ADDRSTRLEN];
char dest[INET6_ADDRSTRLEN];
inet_ntop( AF_INET, (void *)&ip->saddr, src, sizeof(src) );
inet_ntop( AF_INET, (void *)&ip->daddr, dest, sizeof(dest) );
printf("IPv%d: hdr-size=%d pkt-size=%d protocol=%d TTL=%d src=%s dest=%s\n",
ip->version,
ip->ihl*4,
ntohs(ip->tot_len),
ip->protocol,
ip->ttl,
src,
dest);
In my opinion, my buffer contains information about the last packet step (router -> my home) that explains why TTL value is 254 and why I found the same couple of IP with traceroute:
$> traceroute qwant.com
traceroute to qwant.com (194.187.168.99), 30 hops max, 60 byte packets
172.17.0.1 (172.17.0.1) 0.026 ms 0.011 ms 0.010 ms
10.0.2.2 (10.0.2.2) 0.149 ms 0.160 ms 0.156 ms
[...]
194.187.168.99 (194.187.168.99) 147.634 ms 147.506 ms 147.540 ms
Why is the information received not about my server target? How can I receive this information?

Solution :
I change my getaddrinfo call and my socket init :
getaddrinfo(stats.host, NULL, NULL, &addrinfo);
sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
setsockopt(sd, SOL_IP, IP_TTL, &val, sizeof(val));
Next I have create 2 struct sockaddr_in : one for sendto and the 2nd for recvfrom.
struct sockaddr_in send;
struct sockaddr_in recv;
Now init the send struct for specify the destination at sendto, but no change recv : It was automaticly assigned during the first recvfrom call.
bzero(&send, sizeof(send));
send.sin_family = addrinfo->ai_family;
send.sin_port = 0;
send.sin_addr.s_addr = ((struct sockaddr_in *)addrinfo->ai_addr)->sin_addr.s_addr;
Finaly I have change my packet struct
struct packet
{
struct icmphdr hdr;
char msg[PACKETSIZE-sizeof(struct icmphdr)];
};
Now, just call sendto and recvfrom like that:
socklen_t len = sizeof(recv);
sendto(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&send, sizeof(send));
recvfrom(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&recv, &len);

The Internet Assigned Numbers Authority (IANA) has reserved the
following three blocks of the IP address space for private networks:
10.0.0.0 - 10.255.255.255
172.16.0.0 - 172.31.255.255
192.168.0.0 - 192.168.255.255
172.17.0.2 is an address in of a private network. I guess your machine is inside this network, and also the server.
getaddrinfo() can return you more than one address. Try to run over all the responses and check if you get also the Internet address.
from man getaddrinfo:
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully bind(2).
If socket(2) (or bind(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
close(sfd);
}

Related

Undertstanding UDP broadcast sending and receiving in C (system functions)

I'm trying to learn how sockets and networks work. For example in C in Linux. I have two simple programs. Both take ip and port as parameters. The first program is a server that broadcasts a message every second.
int main(int argc, char** argv){
struct sockaddr_in server;
if (argc < 3){ printf("\nToo few arguments. You need to pass ipv4 and port number!\n"); return 1; }
int port = -1;
if (isNumber(argv[2])) port = atoi(argv[2]);
if (port < 0){
printf("Error value for port!\n");
return 1;
}
/** prepare server */
server.sin_port = htons(port);
server.sin_family = AF_INET;
inet_aton(argv[1], &server.sin_addr);
int serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
int broadcast=1;
setsockopt(serverSocket, SOL_SOCKET, SO_BROADCAST,
&broadcast, sizeof broadcast);
bind(serverSocket, (struct sockaddr*) &server, sizeof(server));
/** Client to send */
struct sockaddr_in client;
client.sin_port = htons(port);
client.sin_family = AF_INET;
client.sin_addr.s_addr = INADDR_BROADCAST;
char buf[128] = "Test from server";
while(1){
sleep(1);
sendto(serverSocket, buf, 18, 0, (struct sockaddr*) &client, sizeof(client));
}
close(serverSocket);
return 0;
}
The second program is a simple client that receives a message, prints it out, and outputs the sender's ip.
int main(int argc, char** argv){
struct sockaddr_in currUser;
if (argc < 3){ printf("\nToo few arguments. You need to pass ipv4 and port number!\n"); return 1; }
int port = -1;
if (isNumber(argv[2])) port = atoi(argv[2]);
if (port < 0){
printf("Error value for port!\n");
return 1;
}
currUser.sin_port = htons(port);
currUser.sin_family = AF_INET;
inet_aton(argv[1], &currUser.sin_addr);
int userSocket = socket(AF_INET, SOCK_DGRAM, 0);
bind(userSocket, (struct sockaddr*) &currUser, sizeof(currUser));
/** listen */
char buf[128];
struct sockaddr_in brd;
unsigned slen = sizeof(brd);
while(1){
int readCount = recvfrom(userSocket, buf, 128, 0, (struct sockaddr *)&brd, &slen);
buf[readCount] = 0;
printf("Get %s\n", buf);
printf("Ip who send: %s\n", inet_ntoa(brd.sin_addr));
}
close(userSocket);
return 0;
}
I expect that recvfrom get the source address of the message in brd . If I send directly to the client from the server (i.e. not broadcast, but in the client parameter for sendto specifying directly ip ), it works. However, for the broadcast case, the address is different. Here is an example:
The output for $route is as follows:
Destination Gateway Genmask Flags Metric Ref Use Iface
default _gateway 0.0.0.0 UG 600 0 0 wlp3s0
link-local 0.0.0.0 255.255.0.0 U 1000 0 0 br-29e12aa6bbb8
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-29e12aa6bbb8
192.168.0.0 0.0.0.0 255.255.255.0 U 600 0 0 wlp3s0
Tell me please, I don't understand something in the operation of the recvfrom function or how the packet is transmitted over the network within these two programs?

UDP packets not delivered - recvfrom() never returns

I have setup a UDP receiver, as:
int rx_socket;
struct sockaddr_in my_addr;
struct sockaddr_in rem_addr;
socklen_t addrlen = sizeof(rem_addr);
rx_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9900);
rc = bind(rx_socket, (struct sockaddr *) &(my_addr), sizeof(my_addr));
if (!rc) {
printf("BIND SUCCESSFULL\n");
}
char buf[250];
while(1) {
printf("WAITING\n");
recvfrom(rx_socket, buf, sizeof(buf), 0, (struct sockaddr *)&rem_addr, &addrlen);
printf("RECEIVED\n");
}
The recvfrom() never returns. I have done some Wireshark analysis, and it indicates the packets are there:
Summary:
User Datagram Protocol, Src Port: 57506 (57506), Dst Port: iua (9900)
Checksum: 0x14a2 [validation disabled]
Data (8 bytes)
Any help will be appreciated.
EDIT:
An interesting observation is that the source, which is a DSP fails to send packets, i.e., sendto() returns -1, until I ping to it, from destination. Right after the ping, the source can start transmitting packets.
EDIT 2:
Here is the sender's code:
int fd;
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9900);
inet_aton("10.0.201.102", &(my_addr.sin_addr));
char buf[250];
for (;;) {
int bytesSent = sendto(fd, buf, 8, 0,
(struct sockaddr *) &(my_addr), sizeof(my_addr));
printf("sent: %d bytes\n", bytesSent);
sleep(1000);
}
So the problem turned out to be with virtualbox. My sender is on host, but receiver is on a virtual machine. If I run the receiver on the host as well, UDP packets are being received.

socket connect with timeout hangs if not connected to a network

The following routine will timeout if a non-existant ip address is given, which is great. However, if the linux system (which it is running on) is not connected to any network on either eth0 or wlan0 then it hangs. Even if a network is connected to eth0 again, still no response. Is there a way to get the timeout to apply even if not connected to a network? Alternatively, is there a check that can be done before hand that will determine if a connection to a network exists? Thanks.
int connect_to_host()
{
u_short port; /* user specified port number */
char *addr; /* will be a pointer to the address */
struct sockaddr_in address; /* the libc network address data structure */
short int sock = -1; /* file descriptor for the network socket */
fd_set fdset;
struct timeval tv;
int connected = 0;
port = 22;
addr = "192.168.2.5";
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(addr); /* assign the address */
address.sin_port = htons(port); /* translate int2port num */
sock = socket(AF_INET, SOCK_STREAM, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);
connect(sock, (struct sockaddr *)&address, sizeof(address));
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_sec = 3; /* 10 second timeout */
tv.tv_usec = 0;
if (select(sock + 1, NULL, &fdset, NULL, &tv) == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0) {
printf("%s:%d is open\n", addr, port);
connected = 1;
}
}
close(sock);
return connected;
}

sendto() has different behavior for IPv4 & IPv6 when send datagram to unreachable destination

When I use sendto() to send a datagram to a non-existing destination, I found that the result is different for IPv4 and IPv6.
IPv4:
It just returns a positive value.
IPv6:
It returns -1 with errno set to ENETUNREACH
Does anyone know why this happen?
Here's my code:
int main (int argc, char *argv[])
{
// Usage: program [version]
int version = argc == 1 ? 4 : atoi(argv[1]);
int fd = socket (AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (fd == -1)
ErrAndExit ("socket");
if (version == 4) // use ipv4
{
struct sockaddr_in srv_addr;
memset (&srv_addr, 0, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons (11111);
if (inet_pton (AF_INET, "192.168.0.200", &srv_addr.sin_addr) != 1)
ErrAndExit ("inet_pton");
socklen_t len = sizeof(srv_addr);
puts("going to sendto...");
ssize_t res = sendto (fd, "hello", 6, 0, (struct sockaddr*) &srv_addr, len);
if (res == -1)
ErrAndExit("sendto");
printf ("done with res: %ld\n", res);
}
else // use ipv6
{
struct sockaddr_in6 srv_addr;
memset (&srv_addr, 0, sizeof(srv_addr));
srv_addr.sin6_family = AF_INET6;
srv_addr.sin6_port = htons (11111);
if (inet_pton (AF_INET6, "2002::148:249", &srv_addr.sin6_addr) != 1)
ErrAndExit ("inet_pton");
socklen_t len = sizeof(srv_addr);
puts("going to sendto...");
ssize_t res = sendto (fd, "hello", 6, 0, (struct sockaddr*) &srv_addr, len);
if (res == -1)
ErrAndExit("sendto");
printf ("done with res: %ld\n", res);
}
return 0;
}
It's surprising that IPv6 can give you an error, not that IPv4 can't. IPv4 normally only delivers errors to connected UDP sockets.
This happened for me when the srv_addr.sin6_scope value was incorrect.
Some more information about using sin6_scope ID in other questions:
Adding support for IPv6 in IPv4 client/server apps - sin6_flowinfo and sin6_scope_id fields?

UDP Socket in C - Setting it up wrong?

I'm working on a project that involves sending various requests to a server through UDP. However, I seem to be setting up the socket entirely wrong, as the server does not respond to any of my requests. We were provided with a server binary to test against, and the code below ellicits no response. Am I setting up the UDP socket correctly? If so, am I somehow using sendto wrong? I have confirmed that I am sending the correct number of bits.
The input for the program is: ./client [URL] [port] [username], and I always test with ./client localhost 8080 user. Here is the struct I am sending and the code.
struct request_login {
int req_type; /* = REQ_LOGIN */
char req_username[32];
} packed;
Code:
struct sockaddr_in sa;
int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockfd == -1){
printf("Could not create socket.");
exit(EXIT_FAILURE);
}
// Prepare the socket address
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(argv[1]);
// Convert to network order
sa.sin_port = htonl(atoi(argv[2]));
// Assemble and send login request
struct request_login * reqlogin = (struct request_login *) malloc(sizeof(struct request_login));
reqlogin->req_type = REQ_LOGIN;
strcpy(reqlogin->req_username, argv[3]);
int res = sendto(sockfd, reqlogin, sizeof (struct request_login), 0, (struct sockaddr*)&sa, sizeof sa);
free(reqlogin)
Huh?
This:
sa.sin_addr.s_addr = inet_addr(argv[1]);
certainly won't do the right thing if, as you say, argv[1] is typically "localhost". You need to look up the host name, so that you get an IP address. You can only use inet_addr() if the input is a dotted IP address, not a host name.
Look at getaddrinfo().
After re-reading your code a couple of times, I think I know what one cause of the error may be:
sa.sin_port = htonl(atoi(argv[2]));
The port number is a short so you should use htons instead. It's very small and easy to miss.
try this instead:
struct addrinfo hint;
memset(&chk,0,sizeof(chk));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
struct addrinfo* servAddr = NULL;
int ret = getaddrinfo(argv[1],atoi(argv[2]),&hint,&servAddr);
if (-1 == ret)
{
perror("getaddrinfo failed");
exit(EXIT_FAILURE);
}
int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockfd == -1){
printf("Could not create socket.");
exit(EXIT_FAILURE);
}
// Assemble and send login request
struct request_login reqlogin;
reqlogin.req_type = REQ_LOGIN;
strcpy(reqlogin.req_username, argv[3]);
int res = sendto(sockfd, &reqlogin, sizeof (struct request_login), 0, servAddr->ai_addr, servAddr->ai_addrlen);

Resources