Add --port-limit option.

By default, when sending a query via random ports to multiple upstream servers or
retrying a query dnsmasq will use a single random port for all the tries/retries.
This option allows a larger number of ports to be used, which can increase robustness
in certain network configurations. Note that increasing this to more than
two or three can have security and resource implications and should only
be done with understanding of those.
This commit is contained in:
Simon Kelley
2022-08-17 15:33:15 +01:00
parent 4447d48bb9
commit 24c3b5b3d4
4 changed files with 64 additions and 11 deletions

View File

@@ -182,6 +182,14 @@ spoofing attacks but it may be faster and use less resources. Setting this opti
to zero makes dnsmasq use a single port allocated to it by the
OS: this was the default behaviour in versions prior to 2.43.
.TP
.B --port-limit=<#ports>
By default, when sending a query via random ports to multiple upstream servers or
retrying a query dnsmasq will use a single random port for all the tries/retries.
This option allows a larger number of ports to be used, which can increase robustness
in certain network configurations. Note that increasing this to more than
two or three can have security and resource implications and should only
be done with understanding of those.
.TP
.B --min-port=<port>
Do not use ports less than that given as source for outbound DNS
queries. Dnsmasq picks random ports as source for outbound queries:

View File

@@ -1139,6 +1139,7 @@ extern struct daemon {
int log_fac; /* log facility */
char *log_file; /* optional log file */
int max_logs; /* queue limit */
int randport_limit; /* Maximum number of source ports for query. */
int cachesize, ftabsize;
int port, query_port, min_port, max_port;
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;

View File

@@ -2311,7 +2311,7 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
{
static int finger = 0;
int i, j = 0;
struct randfd_list *rfl;
struct randfd_list **up, *rfl, *found, **found_link;
struct randfd *rfd = NULL;
int fd = 0;
@@ -2319,16 +2319,26 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
if (serv->sfd)
return serv->sfd->fd;
/* existing suitable random port socket linked to this transaction? */
for (rfl = *fdlp; rfl; rfl = rfl->next)
/* existing suitable random port socket linked to this transaction?
Find the last one in the list and count how many there are. */
for (found = NULL, found_link = NULL, i = 0, up = fdlp, rfl = *fdlp; rfl; up = &rfl->next, rfl = rfl->next)
if (server_isequal(serv, rfl->rfd->serv))
return rfl->rfd->fd;
{
i++;
found = rfl;
found_link = up;
}
/* No. need new link. */
if ((rfl = daemon->rfl_spare))
daemon->rfl_spare = rfl->next;
else if (!(rfl = whine_malloc(sizeof(struct randfd_list))))
return -1;
/* We have the maximum number for this query already. Promote
the last one on the list to the head, to circulate them,
and return it. */
if (found && i >= daemon->randport_limit)
{
*found_link = found->next;
found->next = *fdlp;
*fdlp = found;
return found->rfd->fd;
}
/* limit the number of sockets we have open to avoid starvation of
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
@@ -2345,6 +2355,31 @@ 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;
else if (!(rfl = whine_malloc(sizeof(struct randfd_list))))
{
/* malloc failed, don't leak allocated sock */
if (rfd)
{
close(rfd->fd);
rfd->refcount = 0;
}
return -1;
}
/* No free ones or cannot get new socket, grab an existing one */
if (!rfd)
for (j = 0; j < daemon->numrrand; j++)

View File

@@ -181,6 +181,7 @@ struct myoption {
#define LOPT_STRIP_MAC 372
#define LOPT_CONF_OPT 373
#define LOPT_CONF_SCRIPT 374
#define LOPT_RANDPORT_LIM 375
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -366,6 +367,7 @@ static const struct myoption opts[] =
{ "log-debug", 0, 0, LOPT_LOG_DEBUG },
{ "umbrella", 2, 0, LOPT_UMBRELLA },
{ "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
{ "port-limit", 1, 0, LOPT_RANDPORT_LIM },
{ NULL, 0, 0, 0 }
};
@@ -430,6 +432,7 @@ static struct {
{ 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
{ 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
{ LOPT_RANDPORT_LIM, ARG_ONE, "#ports", gettext_noop("Set maximum number of random originating ports for a query."), NULL },
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
{ LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
@@ -3180,6 +3183,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->osport = 1;
break;
case LOPT_RANDPORT_LIM: /* --port-limit */
if (!atoi_check(arg, &daemon->randport_limit) || (daemon->randport_limit < 1))
ret_err(gen_err);
break;
case 'T': /* --local-ttl */
case LOPT_NEGTTL: /* --neg-ttl */
case LOPT_MAXTTL: /* --max-ttl */
@@ -5494,6 +5502,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_refresh = SOA_REFRESH;
daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY;
daemon->randport_limit = 1;
#ifndef NO_ID
add_txt("version.bind", "dnsmasq-" VERSION, 0 );