[Dnsmasq-discuss] Is dnsmasq supposed to listen on UDP port 0.0.0.0:67 when listen-address is specified?

Parke parke.nexus at gmail.com
Thu Dec 21 05:18:47 GMT 2017


On Wed, Dec 20, 2017 at 7:47 PM, Kurt H Maier <khm at sciops.net> wrote:
> If I understand this right, I think I can replicate it with e.g.
>
> dnsmasq --interface=eth0 \
> --dhcp-range=192.168.0.50,192.168.0.100,12h \
> --bind-interfaces \
> --listen-address=192.168.1.50

In terms of DNS on port 53:

--listen-address=192.168.1.50 will bind to 1 IPv4 address (and is
equivalent to my test.conf file).

--interface=eth0 will bind to 2 IPv4 addresses and 2 IPv6 addresses.
The second IPv4 and second IPv6 addresses are the localhost addresses.

> In this case, we can trace this to get:
> setsockopt(4, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0
> setsockopt(4, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
> setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
> bind(4, {sa_family=AF_INET, sin_port=htons(67), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
> ...
> setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth0\0\0\0\0\0\0\0\0\0\0\0\0", 16) = 0
>
> So, while SO_REUSEPORT and SO_REUSEADDR are both successful, I'm not
> able to create another socket to listen (dnsmasq fails with the address
> already in use error).

Well, it could be trying to bind twice to 192.168.1.50:53 (for DNS).
That could fail - those bindings might to be set to SO_REUSEPORT.

I am not trying to run two copies of dnsmasq.  I just noticed that
dnsmasq was binding to 0.0.0.0:67, which I thought was strange, given
that I had specified a listen address.

----

Okay, so here is the code.

http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=blob;f=src/dhcp.c;h=4a2983e79cf0143ddc0789014ba0582c1f47e6e9;hb=HEAD#l95

Interesting, I did not know about the "new" (as of Linux 3.9) option
of SO_REUSEPORT.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c617f398edd4db2b8567a28e899a88f8f574798d

>From the above kernel.org link:

----

This series implements so_reuseport (SO_REUSEPORT socket option) for
TCP and UDP.  For TCP, so_reuseport allows multiple listener sockets
to be bound to the same port.  In the case of UDP, so_reuseport allows
multiple sockets to bind to the same port.  To prevent port hijacking
all sockets bound to the same port using so_reuseport must have the
same uid.  Received packets are distributed to multiple sockets bound
to the same port using a 4-tuple hash.

[snip]

The motivating case for so_reuseport in UDP would be something like a
DNS server.  An alternative would be to recv on the same socket from
multiple threads.  As in the case of TCP, the load across these threads
tends to be disproportionate and we also see a lot of contection on
the socket lock.  Note that SO_REUSEADDR already allows multiple UDP
sockets to bind to the same port, however there is no provision to
prevent hijacking and nothing to distribute packets across all the
sockets sharing the same bound port.  This patch does not change the
semantics of SO_REUSEADDR, but provides usable functionality of it
for unicast.

----

Hmmm.

"Received [UDP] packets are distributed to multiple sockets bound to
the same port using a 4-tuple hash."

I wonder what the above means?  It sounds like load balancing, not
full delivery of each datagram to all sockets.

>From the FreeBSD man page:

https://www.freebsd.org/cgi/man.cgi?query=setsockopt

SO_REUSEADDR indicates that the rules used in validating addresses
supplied in a bind(2) system call should allow reuse of local
addresses.

SO_REUSEPORT allows completely duplicate bindings by multiple
processes if they all set SO_REUSEPORT before binding the port.  This
option permits multiple instances of a program to each receive UDP/IP
multicast or broadcast datagrams destined for the bound port.

----

Hmmm.

"This option permits multiple instances of a program to each receive
UDP/IP multicast or broadcast datagrams destined for the bound port."

But will each program receive each and every datagram?  (Which is what
I believe dnsmasq is expecting.)

In any case...

It does appear that binding to 0.0.0.0:67 is the intended behavior.
(I was unaware that multiple processes could bind to and read from the
same UDP socket.)

I suppose it is possible that binding to 0.0.0.0 is necessary to
receive DHCP packets.  I lack experience with the mechanics of DHCP,
and with UDP broadcast packets, so I cannot say.

And there is the big question of: Will each incoming UDP packet be
sent to all receiving sockets?  Or just to one out of a pool of
receiving sockets?

-Parke



More information about the Dnsmasq-discuss mailing list