Dnsmasq
dnsmasq 錯誤地綁定到 DHCP 的 0.0.0.0:67,同時正確偵聽 10.0.0.1:53 的 DNS 查詢
我有兩個 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 顯示在地址 0.0.0.0 上打開了埠 67:
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
)。
問題出在 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) 沒有任何條件。
看起來作者的意圖是讓多個 dnsmasq 使用 .0.0.0.0:67 共享 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;