AF_UNSPEC cause UDP packet losses - c

Firstly please bear me with the long question - I'm writing a UDP client-server program and odd enough I had this packet loss problem whenever I use this flag for protocol family. Symptom is packets never left the sender host (no captures from tcpdump) but sendto() returns the correct positive value, which is never bigger than 200 bytes. It took me a ridiculous amount of time to figure out this can be resolved by forcing IPv4 but I don't understand why? It's been tested numerous times with server and client running on the same system (scientific linux 6) so the common unreliable UDP transmission is probably not the cause here, nor is there any firewall rule that drops anything at all. I'm wondering if anyone happens to know the cause? Also I thought about posting some code but it doesn't seem to be necessary.. Simply put, AF_UNSPEC results in all UDP packets never leaving the sender but no error, and with AF_INET everything is perfect. This might be a weird question but any insight is appreciated. Thanks a lot!Code for server initialization:
int udp_srv_init(char * port)
{
struct addrinfo hints, *servinfo, *p;
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return EXIT_FAILURE;
}
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((self_udp = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("socket");
continue;
}
if (bind(self_udp, p->ai_addr, p->ai_addrlen) == -1) {
close(self_udp);
fprintf(stderr, "Port %s: ",port);
perror("bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind socket\n");
return EXIT_FAILURE;
}
freeaddrinfo(servinfo);
int buffsize = 65536; // I added this because I thought it was some buffer problem, but didn't change a thing
setsockopt(self_udp, SOL_SOCKET, SO_RCVBUF, (void*)&buffsize, sizeof(buffsize));
return EXIT_SUCCESS;
}
and for sending:
//...
for(i=0; i<num_neighbors; i++)
{
if(neighbors[i].cost == -1)
continue;
int bytes_sent = 0, ret;
while(bytes_sent < packet_size)
{
if(-1 == (ret = sendto(neighbors[i].skt, (void*)&my_neighs +
(ptrdiff_t)bytes_sent, packet_size-bytes_sent,
0, neighbors[i].p->ai_addr, neighbors[i].p->ai_addrlen)))
{
perror("sendto1");
return EXIT_FAILURE;
}
bytes_sent += ret;
}
}
//....
neighbors is a structure array with elements that have member fields:
struct addrinfo *servinfo, *p;
They are obtained from socket initialization, and freeaddrinfo() is only called at the very end of program execution so they are all valid.

Related

listen(): invalid argument

Trying to create a server-client application, and I'm having quite a bit of trouble setting up the connection on the server-side. After setting up the socket, and bind()ing the socket, my listen()-call fails with the error message
listen: Invalid argument
which I get from perror()-ing the case where listen() returns -1.
The synopsis of the program is the following: I use getaddrinfo() to generate a linked list of struct addrinfo's, loop through that until I find one that I can successfully create a socket with, then bind() and finally listen().
The listen() call goes as follows:
if ((status = listen(socket_fd, BACKLOG_SIZE)) == -1) {
perror("listen");
close(socket_fd);
freeaddrinfo(res);
exit(EXIT_FAILURE);
}
To be sure, I've printed the values of socket_fd and BACKLOG_SIZE, turning out to be 3 and 5, respectively. Have been debugging for hours now, and I simply cannot find out where the problem lies. Haven't found anyone with the same issue on stackOverflow, either...
Thank you in advance for any help!
Full program:
int main(int argc, char* argv[]) {
int port_no = server_usage(argc, argv);
ready_connection(port_no);
/* Synopsis:
getaddrinfo()
socket()
bind()
listen()
accept()
*/
int socket_fd = setup_socket(NULL, port_no);
struct sockaddr_storage their_addr;
socklen_t addr_size = sizeof(their_addr);
int new_fd = 0;
// Allow reuse of sockets
int activate=1;
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &activate, sizeof(int));
if ((status = bind(socket_fd, res->ai_addr, res->ai_addrlen)) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
if ((status = connect(socket_fd, res->ai_addr, res->ai_addrlen)) == -1) {
perror("connect");
close(socket_fd);
freeaddrinfo(res); // free the linked-list
exit(EXIT_FAILURE);
}
if ((status = listen(socket_fd, BACKLOG_SIZE)) == -1) {
perror("listen");
close(socket_fd);
freeaddrinfo(res); // free the linked-list
exit(EXIT_FAILURE);
}
if ((new_fd == accept(socket_fd, (struct sockaddr *)&their_addr, &addr_size)) == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
char buffer[BUFSIZE];
recv(new_fd, buffer, BUFSIZE, 0);
close(socket_fd);
close(new_fd);
freeaddrinfo(res); // free the linked-list
return 0;
}
setup_socket()-function:
int setup_socket(char* hostname, int port_no) {
// hints is mask struct, p is loop variable
struct addrinfo hints, *p;
memset(&hints, 0, sizeof hints); // make sure the struct is empty
// TODO IPv6-support?
hints.ai_family = AF_INET; // only IPv4 supported
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
char port_str[6]; // max port size is 5 digits + 0-byte
memset(port_str, 0, 6);
sprintf(port_str, "%d", port_no);
if ((status = getaddrinfo(hostname, port_str, &hints, &res)) == -1) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(EXIT_FAILURE);
}
int socket_fd = 0;
for (p = res; p != NULL; p = p->ai_next) {
if ((socket_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
}
if (socket_fd == 0) {
errno = ENOTSOCK;
perror("no socket");
exit(EXIT_FAILURE);
}
return socket_fd;
}
You cannot connect(), then listen() on the same socket. Lose the connect().

Why cannot bind to port with specified iP address, but it works well with localhost?

I am creating a server that send message to all clients if one client send message to server. I tested my server with telnet and two other clients in the in my computer. It works find if I use "localhost" as value in the first argument of function getaddrinfo. BUt when I replace it with my ip address, which I obtain by searching on google, it failed to bind socket to the port. Why that happens?
This works well when I test my telnet.
if ((rv = getaddrinfo("localhost", PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
The program fails to bind if I specified the IP addresss
Note: that I replaced my IP addresss with the "xx.xxx.xxx.xxx"
if ((rv = getaddrinfo("xx.xxx.xxx.xxx", PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
It is most unusual to want to bind to "localhost" or to your external IP address. Most computers in this day and age don't have an external IP address (the one that Google sees), because there's a router and a demilitarized zone between them and the internet. If you want to expose a service that's visible to your LAN to the internet, then you'll need to configure your router to forward connections, which is a topic that's more suitable for the networking variant of Stack Overflow.
Providing your hints.ai_flags = AI_PASSIVE, you should be able to use NULL in place of "localhost" and getaddrinfo will return a list of all suitable interfaces that you can bind on. If your hints.ai_flags doesn't contain AI_PASSIVE, then there's no guarantee that you can bind on the interfaces returned. Should you choose to use a string here, the AI_PASSIVE flag will be ignored anyway...
In your code, you're creating multiple sockets one by one, discarding the previously created as you create the next one... That's a resource leak, and possibly the cause for the problem you've noticed too. Perhaps you intended to store your multiple socket descriptors into an array, and bind them one at a time?
Don't forget to freeaddrinfo once you're done with ai. In the example below I've used PF_UNSPEC together with SOCK_STREAM to listen on both IPV4 and IPV6 interfaces (as well as any other stream types), but these can be changed to bind to other types of addresses.
#define BIND_ADDR NULL
#define BIND_PORT "1234"
struct addrinfo *ai;
int rv = getaddrinfo(BIND_ADDR,
BIND_PORT,
&(struct addrinfo){ .ai_family = PF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_PASSIVE },
&ai)
if (rv != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
size_t ai_size = 0;
for (struct addrinfo *a = ai; a != NULL; a = a->ai_next) {
ai_size++;
}
int socket[ai_size] = { 0 };
size_t x = 0;
for (struct addrinfo *a = ai; a != NULL; a = a->ai_next) {
socket[x++] = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
/* ... */
}
freeaddrinfo(ai);
/* ... */

How to use getaddrinfo to connect to a server using the external IP?

I'm writing a small C client/server application, but I cannot make the connection work when using the external IP address. The code for both client and server is taken from here, in particular the clients do:
char *default_server_name = "localhost";
char *server_name = NULL;
int nport = DEFAULT_DAMA_PORT;
char port[15];
// Parse the command line options
if (parse_options(argc, argv, &server_name, &nport) < 0) {
return -1;
}
if (server_name == NULL) {
server_name = default_server_name;
}
snprintf(port, 15, "%d", nport);
// Connect to the server
int client_socket;
struct addrinfo hints, *servinfo, *p;
int rv;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(server_name, port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(1);
}
for (p=servinfo; p != NULL; p = p->ai_next) {
if ((client_socket = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
#ifdef DEBUG
perror("socket");
#endif
continue;
}
if (connect(client_socket, p->ai_addr, p->ai_addrlen) == -1) {
close(client_socket);
#ifdef DEBUG
perror("connect");
#endif
continue;
}
// Connected succesfully!
break;
}
if (p == NULL) {
// The loop wasn't able to connect to the server
fprintf(stderr, "Couldn't connect to the server\n.");
exit(1);
}
While the server:
int nport;
char port[15];
if (parse_options(argc, argv, &nport) < 0) {
return -1;
}
snprintf(port, 15, "%d", nport);
int server_socket;
struct addrinfo hints, *servinfo, *p;
int rv;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(1);
}
for (p=servinfo; p != NULL; p = p->ai_next) {
if ((server_socket = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
#ifdef DEBUG
perror("socket");
#endif
continue;
}
if (bind(server_socket, p->ai_addr, p->ai_addrlen) == -1) {
close(server_socket);
#ifdef DEBUG
perror("bind");
#endif
continue;
}
// We binded successfully!
break;
}
if (p == NULL) {
fprintf(stderr, "failed to bind socket\n");
exit(2);
}
int pl_one, pl_two;
socklen_t pl_one_len, pl_two_len;
struct sockaddr_in pl_one_addr, pl_two_addr;
if (listen(server_socket, 2) < 0) {
fatal_error("Error in syscall listen.", 2);
}
// Get the two clients connections.
pl_one_len = sizeof(pl_one_addr);
pl_one = accept(server_socket,
(struct sockaddr *)&pl_one_addr,
&pl_one_len);
if (pl_one < 0) {
fatal_error("Error in syscall accept.", 3);
}
pl_two_len = sizeof(pl_two_addr);
pl_two = accept(server_socket,
(struct sockaddr *)&pl_two_addr,
&pl_two_len);
if (pl_two < 0) {
fatal_error("Error in syscall accept.", 3);
}
If I specify the IP of my machine in the command line, and hence the server_name in the clients is set to a string like 151.51.xxx.xxx, then the sockets cannot connect to the server. Also using 127.0.0.1 shows the same behaviour, which makes me think that when the documentation states:
The host name that you're interested in goes in the nodename
parameter. The address can be either a host name, like
"www.example.com", or an IPv4 or IPv6 address (passed as a string).
it's just joking.
Am I doing something incorrectly? Could there be some problem with firewall etc. that is preventing the clients to connect using the IP addresses?
Note: I've already searched a lot for this problem and some people say to avoid using getaddrinfo at all and directly fill with INADDR_ANY, but the getaddrinfo documentation states that passing NULL as nodename should already fill the address with INADDR_ANY, so I don't see why should I use the old method when the new one does this automatically.
As written in comments you are trying to connect to a server that is located in a network behind a router.
127.0.0.1 or localhost are redirections of the operating system and will not go over the router. That's why it worked for you.
If you specify an external ip address, the connection is made via the router and the ports that you use need to be forwarded in the router configuration. Any normal home internet end user router blocks incoming connections from any port by default.

How can I keep the kernel from sending RST packets from raw sockets on mac os x?

I am using raw sockets with TCP. Whenever a packet gets sent to me, my computer send a RST packet back. I tried http://udi.posterous.com/suppressing-tcp-rst-on-raw-sockets, which isn't working:
int main(void) {
struct addrinfo *info, hints, *p;
int status, sock;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
status = getaddrinfo(NULL, "3000", &hints, &info);
if (status != 0) function_error("getaddrinfo", 1);
for (p = info; p; p = p->ai_next) {
sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sock == -1) continue;
if (bind(sock, p->ai_addr, p->ai_addrlen) == -1) continue;
break;
}
if (p == NULL) {
fprintf(stderr, "Can't connect. Error = %d\n", errno);
return 2;
}
freeaddrinfo(info);
if (listen(sock, 10) == -1)
function_error("listen", 3);
while (1) sleep(60);
close(sock);
return 0;
}
void function_error(char *func, int code) {
fprintf(stderr, "%s error: %d\n", func, errno);
exit(code);
}
How can I get it to stop? Do I need to use that code in the same process I am using the raw socket in?

Error receiving in UDP: Connection refused

I am trying to send a string HI to a server over UDP in a particular port and then to receive a response. However, after I try to get the response using recvfrom() I was stuck in blocking state. I tried using connected UDP but I got:
Error receiving in UDP: Connection refused
What could be the reasons for this? The server is not under my control, but I do know its working fine.
I have added the code
int sockfdudp;
char bufudp[MAXDATASIZE], port[6];
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage addr;
int rv;
char s[INET6_ADDRSTRLEN];
int bytes_recv, bytes_sent;
socklen_t len;
scanf("%s",port);
printf("UDP Port: %s \n", port);
// Start connecting to datagram server
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(SERVER_NAME, port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfdudp = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("Creating datagram socket");
continue;
}
if (connect(sockfdudp, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfdudp);
perror("Connecting stream socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "ClientUDP: failed to bind socket\n");
return 2;
}
freeaddrinfo(servinfo);
if ((bytes_sent = sendto(sockfdudp, UDP_MSG, strlen(UDP_MSG), 0, p->ai_addr, p->ai_addrlen)) == -1) {
perror("ClientUDP: Error sending data");
exit(1);
}
printf("Data %s sent\n", UDP_MSG );
len = sizeof(struct sockaddr_storage);
if ((bytes_recv = recvfrom(sockfdudp, bufudp, MAXDATASIZE-1, 0,(struct sockaddr*)&addr, &len)) == -1) {
perror("Error receiving in UDP");
exit(1);
}
printf("Bytes recv %d\n", bytes_recv);
bufudp[bytes_recv] = '\0';
printf("ClientUDP: Received\n %s \n",bufudp );
close(sockfdudp);
return 0;
Chances are your're sending something to a server who does not listen on that particular port.
That would cause an icmp message to be sent back , and your next recvfrom will return an error in the case where you connect the socket.
Check with tcpdump or wireshark what's going on on the wire.
My guess would be that your ip address is bad somehow, or the port is already in use somehow. UDP is connectionless, so there really isn't any "connection" to fail.

Resources