Second try at port-limit option.

1) It's expected to fail to bind a new source port when they
   are scarce, suppress warning in log in this case.

2) Optimse bind_local when max_port - min_port is small. There's no
   randomness in this case, so we try all possible source ports
   rather than poking at random ones for an arbitrary number of tries.

3) In allocate_rfd() handle the case that all available source ports
   are already open. In this case we need to pick an existing
   socket/port to use, such that it has a different port from any we
   already hold. This gives the required property that the set of ports
   utilised by any given query is set by --port-limit and we don't
   re-use any until we have port-limit different ones.
This commit is contained in:
Simon Kelley
2022-09-09 17:09:32 +01:00
parent e518e87533
commit 3f56bb8ba1
4 changed files with 50 additions and 23 deletions

View File

@@ -1405,6 +1405,7 @@ void *whine_malloc(size_t size);
void *whine_realloc(void *ptr, size_t size);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
int sockaddr_isnull(const union mysockaddr *s);
int hostname_order(const char *a, const char *b);
int hostname_isequal(const char *a, const char *b);
int hostname_issubdomain(char *a, char *b);

View File

@@ -2438,6 +2438,9 @@ static int random_sock(struct server *s)
if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0))
return fd;
/* don't log errors due to running out of available ports, we handle those. */
if (!sockaddr_isnull(&s->source_addr) || errno != EADDRINUSE)
{
if (s->interface[0] == 0)
(void)prettyprint_addr(&s->source_addr, daemon->addrbuff);
else
@@ -2445,6 +2448,8 @@ static int random_sock(struct server *s)
my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"),
daemon->addrbuff, strerror(errno));
}
close(fd);
}
@@ -2518,16 +2523,6 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
break;
}
/* We've hit the global limit on sockets before hitting the query limit,
use an exsiting socket as source in that case. */
if (!rfd && found)
{
*found_link = found->next;
found->next = *fdlp;
*fdlp = found;
return found->rfd->fd;
}
/* No good existing. Need new link. */
if ((rfl = daemon->rfl_spare))
daemon->rfl_spare = rfl->next;
@@ -2551,6 +2546,14 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
if (daemon->randomsocks[i].refcount != 0 &&
server_isequal(serv, daemon->randomsocks[i].serv) &&
daemon->randomsocks[i].refcount != 0xfffe)
{
struct randfd_list *rl;
/* Don't pick one we already have. */
for (rl = *fdlp; rl; rl = rl->next)
if (rl->rfd == &daemon->randomsocks[i])
break;
if (!rl)
{
finger = i + 1;
rfd = &daemon->randomsocks[i];
@@ -2558,6 +2561,7 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
break;
}
}
}
if (!rfd) /* should be when j == daemon->numrrand */
{

View File

@@ -1371,7 +1371,7 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
or both are set. Otherwise use the OS's random ephemeral port allocation by
leaving port == 0 and tries == 1 */
ports_avail = daemon->max_port - daemon->min_port + 1;
tries = ports_avail < 30 ? 3 * ports_avail : 100;
tries = (ports_avail < 30) ? ports_avail : 100;
port = htons(daemon->min_port + (rand16() % ports_avail));
}
@@ -1400,6 +1400,15 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
if (--tries == 0)
return 0;
/* For small ranges, do a systematic search, not a random one. */
if (ports_avail < 30)
{
unsigned short hport = ntohs(port);
if (hport++ == daemon->max_port)
hport = daemon->min_port;
port = htons(hport);
}
else
port = htons(daemon->min_port + (rand16() % ports_avail));
}

View File

@@ -364,6 +364,19 @@ int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
return 0;
}
int sockaddr_isnull(const union mysockaddr *s)
{
if (s->sa.sa_family == AF_INET &&
s->in.sin_addr.s_addr == 0)
return 1;
if (s->sa.sa_family == AF_INET6 &&
IN6_IS_ADDR_UNSPECIFIED(&s->in6.sin6_addr))
return 1;
return 0;
}
int sa_len(union mysockaddr *addr)
{
#ifdef HAVE_SOCKADDR_SA_LEN