![dnsmasq 错误地绑定到 0.0.0.0:67 进行 DHCP,同时正确侦听 10.0.0.1:53 进行 DNS 查询](https://linux22.com/image/191631/dnsmasq%20%E9%94%99%E8%AF%AF%E5%9C%B0%E7%BB%91%E5%AE%9A%E5%88%B0%200.0.0.0%3A67%20%E8%BF%9B%E8%A1%8C%20DHCP%EF%BC%8C%E5%90%8C%E6%97%B6%E6%AD%A3%E7%A1%AE%E4%BE%A6%E5%90%AC%2010.0.0.1%3A53%20%E8%BF%9B%E8%A1%8C%20DNS%20%E6%9F%A5%E8%AF%A2.png)
我有两个 DHCP 服务器,运行在不同的两个接口上。 (只有 eth0/10.0.0.1 使用 dnsmasq。)
根据联机帮助页,以下选项应使其仅侦听 10.0.0.1(对于 DNS 和 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
上述参数导致 DNS 的预期行为,但 DHCP 错误地绑定到 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
我已尝试过这些参数的几种变化,但 DHCP 始终在监听0.0.0.0:67
。
更新:
strace 显示端口 67 在地址 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
有趣的是,它是用 开启的SO_REUSEPORT
,允许多个监听进程进行监听(如果它们也设置了SO_REUSEPORT
)。
答案1
问题出在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);
套接字绑定到 INADDR_ANY (0.0.0.0),没有任何条件。
看起来作者的意图是让多个 dnsmasqs 使用 共享 0.0.0.0:67 SO_REUSEPORT
。这对于多个 dnsmasq 来说很好,但当 dnsmasq 需要与另一个未设置 SO_REUSEPORT 的 DHCP 服务器共存时就不好了。
Java有SO_REUSEADDR,但没有SO_REUSEPORT:
packages/modules/NetworkStack/src/android/net/dhcp/DhcpServer.java:642: error: cannot find symbol
Os.setsockoptInt(mSocket, SOL_SOCKET, SO_REUSEPORT, 1);
但这可能可以通过以下方式解决:
final int SO_REUSEPORT = 15;