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 14 Initial 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)
errExit("bind");
len = sizeof(struct sockaddr_in);
if (getsockname(sfd, (struct sockaddr *) &svaddr, &len) == -1)
errExit("getsockname");
printf("Server port: %d\n", svaddr.sin_port);
if (listen(sfd, 50) == -1)
errExit("listen");
for (j = 0; j < limit; j++) {
cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd == -1)
errExit("socket");
if (connect(cfd, (struct sockaddr *) &svaddr, len) == -1) {
errMsg("connect");
if (argc > 2)
sleep(atoi(argv[1]));
exit(EXIT_FAILURE);
}
afd = accept(sfd, NULL, 0);
if (afd ==-1)
errExit("accept");
len = sizeof(struct sockaddr_in);
if (getsockname(cfd, (struct sockaddr *) &claddr, &len) == -1)
errExit("getsockname");
printf("Client port [%d]: %d\n", j, ntohs(claddr.sin_port));
}
if (argc > 2)
sleep(atoi(argv[2]));
exit(EXIT_SUCCESS);
}
--
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/
--
To UNSUBSCRIBE, email to [email protected]
with a subject of "unsubscribe". Trouble? Contact [email protected]