Pull request: 3289 freebsd dhcp
Merge in DNS/adguard-home from 3289-freebsd-dhcp to master Updates #3289. Squashed commit of the following: commit 1365d8f17293da611b860525d519a7bbd7851902 Author: Eugene Burkov <e.burkov@adguard.com> Date: Fri Jul 30 15:01:13 2021 +0300 dhcpd: fix doc commit 26724df27e92d457c39c8bf0fb78179a874e3fb2 Author: Eugene Burkov <e.burkov@adguard.com> Date: Fri Jul 30 14:52:58 2021 +0300 all: imp code & docs commit 9a9574a885d3d2129ef54fefb9a56857ce060cff Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jul 29 15:51:07 2021 +0300 all: fix broadcasting, sup freebsd dhcp, fix http response
This commit is contained in:
parent
63ee95dfbe
commit
6fa1167251
|
@ -15,6 +15,7 @@ and this project adheres to
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Static IP address detection on FreeBSD ([#3289]).
|
||||||
- Optimistic cache ([#2145]).
|
- Optimistic cache ([#2145]).
|
||||||
- New possible value of `6h` for `querylog_interval` setting ([#2504]).
|
- New possible value of `6h` for `querylog_interval` setting ([#2504]).
|
||||||
- Blocking access using client IDs ([#2624], [#3162]).
|
- Blocking access using client IDs ([#2624], [#3162]).
|
||||||
|
@ -62,6 +63,7 @@ and this project adheres to
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Incomplete HTTP response for static IP address.
|
||||||
- DNSCrypt queries weren't appearing in query log ([#3372]).
|
- DNSCrypt queries weren't appearing in query log ([#3372]).
|
||||||
- Wrong IP address for proxied DNS-over-HTTPS queries ([#2799]).
|
- Wrong IP address for proxied DNS-over-HTTPS queries ([#2799]).
|
||||||
- Domain name letter case mismatches in DNS rewrites ([#3351]).
|
- Domain name letter case mismatches in DNS rewrites ([#3351]).
|
||||||
|
@ -108,6 +110,7 @@ and this project adheres to
|
||||||
[#3217]: https://github.com/AdguardTeam/AdGuardHome/issues/3217
|
[#3217]: https://github.com/AdguardTeam/AdGuardHome/issues/3217
|
||||||
[#3256]: https://github.com/AdguardTeam/AdGuardHome/issues/3256
|
[#3256]: https://github.com/AdguardTeam/AdGuardHome/issues/3256
|
||||||
[#3257]: https://github.com/AdguardTeam/AdGuardHome/issues/3257
|
[#3257]: https://github.com/AdguardTeam/AdGuardHome/issues/3257
|
||||||
|
[#3289]: https://github.com/AdguardTeam/AdGuardHome/issues/3289
|
||||||
[#3335]: https://github.com/AdguardTeam/AdGuardHome/issues/3335
|
[#3335]: https://github.com/AdguardTeam/AdGuardHome/issues/3335
|
||||||
[#3343]: https://github.com/AdguardTeam/AdGuardHome/issues/3343
|
[#3343]: https://github.com/AdguardTeam/AdGuardHome/issues/3343
|
||||||
[#3351]: https://github.com/AdguardTeam/AdGuardHome/issues/3351
|
[#3351]: https://github.com/AdguardTeam/AdGuardHome/issues/3351
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
//go:build freebsd
|
||||||
|
// +build freebsd
|
||||||
|
|
||||||
|
package aghnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghio"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func canBindPrivilegedPorts() (can bool, err error) {
|
||||||
|
return aghos.HaveAdminRights()
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxCheckedFileSize is the maximum acceptable length of the /etc/rc.conf file.
|
||||||
|
const maxCheckedFileSize = 1024 * 1024
|
||||||
|
|
||||||
|
func ifaceHasStaticIP(ifaceName string) (ok bool, err error) {
|
||||||
|
const filename = "/etc/rc.conf"
|
||||||
|
|
||||||
|
var f *os.File
|
||||||
|
f, err = os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer func() { err = errors.WithDeferred(err, f.Close()) }()
|
||||||
|
|
||||||
|
var r io.Reader
|
||||||
|
r, err = aghio.LimitReader(f, maxCheckedFileSize)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rcConfStaticConfig(r, ifaceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rcConfStaticConfig checks if the interface is configured by /etc/rc.conf to
|
||||||
|
// have a static IP.
|
||||||
|
func rcConfStaticConfig(r io.Reader, ifaceName string) (has bool, err error) {
|
||||||
|
s := bufio.NewScanner(r)
|
||||||
|
for ifaceLinePref := fmt.Sprintf("ifconfig_%s", ifaceName); s.Scan(); {
|
||||||
|
line := strings.TrimSpace(s.Text())
|
||||||
|
if !strings.HasPrefix(line, ifaceLinePref) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
eqIdx := len(ifaceLinePref)
|
||||||
|
if line[eqIdx] != '=' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldsStart, fieldsEnd := eqIdx+2, len(line)-1
|
||||||
|
if fieldsStart >= fieldsEnd {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(line[fieldsStart:fieldsEnd])
|
||||||
|
if len(fields) >= 2 &&
|
||||||
|
strings.ToLower(fields[0]) == "inet" &&
|
||||||
|
net.ParseIP(fields[1]) != nil {
|
||||||
|
return true, s.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, s.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ifaceSetStaticIP(string) (err error) {
|
||||||
|
return aghos.Unsupported("setting static ip")
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
//go:build freebsd
|
||||||
|
// +build freebsd
|
||||||
|
|
||||||
|
package aghnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRcConfStaticConfig(t *testing.T) {
|
||||||
|
const ifaceName = `em0`
|
||||||
|
const nl = "\n"
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
rcconfData string
|
||||||
|
wantHas bool
|
||||||
|
}{{
|
||||||
|
name: "simple",
|
||||||
|
rcconfData: `ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
|
||||||
|
wantHas: true,
|
||||||
|
}, {
|
||||||
|
name: "case_insensitiveness",
|
||||||
|
rcconfData: `ifconfig_em0="InEt 127.0.0.253 NeTmAsK 0xffffffff"` + nl,
|
||||||
|
wantHas: true,
|
||||||
|
}, {
|
||||||
|
name: "comments_and_trash",
|
||||||
|
rcconfData: `# comment 1` + nl +
|
||||||
|
`` + nl +
|
||||||
|
`# comment 2` + nl +
|
||||||
|
`ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
|
||||||
|
wantHas: true,
|
||||||
|
}, {
|
||||||
|
name: "aliases",
|
||||||
|
rcconfData: `ifconfig_em0_alias="inet 127.0.0.1/24"` + nl +
|
||||||
|
`ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
|
||||||
|
wantHas: true,
|
||||||
|
}, {
|
||||||
|
name: "incorrect_config",
|
||||||
|
rcconfData: `ifconfig_em0="inet6 127.0.0.253 netmask 0xffffffff"` + nl +
|
||||||
|
`ifconfig_em0="inet 127.0.0.253 net-mask 0xffffffff"` + nl +
|
||||||
|
`ifconfig_em0="inet 256.256.256.256 netmask 0xffffffff"` + nl +
|
||||||
|
`ifconfig_em0=""` + nl,
|
||||||
|
wantHas: false,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
r := strings.NewReader(tc.rcconfData)
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
has, err := rcConfStaticConfig(r, ifaceName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.wantHas, has)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,9 @@
|
||||||
//go:build !(linux || darwin)
|
//go:build !(linux || darwin || freebsd)
|
||||||
// +build !linux,!darwin
|
// +build !linux,!darwin,!freebsd
|
||||||
|
|
||||||
package aghnet
|
package aghnet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,10 +11,10 @@ func canBindPrivilegedPorts() (can bool, err error) {
|
||||||
return aghos.HaveAdminRights()
|
return aghos.HaveAdminRights()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ifaceHasStaticIP(string) (bool, error) {
|
func ifaceHasStaticIP(string) (ok bool, err error) {
|
||||||
return false, fmt.Errorf("cannot check if IP is static: not supported on %s", runtime.GOOS)
|
return false, aghos.Unsupported("checking static ip")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ifaceSetStaticIP(string) error {
|
func ifaceSetStaticIP(string) (err error) {
|
||||||
return fmt.Errorf("cannot set static IP on %s", runtime.GOOS)
|
return aghos.Unsupported("setting static ip")
|
||||||
}
|
}
|
||||||
|
|
|
@ -412,7 +412,9 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
|
||||||
result := dhcpSearchResult{
|
result := dhcpSearchResult{
|
||||||
V4: dhcpSearchV4Result{
|
V4: dhcpSearchV4Result{
|
||||||
OtherServer: dhcpSearchOtherResult{},
|
OtherServer: dhcpSearchOtherResult{},
|
||||||
StaticIP: dhcpStaticIPStatus{},
|
StaticIP: dhcpStaticIPStatus{
|
||||||
|
Static: "yes",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
V6: dhcpSearchV6Result{
|
V6: dhcpSearchV6Result{
|
||||||
OtherServer: dhcpSearchOtherResult{},
|
OtherServer: dhcpSearchOtherResult{},
|
||||||
|
|
|
@ -38,6 +38,9 @@ type V4ServerConf struct {
|
||||||
|
|
||||||
GatewayIP net.IP `yaml:"gateway_ip" json:"gateway_ip"`
|
GatewayIP net.IP `yaml:"gateway_ip" json:"gateway_ip"`
|
||||||
SubnetMask net.IP `yaml:"subnet_mask" json:"subnet_mask"`
|
SubnetMask net.IP `yaml:"subnet_mask" json:"subnet_mask"`
|
||||||
|
// broadcastIP is the broadcasting address pre-calculated from the
|
||||||
|
// configured gateway IP and subnet mask.
|
||||||
|
broadcastIP net.IP
|
||||||
|
|
||||||
// The first & the last IP address for dynamic leases
|
// The first & the last IP address for dynamic leases
|
||||||
// Bytes [0..2] of the last allowed IP address must match the first IP
|
// Bytes [0..2] of the last allowed IP address must match the first IP
|
||||||
|
|
|
@ -927,12 +927,30 @@ func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4
|
||||||
resp.Options.Update(dhcpv4.OptMessageType(dhcpv4.MessageTypeNak))
|
resp.Options.Update(dhcpv4.OptMessageType(dhcpv4.MessageTypeNak))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// peer is expected to be of type *net.UDPConn as the server4.NewServer
|
||||||
|
// initializes it.
|
||||||
|
udpPeer, ok := peer.(*net.UDPAddr)
|
||||||
|
if !ok {
|
||||||
|
log.Error("dhcpv4: peer is of unexpected type %T", peer)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Despite the fact that server4.NewIPv4UDPConn explicitly sets socket
|
||||||
|
// options to allow broadcasting, it also binds the connection to a
|
||||||
|
// specific interface. On FreeBSD conn.WriteTo causes errors while
|
||||||
|
// writing to the addresses that belong to another interface. So, use
|
||||||
|
// the broadcast address specific for the binded interface in case
|
||||||
|
// server4.Server.Serve sets it to net.IPv4Bcast.
|
||||||
|
if udpPeer.IP.Equal(net.IPv4bcast) {
|
||||||
|
udpPeer.IP = s.conf.broadcastIP
|
||||||
|
}
|
||||||
|
|
||||||
log.Debug("dhcpv4: sending: %s", resp.Summary())
|
log.Debug("dhcpv4: sending: %s", resp.Summary())
|
||||||
|
|
||||||
_, err = conn.WriteTo(resp.ToBytes(), peer)
|
_, err = conn.WriteTo(resp.ToBytes(), peer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
|
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1043,6 +1061,12 @@ func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
|
||||||
Mask: subnetMask,
|
Mask: subnetMask,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bcastIP := aghnet.CloneIP(routerIP)
|
||||||
|
for i, b := range subnetMask {
|
||||||
|
bcastIP[i] |= ^b
|
||||||
|
}
|
||||||
|
s.conf.broadcastIP = bcastIP
|
||||||
|
|
||||||
s.conf.ipRange, err = newIPRange(conf.RangeStart, conf.RangeEnd)
|
s.conf.ipRange, err = newIPRange(conf.RangeStart, conf.RangeEnd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s, fmt.Errorf("dhcpv4: %w", err)
|
return s, fmt.Errorf("dhcpv4: %w", err)
|
||||||
|
|
Loading…
Reference in New Issue