두 개의 서로 다른 인터페이스에서 실행되는 두 개의 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)에 바인딩됩니다.
작성자의 의도는 여러 dnsmasq가 share 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;