From 784bc318cab7b78fa66625c966239a9392adafd4 Mon Sep 17 00:00:00 2001 From: Eugene Burkov Date: Mon, 16 Aug 2021 15:53:33 +0300 Subject: [PATCH] Pull request: 3444 reuse port Merge in DNS/adguard-home from 3444-dhcp-again to master Closes #3444. Squashed commit of the following: commit 5459ded7d58f219ab5417977e0df17c177c73a4a Merge: d6559090 8e667d3c Author: Eugene Burkov Date: Mon Aug 16 15:35:30 2021 +0300 Merge branch 'master' into 3444-dhcp-again commit d6559090a21b6b13be6970a3839db1106fb539b8 Author: Eugene Burkov Date: Mon Aug 16 15:28:38 2021 +0300 aghnet: fix linux commit 262f729224d73a70d61a4b29d5a4d34502b7b094 Author: Eugene Burkov Date: Mon Aug 16 14:30:09 2021 +0300 aghnet: rm debug commit c54b107264f792ec7f17f8d790908408070307d3 Author: Eugene Burkov Date: Mon Aug 16 14:21:07 2021 +0300 aghnet: imp bsd compat, fix openbsd static ip commit f9871a4c51d1f5d2c799a8d1308a4d30a47485f6 Author: Eugene Burkov Date: Sat Aug 14 00:17:46 2021 +0300 aghnet: setsockopt --- go.mod | 1 + internal/aghnet/dhcp_unix.go | 7 ++-- internal/aghnet/interfaces_linux.go | 24 +++++++++++++ internal/aghnet/interfaces_unix.go | 51 +++++++++++++++++++++++++++ internal/aghnet/interfaces_windows.go | 18 ++++++++++ internal/aghnet/net_openbsd.go | 4 +-- 6 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 internal/aghnet/interfaces_linux.go create mode 100644 internal/aghnet/interfaces_unix.go create mode 100644 internal/aghnet/interfaces_windows.go diff --git a/go.mod b/go.mod index 198bba1e..fb1a886d 100644 --- a/go.mod +++ b/go.mod @@ -32,4 +32,5 @@ require ( howett.net/plist v0.0.0-20201203080718-1454fab16a06 ) +// TODO(e.burkov): Get rid of the fork in v0.108.0. replace github.com/insomniacslk/dhcp => github.com/AdguardTeam/dhcp v0.0.0-20210519141215-51808c73c0bf diff --git a/internal/aghnet/dhcp_unix.go b/internal/aghnet/dhcp_unix.go index a4a3820d..479d9926 100644 --- a/internal/aghnet/dhcp_unix.go +++ b/internal/aghnet/dhcp_unix.go @@ -112,12 +112,12 @@ func discover4(iface *net.Interface, dstAddr *net.UDPAddr, hostname string) (ok // It's also known that listening on the specified interface's address // ignores broadcasted packets when reading. var c net.PacketConn - if c, err = net.ListenPacket("udp4", ":68"); err != nil { + if c, err = listenPacketReusable(iface.Name, "udp4", ":68"); err != nil { return false, fmt.Errorf("couldn't listen on :68: %w", err) } defer func() { err = errors.WithDeferred(err, c.Close()) }() - // Send to resolved broadcast. + // Send to broadcast. if _, err = c.WriteTo(req.ToBytes(), dstAddr); err != nil { return false, fmt.Errorf("couldn't send a packet to %s: %w", dstAddr, err) } @@ -154,9 +154,6 @@ func tryConn4(req *dhcpv4.DHCPv4, c net.PacketConn, iface *net.Interface) (ok, n b := make([]byte, 1500) n, _, err := c.ReadFrom(b) - if n > 0 { - log.Debug("received %d bytes: %v", n, b) - } if err != nil { if isTimeout(err) { log.Debug("dhcpv4: didn't receive dhcp response") diff --git a/internal/aghnet/interfaces_linux.go b/internal/aghnet/interfaces_linux.go new file mode 100644 index 00000000..a3cda5fa --- /dev/null +++ b/internal/aghnet/interfaces_linux.go @@ -0,0 +1,24 @@ +//go:build linux +// +build linux + +package aghnet + +import ( + "net" + + "github.com/AdguardTeam/golibs/netutil" + "github.com/insomniacslk/dhcp/dhcpv4/nclient4" +) + +// listenPacketReusable announces on the local network address additionally +// configuring the socket to have a reusable binding. +func listenPacketReusable(ifaceName, network, address string) (c net.PacketConn, err error) { + var port int + _, port, err = netutil.SplitHostPort(address) + if err != nil { + return nil, err + } + + // TODO(e.burkov): Inspect nclient4.NewRawUDPConn and implement here. + return nclient4.NewRawUDPConn(ifaceName, port) +} diff --git a/internal/aghnet/interfaces_unix.go b/internal/aghnet/interfaces_unix.go new file mode 100644 index 00000000..529b0d9c --- /dev/null +++ b/internal/aghnet/interfaces_unix.go @@ -0,0 +1,51 @@ +//go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris +// +build aix darwin dragonfly freebsd netbsd openbsd solaris + +package aghnet + +import ( + "context" + "fmt" + "net" + "os" + "syscall" + + "github.com/AdguardTeam/golibs/errors" + "golang.org/x/sys/unix" +) + +// reuseAddrCtrl is the function to be set to net.ListenConfig.Control. It +// configures the socket to have a reusable port binding. +func reuseAddrCtrl(_, _ string, c syscall.RawConn) (err error) { + cerr := c.Control(func(fd uintptr) { + // TODO(e.burkov): Consider using SO_REUSEPORT. + err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) + if err != nil { + err = os.NewSyscallError("setsockopt", err) + } + }) + + const ( + errMsg = "setting control options" + errMsgFmt = errMsg + ": %w" + ) + + if err != nil && cerr != nil { + err = errors.List(errMsg, err, cerr) + } else if err != nil { + err = fmt.Errorf(errMsgFmt, err) + } else if cerr != nil { + err = fmt.Errorf(errMsgFmt, cerr) + } + + return err +} + +// listenPacketReusable announces on the local network address additionally +// configuring the socket to have a reusable binding. +func listenPacketReusable(_, network, address string) (c net.PacketConn, err error) { + var lc net.ListenConfig + lc.Control = reuseAddrCtrl + + return lc.ListenPacket(context.Background(), network, address) +} diff --git a/internal/aghnet/interfaces_windows.go b/internal/aghnet/interfaces_windows.go new file mode 100644 index 00000000..e483c350 --- /dev/null +++ b/internal/aghnet/interfaces_windows.go @@ -0,0 +1,18 @@ +//go:build windows +// +build windows + +package aghnet + +import ( + "net" + + "github.com/AdguardTeam/AdGuardHome/internal/aghos" +) + +// listenPacketReusable announces on the local network address additionally +// configuring the socket to have a reusable binding. +func listenPacketReusable(_, _, _ string) (c net.PacketConn, err error) { + // TODO(e.burkov): Check if we are able to control sockets on Windows + // in the same way as on Unix. + return nil, aghos.Unsupported("listening packet reusable") +} diff --git a/internal/aghnet/net_openbsd.go b/internal/aghnet/net_openbsd.go index d2604005..a2b5e6e6 100644 --- a/internal/aghnet/net_openbsd.go +++ b/internal/aghnet/net_openbsd.go @@ -31,11 +31,11 @@ func hostnameIfStaticConfig(r io.Reader) (_ []string, ok bool, err error) { line := strings.TrimSpace(s.Text()) fields := strings.Fields(line) if len(fields) >= 2 && fields[0] == "inet" && net.ParseIP(fields[1]) != nil { - return nil, true, s.Err() + return nil, false, s.Err() } } - return nil, false, s.Err() + return nil, true, s.Err() } func ifaceSetStaticIP(string) (err error) {