Bug#745775: manpages-dev: connect(2) does not document EADDRNOTAVAIL
On 04/25/2014 11:02 PM, William Morriss wrote: Okay I was able to do it without fork. This one been tested on Arch Linux and Ubuntu. It's simpler I think and also won't give the bind error. It's below and attached. William, Thanks for that. I've got it now, I think. The point is I think that you are running out of ephemeral ports on the system. An ephemeral port is a random port number that an Internet domain socket is assigned during any of the following operations: * bind(), specifying a port number of 0 * listen() on an unbound socket * connect() on an unbound socket * sendto() on an unbound socket The point is that port numbers are 16 bits long, and the ephemeral port numbers that are used by unprivileged processes is restricted to the range given in /proc/sys/net/ipv4/ip_local_port_range (see the ip(7) man page). I've appended an even simpler program below that I think produces the error for the same reason as your program. (You may need to run as root and up your open files limit (ulimit -n) when testing this.) You might want to confirm that it's doing something equivalent your program, in terms of triggering the error. Funnily enough, someone asked me the other day what happens if you run out of ephemeral ports, and I replied that the relevant system call would get an error. The next question was: what error? I said I didn't know (was a way from a machine, so could not check the man page). And now we see that the error is not in a man page. However, things are sadly a little worse than I expected. The various cases above give different errors if the ephemeral port range is exhausted: * listen() and bind() give EADDRINUSE * connect() gives EADDRNOTAVAIL * sendto() gives EAGAIN I will add this text to connect() EADDRNOTAVAIL (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers in the ephemeral port range are currently in use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). and similar text to the other pages. And I've updated the text in ip(7) to say ip_local_port_range (since Linux 2.2) This file contains two integers that define the default local port range allocated to sockets that are not explicitly bound to a port number—that is, the range used for ephemeral ports. An ephemeral port is allo‐ cated to a socket in the following circumstances: * the port number in a socket address is specified as 0 when calling bind(2); * listen(2) is called on a stream socket that was not previously bound; * connect(2) was called on a socket that was not not previously bound; * sendto(2) is called on a datagram socket that was not not previously bound. Allocation of ephemeral ports starts with the first num‐ ber in ip_local_port_range and ends with the second num‐ ber. If the range of ephemeral ports is exhausted, then the relevant system call returns an error (but see BUGS) Note that the port range in ip_local_port_range should not conflict with the ports used by masquerading (although the case is handled). Also, arbitrary choices may cause problems with some firewall packet filters that make assumptions about the local ports in use. The first number should be at least greater than 1024, or better, greater than 4096, to avoid clashes with well known ports and to minimize firewall problems. Thanks for the report. Cheers, Michael /*#* consume_ephemeral_ports_connect.c Determine what error is generated when connect() is unable to obtain an ephemeral port. Usage: ./consume_ephemeral_ports_connect [num-loops [sleep-secs]] */ /*#** Change history Apr 14Initial creation */ #include netinet/in.h #include arpa/inet.h #include sys/socket.h #include sys/types.h #include stdio.h #include stdlib.h #include unistd.h #include string.h #include limits.h #define errMsg(msg) do { perror(msg); } while (0) #define errExit(msg)do { perror(msg); exit(EXIT_FAILURE); \ } while (0) int main(int argc, char *argv[]) { struct sockaddr_in svaddr, claddr; int sfd, cfd, afd, limit, j; socklen_t len; limit = (argc 1) ? atoi(argv[1]) : INT_MAX; sfd = socket(AF_INET, SOCK_STREAM, 0); if (sfd == -1) errExit(socket); memset(svaddr, 0, sizeof(struct sockaddr_in)); svaddr.sin_family = AF_INET; svaddr.sin_addr.s_addr = htonl(INADDR_ANY); svaddr.sin_port = 0; if (bind(sfd, (struct sockaddr *) svaddr, sizeof(struct sockaddr_in)) == -1)
Bug#745775: manpages-dev: connect(2) does not document EADDRNOTAVAIL
William, this report is a little vague... Do you have a *minimal* program that demonstrates the problem? -- To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org with a subject of unsubscribe. Trouble? Contact listmas...@lists.debian.org
Bug#745775: manpages-dev: connect(2) does not document EADDRNOTAVAIL
Due to the complexity of this situation I have to reproduce this is the smallest program I could write that produces connect: cannot assign requested address. The first run on my machine, it counts to ~220 and then fails with errno 99 on all of the client processes. It then fails immediately if I run it again, but if you wait you'll have to run it again. Sometimes it fails with bind: Address already in use followed by connect: Connection refused from all of the clients; this happens non-deterministically. Please don't judge this program too harshly because it serves only to get connect to give that errno. It requires netstat, which is in package net-tools. The program is below as well as attached. #include arpa/inet.h #include assert.h #include errno.h #include signal.h #include stdio.h #include stdlib.h #include string.h #include sys/socket.h #include sys/types.h #include unistd.h const int PORTNUM = 2423; const int BACKLOG = 128; int in_socketfd; void handle(int interrupt) { sched_yield(); close(in_socketfd); exit(0); } void server() { // on SIGINT, exit cleanly struct sigaction term_act; term_act.sa_handler = handle; sigemptyset(term_act.sa_mask); term_act.sa_flags = 0; if (sigaction(SIGINT, term_act, NULL)) { perror(sigaction); exit(errno); } in_socketfd = socket(AF_INET, SOCK_STREAM, 0); if (in_socketfd == -1) { perror(socket); exit(errno); } struct sockaddr_in in_addr; memset(in_addr,0,sizeof(in_addr)); in_addr.sin_family = AF_INET; in_addr.sin_addr.s_addr = htonl(INADDR_ANY); in_addr.sin_port = htons(PORTNUM); if (bind(in_socketfd, (struct sockaddr*)in_addr, sizeof(struct sockaddr_in))) { perror(bind); exit(errno); }; if (listen(in_socketfd,BACKLOG)) { perror(listen); exit(errno); } while (1) { struct sockaddr_in dest; socklen_t dest_size = sizeof(struct sockaddr_in); int socketfd = accept(in_socketfd, (struct sockaddr*) dest, dest_size); if (socketfd == -1) { perror(accept); exit(errno); } char placeholder[15]; assert(recv(socketfd, placeholder, 15, 0) == 0); // close(socketfd); } } int client() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror(socket); return errno; } struct sockaddr_in dest; memset(dest,0,sizeof(dest)); dest.sin_family = AF_INET; dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK); dest.sin_port = htons(PORTNUM); if (connect(sockfd,(struct sockaddr*)dest, sizeof(struct sockaddr_in)) == -1) { perror(connect); return errno; } close(sockfd); return EXIT_SUCCESS; } #define fork_split() \ do { \ pid_t pid = fork(); #define fork_join() \ if (pid) { \ int child_ret; \ waitpid(pid, child_ret, 0); \ ret = ret || child_ret; \ } else { \ exit(ret); \ } \ } while (0); int test() { int ret = EXIT_SUCCESS; int spid = fork(); if (spid) { // parent : clients // wait for server to be listening while (WEXITSTATUS(system(netstat -tapen 2/dev/null | grep ':2423' /dev/null)) == 1) { sched_yield(); } fork_split(); fork_split(); fork_split(); fork_split(); fork_split(); fork_split(); fork_split(); ret = client(); fork_join(); fork_join(); fork_join(); fork_join(); fork_join(); fork_join(); fork_join(); kill(spid, SIGINT); int s_ret; waitpid(spid, s_ret,0); return s_ret || ret; } else { // child : server server(); } return ret; } int main() { int i = 0; while (test() == EXIT_SUCCESS) { printf(%i\n,i++); } } On Fri, Apr 25, 2014 at 8:43 AM, Michael Kerrisk (man-pages) mtk.manpa...@gmail.com wrote: William, this report is a little vague... Do you have a *minimal* program that demonstrates the problem? #include arpa/inet.h #include assert.h #include errno.h #include signal.h #include stdio.h #include stdlib.h #include string.h #include sys/socket.h #include sys/types.h #include unistd.h const int PORTNUM = 2423; const int BACKLOG = 128; int in_socketfd; void handle(int interrupt) { sched_yield(); close(in_socketfd); exit(0); } void server() { // on SIGINT, exit cleanly struct sigaction term_act; term_act.sa_handler = handle; sigemptyset(term_act.sa_mask); term_act.sa_flags = 0; if (sigaction(SIGINT, term_act, NULL)) { perror(sigaction); exit(errno); } in_socketfd = socket(AF_INET, SOCK_STREAM, 0); if (in_socketfd == -1) { perror(socket); exit(errno); } struct sockaddr_in in_addr;
Bug#745775: manpages-dev: connect(2) does not document EADDRNOTAVAIL
Okay I was able to do it without fork. This one been tested on Arch Linux and Ubuntu. It's simpler I think and also won't give the bind error. It's below and attached. William Morriss #include arpa/inet.h #include assert.h #include errno.h #include signal.h #include stdio.h #include stdlib.h #include string.h #include sys/socket.h #include sys/types.h const int PORTNUM = 2423; const int BACKLOG = 16; int in_socketfd; void handle(int interrupt) { sched_yield(); close(in_socketfd); exit(0); } void server() { // on SIGINT, exit cleanly struct sigaction term_act; term_act.sa_handler = handle; sigemptyset(term_act.sa_mask); term_act.sa_flags = 0; if (sigaction(SIGINT, term_act, NULL)) { perror(sigaction); exit(errno); } in_socketfd = socket(AF_INET, SOCK_STREAM, 0); if (in_socketfd == -1) { perror(socket); exit(errno); } struct sockaddr_in in_addr; memset(in_addr,0,sizeof(in_addr)); in_addr.sin_family = AF_INET; in_addr.sin_addr.s_addr = htonl(INADDR_ANY); in_addr.sin_port = htons(PORTNUM); if (bind(in_socketfd, (struct sockaddr*)in_addr, sizeof(struct sockaddr_in))) { perror(bind); exit(errno); }; if (listen(in_socketfd,BACKLOG)) { perror(listen); exit(errno); } while (1) { struct sockaddr_in dest; socklen_t dest_size = sizeof(struct sockaddr_in); int socketfd = accept(in_socketfd, (struct sockaddr*) dest, dest_size); if (socketfd == -1) { perror(accept); exit(errno); } char placeholder[15]; assert(recv(socketfd, placeholder, 15, 0) == 0); close(socketfd); } } int client() { int i; for (i = 0; i 5; i++) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror(socket); return errno; } struct sockaddr_in dest; memset(dest,0,sizeof(dest)); dest.sin_family = AF_INET; dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK); dest.sin_port = htons(PORTNUM); if (connect(sockfd,(struct sockaddr*)dest, sizeof(struct sockaddr_in)) == -1) { perror(connect); return errno; } close(sockfd); } return EXIT_SUCCESS; } int test() { int ret = EXIT_SUCCESS; int spid = fork(); if (spid) { // parent : clients // wait for server to be listening while (WEXITSTATUS(system(netstat -tapen 2/dev/null | grep ':2423' /dev/null)) == 1) { sched_yield(); } ret = client(); kill(spid, SIGINT); int s_ret; waitpid(spid, s_ret,0); return s_ret || ret; } else { // child : server server(); } return ret; } int main() { int i = 0; while (test() == EXIT_SUCCESS) { printf(%i\n,i++); } } On Fri, Apr 25, 2014 at 8:43 AM, Michael Kerrisk (man-pages) mtk.manpa...@gmail.com wrote: William, this report is a little vague... Do you have a *minimal* program that demonstrates the problem? #include arpa/inet.h #include assert.h #include errno.h #include signal.h #include stdio.h #include stdlib.h #include string.h #include sys/socket.h #include sys/types.h const int PORTNUM = 2423; const int BACKLOG = 16; int in_socketfd; void handle(int interrupt) { sched_yield(); close(in_socketfd); exit(0); } void server() { // on SIGINT, exit cleanly struct sigaction term_act; term_act.sa_handler = handle; sigemptyset(term_act.sa_mask); term_act.sa_flags = 0; if (sigaction(SIGINT, term_act, NULL)) { perror(sigaction); exit(errno); } in_socketfd = socket(AF_INET, SOCK_STREAM, 0); if (in_socketfd == -1) { perror(socket); exit(errno); } struct sockaddr_in in_addr; memset(in_addr,0,sizeof(in_addr)); in_addr.sin_family = AF_INET; in_addr.sin_addr.s_addr = htonl(INADDR_ANY); in_addr.sin_port = htons(PORTNUM); if (bind(in_socketfd, (struct sockaddr*)in_addr, sizeof(struct sockaddr_in))) { perror(bind); exit(errno); }; if (listen(in_socketfd,BACKLOG)) { perror(listen); exit(errno); } while (1) { struct sockaddr_in dest; socklen_t dest_size = sizeof(struct sockaddr_in); int socketfd = accept(in_socketfd, (struct sockaddr*) dest, dest_size); if (socketfd == -1) { perror(accept); exit(errno); } char placeholder[15]; assert(recv(socketfd, placeholder, 15, 0) == 0); close(socketfd); } } int client() { int i; for (i = 0; i 5; i++) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror(socket); return errno; } struct sockaddr_in dest;
Bug#745775: manpages-dev: connect(2) does not document EADDRNOTAVAIL
Package: manpages-dev Version: 3.40-0 Severity: normal After spawning a bunch (500) of processes to connect and close connections to the same port on my local machine, connect returned -1 with errno=EADDRNOTAVAIL, which is not described in the manpage connect(2). EADDRNOTAVAIL is defined in /usr/include/asm-generic/errno.h I suspect this is an upstream problem because this is not communicated in Arch Linux's manpage either, and I have also produced the errno there. $ uname -a Linux L1 3.5.0-49-generic #73-Ubuntu SMP Tue Apr 1 18:32:29 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux