Bug#745775: manpages-dev: connect(2) does not document EADDRNOTAVAIL

2014-04-26 Thread Michael Kerrisk (man-pages)

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

2014-04-25 Thread Michael Kerrisk (man-pages)
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

2014-04-25 Thread William Morriss
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

2014-04-25 Thread William Morriss
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

2014-04-24 Thread William Morriss
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