diff --git a/dhcpd/check_other_dhcp.go b/dhcpd/check_other_dhcp.go index 6c6f353b..e7c1c89c 100644 --- a/dhcpd/check_other_dhcp.go +++ b/dhcpd/check_other_dhcp.go @@ -2,16 +2,14 @@ package dhcpd import ( "bytes" - "crypto/rand" - "encoding/binary" "fmt" - "math" "net" "os" "time" "github.com/AdguardTeam/golibs/log" - "github.com/krolaw/dhcp4" + "github.com/insomniacslk/dhcp/dhcpv4" + "github.com/insomniacslk/dhcp/iana" "golang.org/x/net/ipv4" ) @@ -34,51 +32,17 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) { src := net.JoinHostPort(srcIP.String(), "68") dst := "255.255.255.255:67" - // form a DHCP request packet, try to emulate existing client as much as possible - xID := make([]byte, 4) - n, err := rand.Read(xID) - if n != 4 && err == nil { - err = fmt.Errorf("Generated less than 4 bytes") - } - if err != nil { - return false, wrapErrPrint(err, "Couldn't generate random bytes") - } - hostname, err := os.Hostname() - if err != nil { - return false, wrapErrPrint(err, "Couldn't get hostname") - } - requestList := []byte{ - byte(dhcp4.OptionSubnetMask), - byte(dhcp4.OptionClasslessRouteFormat), - byte(dhcp4.OptionRouter), - byte(dhcp4.OptionDomainNameServer), - byte(dhcp4.OptionDomainName), - byte(dhcp4.OptionDomainSearch), - 252, // private/proxy autodiscovery - 95, // LDAP - byte(dhcp4.OptionNetBIOSOverTCPIPNameServer), - byte(dhcp4.OptionNetBIOSOverTCPIPNodeType), - } - maxUDPsizeRaw := make([]byte, 2) - binary.BigEndian.PutUint16(maxUDPsizeRaw, 1500) - leaseTimeRaw := make([]byte, 4) - leaseTime := uint32(math.RoundToEven((time.Hour * 24 * 90).Seconds())) - binary.BigEndian.PutUint32(leaseTimeRaw, leaseTime) - options := []dhcp4.Option{ - {Code: dhcp4.OptionParameterRequestList, Value: requestList}, - {Code: dhcp4.OptionMaximumDHCPMessageSize, Value: maxUDPsizeRaw}, - {Code: dhcp4.OptionClientIdentifier, Value: append([]byte{0x01}, iface.HardwareAddr...)}, - {Code: dhcp4.OptionIPAddressLeaseTime, Value: leaseTimeRaw}, - {Code: dhcp4.OptionHostName, Value: []byte(hostname)}, - } - packet := dhcp4.RequestPacket(dhcp4.Discover, iface.HardwareAddr, nil, xID, false, options) + hostname, _ := os.Hostname() + + req, err := dhcpv4.NewDiscovery(iface.HardwareAddr) + req.Options.Update(dhcpv4.OptClientIdentifier(iface.HardwareAddr)) + req.Options.Update(dhcpv4.OptHostName(hostname)) // resolve 0.0.0.0:68 udpAddr, err := net.ResolveUDPAddr("udp4", src) if err != nil { return false, wrapErrPrint(err, "Couldn't resolve UDP address %s", src) } - // spew.Dump(udpAddr, err) if !udpAddr.IP.To4().Equal(srcIP) { return false, wrapErrPrint(err, "Resolved UDP address is not %s", src) @@ -102,7 +66,7 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) { // send to 255.255.255.255:67 cm := ipv4.ControlMessage{} - _, err = c.WriteTo(packet, &cm, dstAddr) + _, err = c.WriteTo(req.ToBytes(), &cm, dstAddr) if err != nil { return false, wrapErrPrint(err, "Couldn't send a packet to %s", dst) } @@ -113,7 +77,7 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) { // TODO: replicate dhclient's behaviour of retrying several times with progressively bigger timeouts b := make([]byte, 1500) _ = c.SetReadDeadline(time.Now().Add(defaultDiscoverTime)) - n, _, _, err = c.ReadFrom(b) + n, _, _, err := c.ReadFrom(b) if isTimeout(err) { // timed out -- no DHCP servers return false, nil @@ -121,27 +85,24 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) { if err != nil { return false, wrapErrPrint(err, "Couldn't receive packet") } - // spew.Dump(n, fromAddr, err, b) log.Tracef("Received packet (%v bytes)", n) - if n < 240 { - // packet too small for dhcp + response, err := dhcpv4.FromBytes(b[:n]) + if err != nil { + log.Debug("DHCPv4: dhcpv4.FromBytes: %s", err) continue } - response := dhcp4.Packet(b[:n]) - if response.OpCode() != dhcp4.BootReply || - response.HType() != 1 /*Ethernet*/ || - response.HLen() > 16 || - !bytes.Equal(response.CHAddr(), iface.HardwareAddr) || - !bytes.Equal(response.XId(), xID) { - continue - } + log.Debug("DHCPv4: received message from server: %s", response.Summary()) - parsedOptions := response.ParseOptions() - if t := parsedOptions[dhcp4.OptionDHCPMessageType]; len(t) != 1 { - continue //packet without DHCP message type + if !(response.OpCode == dhcpv4.OpcodeBootReply && + response.HWType == iana.HWTypeEthernet && + bytes.Equal(response.ClientHWAddr, iface.HardwareAddr) && + bytes.Equal(response.TransactionID[:], req.TransactionID[:]) && + response.Options.Has(dhcpv4.OptionDHCPMessageType)) { + log.Debug("DHCPv4: received message from server doesn't match our request") + continue } log.Tracef("The packet is from an active DHCP server") diff --git a/dhcpd/dhcpd.go b/dhcpd/dhcpd.go index 38b3b3d3..ba3538c9 100644 --- a/dhcpd/dhcpd.go +++ b/dhcpd/dhcpd.go @@ -15,9 +15,8 @@ const leaseExpireStatic = 1 var webHandlersRegistered = false // Lease contains the necessary information about a DHCP lease -// field ordering is important -- yaml fields will mirror ordering from here type Lease struct { - HWAddr net.HardwareAddr `json:"mac" yaml:"hwaddr"` + HWAddr net.HardwareAddr `json:"mac"` IP net.IP `json:"ip"` Hostname string `json:"hostname"` @@ -29,17 +28,17 @@ type Lease struct { // ServerConfig - DHCP server configuration // field ordering is important -- yaml fields will mirror ordering from here type ServerConfig struct { - Conf4 V4ServerConf `json:"-" yaml:"dhcpv4"` - Conf6 V6ServerConf `json:"-" yaml:"dhcpv6"` + Conf4 V4ServerConf `yaml:"dhcpv4"` + Conf6 V6ServerConf `yaml:"dhcpv6"` - WorkDir string `json:"-" yaml:"-"` - DBFilePath string `json:"-" yaml:"-"` // path to DB file + WorkDir string `yaml:"-"` + DBFilePath string `yaml:"-"` // path to DB file // Called when the configuration is changed by HTTP request - ConfigModified func() `json:"-" yaml:"-"` + ConfigModified func() `yaml:"-"` // Register an HTTP handler - HTTPRegister func(string, string, func(http.ResponseWriter, *http.Request)) `json:"-" yaml:"-"` + HTTPRegister func(string, string, func(http.ResponseWriter, *http.Request)) `yaml:"-"` } type onLeaseChangedT func(flags int) diff --git a/go.mod b/go.mod index bf7a29fc..faab69fa 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7 github.com/joomcode/errorx v1.0.1 github.com/kardianos/service v1.0.0 - github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414 github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 // indirect github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 // indirect github.com/miekg/dns v1.1.29 diff --git a/go.sum b/go.sum index 9b393a7e..404a35e5 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414 h1:6wnYc2S/lVM7BvR32BM74ph7bPgqMztWopMYKgVyEho= -github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414/go.mod h1:0AqAH3ZogsCrvrtUpvc6EtVKbc3w6xwZhkvGLuqyi3o= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 h1:lez6TS6aAau+8wXUP3G9I3TGlmPFEq2CTxBaRqY6AGE= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=