- dhcp: CheckIfOtherDHCPServersPresent: fix

Sometimes request from DHCP server couldn't be received
 because we were bound to a specific IP address.
This commit is contained in:
Simon Zolin 2020-08-12 18:13:07 +03:00
parent 6090400ea0
commit 23752377b7
3 changed files with 10 additions and 94 deletions

View File

@ -7,12 +7,13 @@ import (
"fmt" "fmt"
"net" "net"
"os" "os"
"runtime"
"time" "time"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/insomniacslk/dhcp/iana" "github.com/insomniacslk/dhcp/iana"
"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,
@ -29,6 +30,10 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
return false, fmt.Errorf("couldn't find IPv4 address of interface %s %+v", ifaceName, iface) return false, fmt.Errorf("couldn't find IPv4 address of interface %s %+v", ifaceName, iface)
} }
if runtime.GOOS == "darwin" {
return false, fmt.Errorf("Can't find DHCP server: not supported on macOS")
}
srcIP := ifaceIPNet[0] srcIP := ifaceIPNet[0]
src := net.JoinHostPort(srcIP.String(), "68") src := net.JoinHostPort(srcIP.String(), "68")
dst := "255.255.255.255:67" dst := "255.255.255.255:67"
@ -60,7 +65,7 @@ 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 := newBroadcastPacketConn(net.IPv4(0, 0, 0, 0), 68, ifaceName) c, err := nclient4.NewRawUDPConn(ifaceName, 68)
if c != nil { if c != nil {
defer c.Close() defer c.Close()
} }
@ -69,8 +74,7 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
} }
// send to 255.255.255.255:67 // send to 255.255.255.255:67
cm := ipv4.ControlMessage{} _, err = c.WriteTo(req.ToBytes(), dstAddr)
_, err = c.WriteTo(req.ToBytes(), &cm, dstAddr)
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)
} }
@ -81,9 +85,10 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
// 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
log.Debug("DHCPv4: didn't receive DHCP response")
return false, nil return false, nil
} }
if err != nil { if err != nil {

View File

@ -1,45 +0,0 @@
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
}

View File

@ -1,44 +0,0 @@
// +build aix darwin dragonfly freebsd 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
}
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
}