2

I have two DHCP servers, running on different two interfaces. (Only eth0/10.0.0.1 is using dnsmasq.)

According to the manpage, the following options should make it listen on only 10.0.0.1, for both DNS and DHCP:

dnsmasq --keep-in-foreground --pid-file=/data/dnsmasq.pid --server=172.31.139.16 \
--server=172.30.139.16 --bind-interfaces --except-interface=wlan0 --except-interface=lo \
--except-interface=wwan0 --dhcp-range=10.0.0.100,10.0.0.109 --log-dhcp --dhcp-authoritative \
--listen-address=10.0.0.1

The arguments above result in the expected behaviour for DNS, but DHCP is incorrectly bound to 0.0.0.0:67.

smarc_mx8mq:/ # netstat -lnup                                                                                                                                                                                                                         
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program Name
udp        0      0 10.0.0.1:53             0.0.0.0:*                           5167/dnsmasq
udp        0      0 0.0.0.0:67              0.0.0.0:*                           5167/dnsmasq

I have tried several variations on these arguments, but DHCP always listens on 0.0.0.0:67.


Update:

strace shows port 67 being opened on address 0.0.0.0:

setsockopt(4, SOL_IP, IP_MTU_DISCOVER, [0], 4) = 0
setsockopt(4, SOL_IP, IP_TOS, [192], 4) = 0
setsockopt(4, SOL_IP, IP_PKTINFO, [1], 4) = 0
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
socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) = 5

Interestingly, it's being opened with SO_REUSEPORT, allowing more than one listening process to listen (if they also set SO_REUSEPORT).

1
  • 1
    Interestingly on my machine it is binding both 0.0.0.0%eth0:67 (reasonable) and 0.0.0.0:67 (wat) Feb 7, 2023 at 22:06

1 Answer 1

2

The problem is in dhcp.c

  /* When bind-interfaces is set, there might be more than one dnsmasq
     instance binding port 67. That's OK if they serve different networks.
     Need to set REUSEADDR|REUSEPORT to make this possible.
     Handle the case that REUSEPORT is defined, but the kernel doesn't 
     support it. This handles the introduction of REUSEPORT on Linux. */
  if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
    {
      int rc = 0;

#ifdef SO_REUSEPORT
      if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 && 
      errno == ENOPROTOOPT)
    rc = 0;
#endif
      
      if (rc != -1)
    rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
      
      if (rc == -1)
    die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
    }


  memset(&saddr, 0, sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_port = htons(port);
  saddr.sin_addr.s_addr = INADDR_ANY;
#ifdef HAVE_SOCKADDR_SA_LEN
  saddr.sin_len = sizeof(struct sockaddr_in);
#endif

  if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
    die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);

The socket is bound to INADDR_ANY (0.0.0.0) without any conditionality.

It looks like the author's intention is for multiple dnsmasqs to share 0.0.0.0:67 using SO_REUSEPORT. This is fine for multiple dnsmasqs, but not good when dnsmasq needs to co-exist with another DHCP server which doesn't set SO_REUSEPORT.

Java has SO_REUSEADDR, but not SO_REUSEPORT:

packages/modules/NetworkStack/src/android/net/dhcp/DhcpServer.java:642: error: cannot find symbol
                Os.setsockoptInt(mSocket, SOL_SOCKET, SO_REUSEPORT, 1);

but that might be fixed with:

final int SO_REUSEPORT = 15;
2
  • 1
    Note that a DHCP client doesn't know the IP address of the DHCP server, so it doesn't make sense to bind to DHCP server to an IP address.
    – RalfFriedl
    May 25, 2021 at 11:22
  • 1
    @RalfFriedl Why doesn't it make sense? Binding to an IP address means the server only listens on the interface with the address, but it should be able to receive client broadcasts just fine, as long as they are sent on the network the server is listening on. May 25, 2021 at 13:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .