Merge: - /control/dhcp/find_active_dhcp: fix DHCP server detection
Close #704 * commit '68dc8a13411c09cd8c382a1bf986953659166506': * control: update "DHCP server not found" message * update openapi.yaml - /control/dhcp/find_active_dhcp: fix DHCP server detection
This commit is contained in:
commit
173ab2a3c1
|
@ -12,8 +12,8 @@
|
||||||
"dhcp_description": "If your router does not provide DHCP settings, you can use AdGuard's own built-in DHCP server.",
|
"dhcp_description": "If your router does not provide DHCP settings, you can use AdGuard's own built-in DHCP server.",
|
||||||
"dhcp_enable": "Enable DHCP server",
|
"dhcp_enable": "Enable DHCP server",
|
||||||
"dhcp_disable": "Disable DHCP server",
|
"dhcp_disable": "Disable DHCP server",
|
||||||
"dhcp_not_found": "No active DHCP servers found on the network. It is safe to enable the built-in DHCP server.",
|
"dhcp_not_found": "It is safe to enable the built-in DHCP server - we didn't find any active DHCP servers on the network. However, we encourage you to re-check it manually as our automatic test currently doesn't give 100% guarantee.",
|
||||||
"dhcp_found": "Some active DHCP servers found on the network. It is not safe to enable the built-in DHCP server.",
|
"dhcp_found": "An active DHCP server is found on the network. It is not safe to enable the built-in DHCP server.",
|
||||||
"dhcp_leases": "DHCP leases",
|
"dhcp_leases": "DHCP leases",
|
||||||
"dhcp_leases_not_found": "No DHCP leases found",
|
"dhcp_leases_not_found": "No DHCP leases found",
|
||||||
"dhcp_config_saved": "Saved DHCP server config",
|
"dhcp_config_saved": "Saved DHCP server config",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -11,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/krolaw/dhcp4"
|
"github.com/krolaw/dhcp4"
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckIfOtherDHCPServersPresent sends a DHCP request to the specified network interface,
|
// CheckIfOtherDHCPServersPresent sends a DHCP request to the specified network interface,
|
||||||
|
@ -32,13 +34,13 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
|
||||||
dst := "255.255.255.255:67"
|
dst := "255.255.255.255:67"
|
||||||
|
|
||||||
// form a DHCP request packet, try to emulate existing client as much as possible
|
// form a DHCP request packet, try to emulate existing client as much as possible
|
||||||
xID := make([]byte, 8)
|
xID := make([]byte, 4)
|
||||||
n, err := rand.Read(xID)
|
n, err := rand.Read(xID)
|
||||||
if n != 8 && err == nil {
|
if n != 4 && err == nil {
|
||||||
err = fmt.Errorf("Generated less than 8 bytes")
|
err = fmt.Errorf("Generated less than 4 bytes")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, wrapErrPrint(err, "Couldn't generate 8 random bytes")
|
return false, wrapErrPrint(err, "Couldn't generate random bytes")
|
||||||
}
|
}
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -89,29 +91,31 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
|
||||||
|
|
||||||
// bind to 0.0.0.0:68
|
// bind to 0.0.0.0:68
|
||||||
log.Tracef("Listening to udp4 %+v", udpAddr)
|
log.Tracef("Listening to udp4 %+v", udpAddr)
|
||||||
c, err := net.ListenPacket("udp4", src)
|
c, err := newBroadcastPacketConn(net.IPv4(0, 0, 0, 0), 68, ifaceName)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
}
|
}
|
||||||
// spew.Dump(c, err)
|
// spew.Dump(c, err)
|
||||||
// spew.Printf("net.ListenUDP returned %v, %v\n", c, err)
|
// spew.Printf("net.ListenUDP returned %v, %v\n", c, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, wrapErrPrint(err, "Couldn't listen to %s", src)
|
return false, wrapErrPrint(err, "Couldn't listen on :68")
|
||||||
}
|
}
|
||||||
|
|
||||||
// send to 255.255.255.255:67
|
// send to 255.255.255.255:67
|
||||||
_, err = c.WriteTo(packet, dstAddr)
|
cm := ipv4.ControlMessage{}
|
||||||
|
_, err = c.WriteTo(packet, &cm, dstAddr)
|
||||||
// spew.Dump(n, err)
|
// spew.Dump(n, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, wrapErrPrint(err, "Couldn't send a packet to %s", dst)
|
return false, wrapErrPrint(err, "Couldn't send a packet to %s", dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
// wait for answer
|
// wait for answer
|
||||||
log.Tracef("Waiting %v for an answer", defaultDiscoverTime)
|
log.Tracef("Waiting %v for an answer", defaultDiscoverTime)
|
||||||
// TODO: replicate dhclient's behaviour of retrying several times with progressively bigger timeouts
|
// TODO: replicate dhclient's behaviour of retrying several times with progressively bigger timeouts
|
||||||
b := make([]byte, 1500)
|
b := make([]byte, 1500)
|
||||||
c.SetReadDeadline(time.Now().Add(defaultDiscoverTime))
|
_ = c.SetReadDeadline(time.Now().Add(defaultDiscoverTime))
|
||||||
n, _, err = c.ReadFrom(b)
|
n, _, _, err = c.ReadFrom(b)
|
||||||
if isTimeout(err) {
|
if isTimeout(err) {
|
||||||
// timed out -- no DHCP servers
|
// timed out -- no DHCP servers
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -119,28 +123,31 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, wrapErrPrint(err, "Couldn't receive packet")
|
return false, wrapErrPrint(err, "Couldn't receive packet")
|
||||||
}
|
}
|
||||||
if n > 0 {
|
|
||||||
b = b[:n]
|
|
||||||
}
|
|
||||||
// spew.Dump(n, fromAddr, err, b)
|
// spew.Dump(n, fromAddr, err, b)
|
||||||
|
|
||||||
|
log.Tracef("Received packet (%v bytes)", n)
|
||||||
|
|
||||||
if n < 240 {
|
if n < 240 {
|
||||||
// packet too small for dhcp
|
// packet too small for dhcp
|
||||||
return false, wrapErrPrint(err, "got packet that's too small for DHCP")
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
response := dhcp4.Packet(b[:n])
|
response := dhcp4.Packet(b[:n])
|
||||||
if response.HLen() > 16 {
|
if response.OpCode() != dhcp4.BootReply ||
|
||||||
// invalid size
|
response.HType() != 1 /*Ethernet*/ ||
|
||||||
return false, wrapErrPrint(err, "got malformed packet with HLen() > 16")
|
response.HLen() > 16 ||
|
||||||
|
!bytes.Equal(response.CHAddr(), iface.HardwareAddr) ||
|
||||||
|
!bytes.Equal(response.XId(), xID) {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedOptions := response.ParseOptions()
|
parsedOptions := response.ParseOptions()
|
||||||
_, ok := parsedOptions[dhcp4.OptionDHCPMessageType]
|
if t := parsedOptions[dhcp4.OptionDHCPMessageType]; len(t) != 1 {
|
||||||
if !ok {
|
continue //packet without DHCP message type
|
||||||
return false, wrapErrPrint(err, "got malformed packet without DHCP message type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Tracef("The packet is from an active DHCP server")
|
||||||
// that's a DHCP server there
|
// that's a DHCP server there
|
||||||
return true, nil
|
return true, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
|
package dhcpd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a socket for receiving broadcast packets
|
||||||
|
func newBroadcastPacketConn(bindAddr net.IP, port int, ifname string) (*ipv4.PacketConn, error) {
|
||||||
|
s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := syscall.SetsockoptString(s, syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, ifname); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := syscall.SockaddrInet4{Port: port}
|
||||||
|
copy(addr.Addr[:], bindAddr.To4())
|
||||||
|
err = syscall.Bind(s, &addr)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(s)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f := os.NewFile(uintptr(s), "")
|
||||||
|
c, err := net.FilePacketConn(f)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p := ipv4.NewPacketConn(c)
|
||||||
|
return p, nil
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package dhcpd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a socket for receiving broadcast packets
|
||||||
|
func newBroadcastPacketConn(bindAddr net.IP, port int, ifname string) (*ipv4.PacketConn, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
|
@ -1142,14 +1142,33 @@ definitions:
|
||||||
DhcpSearchResult:
|
DhcpSearchResult:
|
||||||
type: "object"
|
type: "object"
|
||||||
description: "Information about a DHCP server discovered in the current network"
|
description: "Information about a DHCP server discovered in the current network"
|
||||||
required:
|
properties:
|
||||||
- "found"
|
other_server:
|
||||||
|
$ref: "#/definitions/DhcpSearchResultOtherServer"
|
||||||
|
static_ip:
|
||||||
|
$ref: "#/definitions/DhcpSearchResultStaticIP"
|
||||||
|
DhcpSearchResultOtherServer:
|
||||||
|
type: "object"
|
||||||
properties:
|
properties:
|
||||||
found:
|
found:
|
||||||
type: "boolean"
|
|
||||||
gateway_ip:
|
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "192.168.1.1"
|
description: "yes|no|error"
|
||||||
|
example: "no"
|
||||||
|
error:
|
||||||
|
type: "string"
|
||||||
|
description: "Set if found=error"
|
||||||
|
example: ""
|
||||||
|
DhcpSearchResultStaticIP:
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
static:
|
||||||
|
type: "string"
|
||||||
|
description: "yes|no|error"
|
||||||
|
example: "yes"
|
||||||
|
ip:
|
||||||
|
type: "string"
|
||||||
|
description: "Set if static=no"
|
||||||
|
example: ""
|
||||||
DnsAnswer:
|
DnsAnswer:
|
||||||
type: "object"
|
type: "object"
|
||||||
description: "DNS answer section"
|
description: "DNS answer section"
|
||||||
|
|
Loading…
Reference in New Issue