Pull request: dhcpd imp code
Merge in DNS/adguard-home from dhcpd-imp-code to master Squashed commit of the following: commit 413403c169bd3f6b5f2ed63b783d078dbb15e054 Merge: eed1838500fec990bc
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri May 26 12:46:25 2023 +0300 Merge remote-tracking branch 'origin/master' into dhcpd-imp-code commit eed1838502add1e16e5d3ada03778f21913fd5e5 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri May 26 12:16:46 2023 +0300 dhcpd: imp docs commit fa4fe036f7b1f2b49201bf0b5b904f99989082f0 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu May 25 11:32:34 2023 +0300 all: lint script commit a4022b3d4bbfa709e5096397bbe64ea406c8a366 Merge: e08ff3a26cbc7985e7
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu May 25 11:29:57 2023 +0300 Merge remote-tracking branch 'origin/master' into dhcpd-imp-code # Conflicts: # scripts/make/go-lint.sh commit e08ff3a26414e201d6e75608363db941fa2f5b39 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed May 24 15:43:11 2023 +0300 dhcpd: imp code commit 970b538f8ea94d3732d77bfb648e402a1d28ab06 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed May 24 15:40:36 2023 +0300 dhcpd: imp code commit 0e5916ddd7514af983e8557080d55d6aeb6fbbc0 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed May 24 15:37:17 2023 +0300 dhcpd: imp code commit e06a6c6031b232e76ae2be3e3b8fe1a2ffa715e0 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue May 23 16:40:09 2023 +0300 dhcpd: imp code commit eed4ff10ff1b29c71d70fb7978706efde89afee1 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue May 23 15:45:06 2023 +0300 all: lint script commit 87f84ace5f6f34dbc28befa8257d1d2492c5e0a4 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue May 23 15:44:23 2023 +0300 dhcpd: imp code commit a54c9929d51de1f1e6807d650fd08dd80ddbf147 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue May 23 14:29:42 2023 +0300 dhcpd: imp code commit 1bbea342f7f55587724aa9a29d9657e5ce75f5d8 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue May 23 14:12:09 2023 +0300 dhcpd: imp code commit 48fb4eff73683e799ddb3076afdcf7b067ca24b6 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue May 23 13:57:59 2023 +0300 dhcpd: imp code commit f6cd7fcb8d4c1c815a20875d777ea1eca2c8ea89 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue May 23 13:17:54 2023 +0300 dhcpd: imp code commit 2b91dc25bbaa16dba6d9389a4e2224cf91eb4554 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue May 23 12:57:46 2023 +0300 dhcpd: imp code commit 34f5dd58764080f6202fc9a1abd751a15dbf7090 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon May 22 15:31:39 2023 +0300 dhcpd: imp code commit 12ef0d225064a1b78adf7b2cfca21a8dba92ca8e Merge: 6b62a766524b41100c
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon May 22 13:03:41 2023 +0300 Merge remote-tracking branch 'origin/master' into dhcpd-imp-code commit 6b62a7665720b85398d65a1926518a63e6bb6403 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon May 22 12:55:43 2023 +0300 dhcpd: imp code commit 18c5cdf0480fac7711282027a64d58704c75af5f Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon May 22 12:48:30 2023 +0300 dhcpd: imp code commit e7c1f4324cba3fe86cf56df6b971791a5a8790de Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon May 22 12:37:15 2023 +0300 dhcpd: imp code ... and 1 more commit
This commit is contained in:
parent
0fec990bcf
commit
ea8d634f65
|
@ -1,46 +1,60 @@
|
||||||
# DHCP server
|
# Testing DHCP Server
|
||||||
|
|
||||||
Contents:
|
Contents:
|
||||||
* [Test setup with Virtual Box](#vbox)
|
* [Test setup with Virtual Box](#vbox)
|
||||||
|
* [Quick test with DHCPTest](#dhcptest)
|
||||||
|
|
||||||
<a id="vbox"></a>
|
## <a href="#vbox" id="vbox" name="vbox">Test setup with Virtual Box</a>
|
||||||
## Test setup with Virtual Box
|
|
||||||
|
|
||||||
To set up a test environment for DHCP server you need:
|
### Prerequisites
|
||||||
|
|
||||||
* Linux host machine
|
To set up a test environment for DHCP server you will need:
|
||||||
* Virtual Box
|
|
||||||
* Virtual machine (guest OS doesn't matter)
|
|
||||||
|
|
||||||
### Configure client
|
* Linux AG Home host machine (Virtual).
|
||||||
|
* Virtual Box.
|
||||||
|
* Virtual machine (guest OS doesn't matter).
|
||||||
|
|
||||||
1. Install Virtual Box and run the following command to create a Host-Only network:
|
### Configure Virtual Box
|
||||||
|
|
||||||
$ VBoxManage hostonlyif create
|
1. Install Virtual Box and run the following command to create a Host-Only
|
||||||
|
network:
|
||||||
|
|
||||||
You can check its status by `ip a` command.
|
```sh
|
||||||
|
$ VBoxManage hostonlyif create
|
||||||
|
```
|
||||||
|
|
||||||
You can also set up Host-Only network using Virtual Box menu:
|
You can check its status by `ip a` command.
|
||||||
|
|
||||||
File -> Host Network Manager...
|
You can also set up Host-Only network using Virtual Box menu:
|
||||||
|
|
||||||
2. Create your virtual machine and set up its network:
|
```
|
||||||
|
File -> Host Network Manager...
|
||||||
|
```
|
||||||
|
|
||||||
VM Settings -> Network -> Host-only Adapter
|
2. Create your virtual machine and set up its network:
|
||||||
|
|
||||||
3. Start your VM, install an OS. Configure your network interface to use DHCP and the OS should ask for a IP address from our DHCP server.
|
```
|
||||||
|
VM Settings -> Network -> Host-only Adapter
|
||||||
|
```
|
||||||
|
|
||||||
4. To see the current IP address on client OS you can use `ip a` command on Linux or `ipconfig` on Windows.
|
3. Start your VM, install an OS. Configure your network interface to use
|
||||||
|
DHCP and the OS should ask for a IP address from our DHCP server.
|
||||||
|
|
||||||
5. To force the client OS to request an IP from DHCP server again, you can use `dhclient` on Linux or `ipconfig /release` on Windows.
|
4. To see the current IP addresses on client OS you can use `ip a` command on
|
||||||
|
Linux or `ipconfig` on Windows.
|
||||||
|
|
||||||
### Configure server
|
5. To force the client OS to request an IP from DHCP server again, you can
|
||||||
|
use `dhclient` on Linux or `ipconfig /release` on Windows.
|
||||||
|
|
||||||
1. Edit server configuration file 'AdGuardHome.yaml', for example:
|
### Configure server
|
||||||
|
|
||||||
dhcp:
|
1. Edit server configuration file `AdGuardHome.yaml`, for example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dhcp:
|
||||||
enabled: true
|
enabled: true
|
||||||
interface_name: vboxnet0
|
interface_name: vboxnet0
|
||||||
|
local_domain_name: lan
|
||||||
dhcpv4:
|
dhcpv4:
|
||||||
gateway_ip: 192.168.56.1
|
gateway_ip: 192.168.56.1
|
||||||
subnet_mask: 255.255.255.0
|
subnet_mask: 255.255.255.0
|
||||||
|
@ -54,11 +68,29 @@ To set up a test environment for DHCP server you need:
|
||||||
lease_duration: 86400
|
lease_duration: 86400
|
||||||
ra_slaac_only: false
|
ra_slaac_only: false
|
||||||
ra_allow_slaac: false
|
ra_allow_slaac: false
|
||||||
|
```
|
||||||
|
|
||||||
2. Start the server
|
2. Start the server
|
||||||
|
|
||||||
./AdGuardHome
|
```sh
|
||||||
|
./AdGuardHome -v
|
||||||
|
```
|
||||||
|
|
||||||
There should be a message in log which shows that DHCP server is ready:
|
There should be a message in log which shows that DHCP server is ready:
|
||||||
|
|
||||||
[info] DHCP: listening on 0.0.0.0:67
|
```
|
||||||
|
[info] DHCP: listening on 0.0.0.0:67
|
||||||
|
```
|
||||||
|
|
||||||
|
## <a href="#dhcptest" id="dhcptest" name="dhcptest">Quick test with DHCPTest utility</a>
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
* [DHCP test utility][dhcptest-gh].
|
||||||
|
|
||||||
|
### Quick test
|
||||||
|
|
||||||
|
The DHCP server could be tested for DISCOVER-OFFER packets with in
|
||||||
|
interactive mode.
|
||||||
|
|
||||||
|
[dhcptest-gh]: https://github.com/CyberShadow/dhcptest
|
||||||
|
|
|
@ -239,36 +239,16 @@ func Create(conf *ServerConfig) (s *server, err error) {
|
||||||
// [aghhttp.RegisterFunc].
|
// [aghhttp.RegisterFunc].
|
||||||
s.registerHandlers()
|
s.registerHandlers()
|
||||||
|
|
||||||
v4conf := conf.Conf4
|
v4Enabled, v6Enabled, err := s.setServers(conf)
|
||||||
v4conf.InterfaceName = s.conf.InterfaceName
|
|
||||||
v4conf.notify = s.onNotify
|
|
||||||
v4conf.Enabled = s.conf.Enabled && v4conf.RangeStart.IsValid()
|
|
||||||
|
|
||||||
s.srv4, err = v4Create(&v4conf)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if v4conf.Enabled {
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
return nil, fmt.Errorf("creating dhcpv4 srv: %w", err)
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("dhcpd: warning: creating dhcpv4 srv: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
v6conf := conf.Conf6
|
|
||||||
v6conf.Enabled = s.conf.Enabled
|
|
||||||
if len(v6conf.RangeStart) == 0 {
|
|
||||||
v6conf.Enabled = false
|
|
||||||
}
|
|
||||||
v6conf.InterfaceName = s.conf.InterfaceName
|
|
||||||
v6conf.notify = s.onNotify
|
|
||||||
s.srv6, err = v6Create(v6conf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("creating dhcpv6 srv: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.conf.Conf4 = conf.Conf4
|
s.conf.Conf4 = conf.Conf4
|
||||||
s.conf.Conf6 = conf.Conf6
|
s.conf.Conf6 = conf.Conf6
|
||||||
|
|
||||||
if s.conf.Enabled && !v4conf.Enabled && !v6conf.Enabled {
|
if s.conf.Enabled && !v4Enabled && !v6Enabled {
|
||||||
return nil, fmt.Errorf("neither dhcpv4 nor dhcpv6 srv is configured")
|
return nil, fmt.Errorf("neither dhcpv4 nor dhcpv6 srv is configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,6 +269,39 @@ func Create(conf *ServerConfig) (s *server, err error) {
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setServers updates DHCPv4 and DHCPv6 servers created from the provided
|
||||||
|
// configuration conf.
|
||||||
|
func (s *server) setServers(conf *ServerConfig) (v4Enabled, v6Enabled bool, err error) {
|
||||||
|
v4conf := conf.Conf4
|
||||||
|
v4conf.InterfaceName = s.conf.InterfaceName
|
||||||
|
v4conf.notify = s.onNotify
|
||||||
|
v4conf.Enabled = s.conf.Enabled && v4conf.RangeStart.IsValid()
|
||||||
|
|
||||||
|
s.srv4, err = v4Create(&v4conf)
|
||||||
|
if err != nil {
|
||||||
|
if v4conf.Enabled {
|
||||||
|
return true, false, fmt.Errorf("creating dhcpv4 srv: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("dhcpd: warning: creating dhcpv4 srv: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v6conf := conf.Conf6
|
||||||
|
v6conf.InterfaceName = s.conf.InterfaceName
|
||||||
|
v6conf.notify = s.onNotify
|
||||||
|
v6conf.Enabled = s.conf.Enabled
|
||||||
|
if len(v6conf.RangeStart) == 0 {
|
||||||
|
v6conf.Enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.srv6, err = v6Create(v6conf)
|
||||||
|
if err != nil {
|
||||||
|
return v4conf.Enabled, v6conf.Enabled, fmt.Errorf("creating dhcpv6 srv: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v4conf.Enabled, v6conf.Enabled, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Enabled returns true when the server is enabled.
|
// Enabled returns true when the server is enabled.
|
||||||
func (s *server) Enabled() (ok bool) {
|
func (s *server) Enabled() (ok bool) {
|
||||||
return s.conf.Enabled
|
return s.conf.Enabled
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type v4ServerConfJSON struct {
|
type v4ServerConfJSON struct {
|
||||||
|
@ -263,6 +264,28 @@ func (s *server) handleDHCPSetConfigV6(
|
||||||
return srv6, enabled, err
|
return srv6, enabled, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createServers returns DHCPv4 and DHCPv6 servers created from the provided
|
||||||
|
// configuration conf.
|
||||||
|
func (s *server) createServers(conf *dhcpServerConfigJSON) (srv4, srv6 DHCPServer, err error) {
|
||||||
|
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("bad dhcpv4 configuration: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("bad dhcpv6 configuration: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
|
||||||
|
return nil, nil, fmt.Errorf("dhcpv4 or dhcpv6 configuration must be complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
return srv4, srv6, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleDHCPSetConfig is the handler for the POST /control/dhcp/set_config
|
||||||
|
// HTTP API.
|
||||||
func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
conf := &dhcpServerConfigJSON{}
|
conf := &dhcpServerConfigJSON{}
|
||||||
conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled)
|
conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled)
|
||||||
|
@ -275,22 +298,9 @@ func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
|
srv4, srv6, err := s.createServers(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "bad dhcpv4 configuration: %s", err)
|
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
|
|
||||||
if err != nil {
|
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "bad dhcpv6 configuration: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
|
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "dhcpv4 or dhcpv6 configuration must be complete")
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -350,10 +360,10 @@ type netInterfaceJSON struct {
|
||||||
Addrs6 []netip.Addr `json:"ipv6_addresses"`
|
Addrs6 []netip.Addr `json:"ipv6_addresses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleDHCPInterfaces is the handler for the GET /control/dhcp/interfaces HTTP
|
// handleDHCPInterfaces is the handler for the GET /control/dhcp/interfaces
|
||||||
// API.
|
// HTTP API.
|
||||||
func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||||
resp := map[string]netInterfaceJSON{}
|
resp := map[string]*netInterfaceJSON{}
|
||||||
|
|
||||||
ifaces, err := net.Interfaces()
|
ifaces, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -364,68 +374,23 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
for _, iface := range ifaces {
|
for _, iface := range ifaces {
|
||||||
if iface.Flags&net.FlagLoopback != 0 {
|
if iface.Flags&net.FlagLoopback != 0 {
|
||||||
// it's a loopback, skip it
|
// It's a loopback, skip it.
|
||||||
continue
|
|
||||||
}
|
|
||||||
if iface.Flags&net.FlagBroadcast == 0 {
|
|
||||||
// this interface doesn't support broadcast, skip it
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var addrs []net.Addr
|
if iface.Flags&net.FlagBroadcast == 0 {
|
||||||
addrs, err = iface.Addrs()
|
// This interface doesn't support broadcast, skip it.
|
||||||
if err != nil {
|
continue
|
||||||
aghhttp.Error(
|
}
|
||||||
r,
|
|
||||||
w,
|
jsonIface, iErr := newNetInterfaceJSON(iface)
|
||||||
http.StatusInternalServerError,
|
if iErr != nil {
|
||||||
"Failed to get addresses for interface %s: %s",
|
aghhttp.Error(r, w, http.StatusInternalServerError, "%s", iErr)
|
||||||
iface.Name,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonIface := netInterfaceJSON{
|
if jsonIface != nil {
|
||||||
Name: iface.Name,
|
|
||||||
HardwareAddr: iface.HardwareAddr.String(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if iface.Flags != 0 {
|
|
||||||
jsonIface.Flags = iface.Flags.String()
|
|
||||||
}
|
|
||||||
// we don't want link-local addresses in json, so skip them
|
|
||||||
for _, addr := range addrs {
|
|
||||||
ipnet, ok := addr.(*net.IPNet)
|
|
||||||
if !ok {
|
|
||||||
// not an IPNet, should not happen
|
|
||||||
aghhttp.Error(
|
|
||||||
r,
|
|
||||||
w,
|
|
||||||
http.StatusInternalServerError,
|
|
||||||
"got iface.Addrs() element %[1]s that is not net.IPNet, it is %[1]T",
|
|
||||||
addr)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// ignore link-local
|
|
||||||
//
|
|
||||||
// TODO(e.burkov): Try to listen DHCP on LLA as well.
|
|
||||||
if ipnet.IP.IsLinkLocalUnicast() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip4 := ipnet.IP.To4(); ip4 != nil {
|
|
||||||
addr := netip.AddrFrom4(*(*[4]byte)(ip4))
|
|
||||||
jsonIface.Addrs4 = append(jsonIface.Addrs4, addr)
|
|
||||||
} else {
|
|
||||||
addr := netip.AddrFrom16(*(*[16]byte)(ipnet.IP))
|
|
||||||
jsonIface.Addrs6 = append(jsonIface.Addrs6, addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(jsonIface.Addrs4)+len(jsonIface.Addrs6) != 0 {
|
|
||||||
jsonIface.GatewayIP = aghnet.GatewayIP(iface.Name)
|
|
||||||
resp[iface.Name] = jsonIface
|
resp[iface.Name] = jsonIface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -433,6 +398,64 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newNetInterfaceJSON creates a JSON object from a [net.Interface] iface.
|
||||||
|
func newNetInterfaceJSON(iface net.Interface) (out *netInterfaceJSON, err error) {
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"failed to get addresses for interface %s: %s",
|
||||||
|
iface.Name,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
out = &netInterfaceJSON{
|
||||||
|
Name: iface.Name,
|
||||||
|
HardwareAddr: iface.HardwareAddr.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if iface.Flags != 0 {
|
||||||
|
out.Flags = iface.Flags.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want link-local addresses in JSON, so skip them.
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ipNet, ok := addr.(*net.IPNet)
|
||||||
|
if !ok {
|
||||||
|
// Not an IPNet, should not happen.
|
||||||
|
return nil, fmt.Errorf("got iface.Addrs() element %[1]s that is not"+
|
||||||
|
" net.IPNet, it is %[1]T", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore link-local.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Try to listen DHCP on LLA as well.
|
||||||
|
if ipNet.IP.IsLinkLocalUnicast() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
vAddr, iErr := netutil.IPToAddrNoMapped(ipNet.IP)
|
||||||
|
if iErr != nil {
|
||||||
|
// Not an IPNet, should not happen.
|
||||||
|
return nil, fmt.Errorf("failed to convert IP address %[1]s: %w", addr, iErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vAddr.Is4() {
|
||||||
|
out.Addrs4 = append(out.Addrs4, vAddr)
|
||||||
|
} else {
|
||||||
|
out.Addrs6 = append(out.Addrs6, vAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out.Addrs4)+len(out.Addrs6) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
out.GatewayIP = aghnet.GatewayIP(iface.Name)
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// dhcpSearchOtherResult contains information about other DHCP server for
|
// dhcpSearchOtherResult contains information about other DHCP server for
|
||||||
// specific network interface.
|
// specific network interface.
|
||||||
type dhcpSearchOtherResult struct {
|
type dhcpSearchOtherResult struct {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
"golang.org/x/net/icmp"
|
"golang.org/x/net/icmp"
|
||||||
|
@ -195,7 +196,7 @@ func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init - initialize RA module
|
// Init initializes RA module.
|
||||||
func (ra *raCtx) Init() (err error) {
|
func (ra *raCtx) Init() (err error) {
|
||||||
ra.stop.Store(0)
|
ra.stop.Store(0)
|
||||||
ra.conn = nil
|
ra.conn = nil
|
||||||
|
@ -203,8 +204,7 @@ func (ra *raCtx) Init() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("dhcpv6 ra: source IP address: %s DNS IP address: %s",
|
log.Debug("dhcpv6 ra: source IP address: %s DNS IP address: %s", ra.ipAddr, ra.dnsIPAddr)
|
||||||
ra.ipAddr, ra.dnsIPAddr)
|
|
||||||
|
|
||||||
params := icmpv6RA{
|
params := icmpv6RA{
|
||||||
managedAddressConfiguration: !ra.raSLAACOnly,
|
managedAddressConfiguration: !ra.raSLAACOnly,
|
||||||
|
@ -223,18 +223,15 @@ func (ra *raCtx) Init() (err error) {
|
||||||
return fmt.Errorf("creating packet: %w", err)
|
return fmt.Errorf("creating packet: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
success := false
|
|
||||||
ipAndScope := ra.ipAddr.String() + "%" + ra.ifaceName
|
ipAndScope := ra.ipAddr.String() + "%" + ra.ifaceName
|
||||||
ra.conn, err = icmp.ListenPacket("ip6:ipv6-icmp", ipAndScope)
|
ra.conn, err = icmp.ListenPacket("ip6:ipv6-icmp", ipAndScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dhcpv6 ra: icmp.ListenPacket: %w", err)
|
return fmt.Errorf("dhcpv6 ra: icmp.ListenPacket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if !success {
|
if err != nil {
|
||||||
derr := ra.Close()
|
err = errors.WithDeferred(err, ra.Close())
|
||||||
if derr != nil {
|
|
||||||
log.Error("closing context: %s", derr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -269,7 +266,6 @@ func (ra *raCtx) Init() (err error) {
|
||||||
log.Debug("dhcpv6 ra: loop exit")
|
log.Debug("dhcpv6 ra: loop exit")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
success = true
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -342,8 +342,8 @@ func (s *v4Server) rmLease(lease *Lease) (err error) {
|
||||||
// server to be configured and it's not.
|
// server to be configured and it's not.
|
||||||
const ErrUnconfigured errors.Error = "server is unconfigured"
|
const ErrUnconfigured errors.Error = "server is unconfigured"
|
||||||
|
|
||||||
// AddStaticLease implements the DHCPServer interface for *v4Server. It is safe
|
// AddStaticLease implements the DHCPServer interface for *v4Server. It is
|
||||||
// for concurrent use.
|
// safe for concurrent use.
|
||||||
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||||
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
|
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
|
||||||
|
|
||||||
|
@ -354,21 +354,23 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||||
l.IP = l.IP.Unmap()
|
l.IP = l.IP.Unmap()
|
||||||
|
|
||||||
if !l.IP.Is4() {
|
if !l.IP.Is4() {
|
||||||
return fmt.Errorf("invalid ip %q, only ipv4 is supported", l.IP)
|
return fmt.Errorf("invalid IP %q: only IPv4 is supported", l.IP)
|
||||||
} else if gwIP := s.conf.GatewayIP; gwIP == l.IP {
|
} else if gwIP := s.conf.GatewayIP; gwIP == l.IP {
|
||||||
return fmt.Errorf("can't assign the gateway IP %s to the lease", gwIP)
|
return fmt.Errorf("can't assign the gateway IP %q to the lease", gwIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.IsStatic = true
|
l.IsStatic = true
|
||||||
|
|
||||||
err = netutil.ValidateMAC(l.HWAddr)
|
err = netutil.ValidateMAC(l.HWAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if hostname := l.Hostname; hostname != "" {
|
if hostname := l.Hostname; hostname != "" {
|
||||||
hostname, err = normalizeHostname(hostname)
|
hostname, err = normalizeHostname(hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,32 +388,9 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||||
l.Hostname = hostname
|
l.Hostname = hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the following actions in an anonymous function to make sure
|
err = s.updateStaticLease(l)
|
||||||
// that the lock gets unlocked before the notification step.
|
|
||||||
func() {
|
|
||||||
s.leasesLock.Lock()
|
|
||||||
defer s.leasesLock.Unlock()
|
|
||||||
|
|
||||||
err = s.rmDynamicLease(l)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf(
|
|
||||||
"removing dynamic leases for %s (%s): %w",
|
|
||||||
l.IP,
|
|
||||||
l.HWAddr,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.addLease(l)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,6 +400,25 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateStaticLease safe removes dynamic lease with the same properties and
|
||||||
|
// then adds a static lease l.
|
||||||
|
func (s *v4Server) updateStaticLease(l *Lease) (err error) {
|
||||||
|
s.leasesLock.Lock()
|
||||||
|
defer s.leasesLock.Unlock()
|
||||||
|
|
||||||
|
err = s.rmDynamicLease(l)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("removing dynamic leases for %s (%s): %w", l.IP, l.HWAddr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.addLease(l)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
|
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
|
||||||
func (s *v4Server) RemoveStaticLease(l *Lease) (err error) {
|
func (s *v4Server) RemoveStaticLease(l *Lease) (err error) {
|
||||||
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
|
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
|
||||||
|
@ -894,24 +892,9 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||||
reqIP = req.ClientIPAddr
|
reqIP = req.ClientIPAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
netIP, ok := netip.AddrFromSlice(reqIP)
|
oldLease := s.findLeaseForIP(reqIP, mac)
|
||||||
if !ok {
|
|
||||||
log.Info("dhcpv4: invalid IP: %s", reqIP)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldLease *Lease
|
|
||||||
for _, l := range s.leases {
|
|
||||||
if bytes.Equal(l.HWAddr, mac) && l.IP == netIP {
|
|
||||||
oldLease = l
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if oldLease == nil {
|
if oldLease == nil {
|
||||||
log.Info("dhcpv4: lease with ip %s for %s not found", reqIP, mac)
|
log.Info("dhcpv4: lease with IP %s for %s not found", reqIP, mac)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -925,7 +908,7 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("allocating new lease for %s: %w", mac, err)
|
return fmt.Errorf("allocating new lease for %s: %w", mac, err)
|
||||||
} else if newLease == nil {
|
} else if newLease == nil {
|
||||||
log.Info("dhcpv4: allocating new lease for %s: no more ip addresses", mac)
|
log.Info("dhcpv4: allocating new lease for %s: no more IP addresses", mac)
|
||||||
|
|
||||||
resp.YourIPAddr = make([]byte, 4)
|
resp.YourIPAddr = make([]byte, 4)
|
||||||
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
||||||
|
@ -941,15 +924,32 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||||
return fmt.Errorf("adding new lease for %s: %w", mac, err)
|
return fmt.Errorf("adding new lease for %s: %w", mac, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("dhcpv4: changed ip from %s to %s for %s", reqIP, newLease.IP, mac)
|
log.Info("dhcpv4: changed IP from %s to %s for %s", reqIP, newLease.IP, mac)
|
||||||
|
|
||||||
resp.YourIPAddr = net.IP(newLease.IP.AsSlice())
|
|
||||||
|
|
||||||
|
resp.YourIPAddr = newLease.IP.AsSlice()
|
||||||
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findLeaseForIP returns a lease for provided ip and mac.
|
||||||
|
func (s *v4Server) findLeaseForIP(ip net.IP, mac net.HardwareAddr) (l *Lease) {
|
||||||
|
netIP, ok := netip.AddrFromSlice(ip)
|
||||||
|
if !ok {
|
||||||
|
log.Info("dhcpv4: invalid IP: %s", ip)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, il := range s.leases {
|
||||||
|
if bytes.Equal(il.HWAddr, mac) && il.IP == netIP {
|
||||||
|
return il
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleRelease is the handler for the DHCP Release request.
|
// handleRelease is the handler for the DHCP Release request.
|
||||||
func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
|
func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||||
mac := req.ClientHWAddr
|
mac := req.ClientHWAddr
|
||||||
|
@ -995,11 +995,80 @@ func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find a lease associated with MAC and prepare response
|
// messageHandler describes a DHCPv4 message handler function.
|
||||||
// Return 1: OK
|
type messageHandler func(s *v4Server, req, resp *dhcpv4.DHCPv4) (rCode int, l *Lease, err error)
|
||||||
// Return 0: error; reply with Nak
|
|
||||||
// Return -1: error; don't reply
|
// messageHandlers is a map of handlers for various messages with message types
|
||||||
func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) int {
|
// keys.
|
||||||
|
var messageHandlers = map[dhcpv4.MessageType]messageHandler{
|
||||||
|
dhcpv4.MessageTypeDiscover: func(
|
||||||
|
s *v4Server,
|
||||||
|
req *dhcpv4.DHCPv4,
|
||||||
|
resp *dhcpv4.DHCPv4,
|
||||||
|
) (rCode int, l *Lease, err error) {
|
||||||
|
l, err = s.handleDiscover(req, resp)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("handling discover: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l == nil {
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1, l, nil
|
||||||
|
},
|
||||||
|
dhcpv4.MessageTypeRequest: func(
|
||||||
|
s *v4Server,
|
||||||
|
req *dhcpv4.DHCPv4,
|
||||||
|
resp *dhcpv4.DHCPv4,
|
||||||
|
) (rCode int, l *Lease, err error) {
|
||||||
|
var toReply bool
|
||||||
|
l, toReply = s.handleRequest(req, resp)
|
||||||
|
if l == nil {
|
||||||
|
if toReply {
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the packet.
|
||||||
|
return -1, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1, l, nil
|
||||||
|
},
|
||||||
|
dhcpv4.MessageTypeDecline: func(
|
||||||
|
s *v4Server,
|
||||||
|
req *dhcpv4.DHCPv4,
|
||||||
|
resp *dhcpv4.DHCPv4,
|
||||||
|
) (rCode int, l *Lease, err error) {
|
||||||
|
err = s.handleDecline(req, resp)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("handling decline: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1, nil, nil
|
||||||
|
},
|
||||||
|
dhcpv4.MessageTypeRelease: func(
|
||||||
|
s *v4Server,
|
||||||
|
req *dhcpv4.DHCPv4,
|
||||||
|
resp *dhcpv4.DHCPv4,
|
||||||
|
) (rCode int, l *Lease, err error) {
|
||||||
|
err = s.handleRelease(req, resp)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("handling release: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1, nil, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle processes request, it finds a lease associated with MAC address and
|
||||||
|
// prepares response.
|
||||||
|
//
|
||||||
|
// Possible return values are:
|
||||||
|
// - "1": OK,
|
||||||
|
// - "0": error, reply with Nak,
|
||||||
|
// - "-1": error, don't reply.
|
||||||
|
func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) (rCode int) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Include server's identifier option since any reply should contain it.
|
// Include server's identifier option since any reply should contain it.
|
||||||
|
@ -1007,47 +1076,26 @@ func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) int {
|
||||||
// See https://datatracker.ietf.org/doc/html/rfc2131#page-29.
|
// See https://datatracker.ietf.org/doc/html/rfc2131#page-29.
|
||||||
resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0].AsSlice()))
|
resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0].AsSlice()))
|
||||||
|
|
||||||
// TODO(a.garipov): Refactor this into handlers.
|
handler := messageHandlers[req.MessageType()]
|
||||||
var l *Lease
|
if handler == nil {
|
||||||
switch mt := req.MessageType(); mt {
|
s.updateOptions(req, resp)
|
||||||
case dhcpv4.MessageTypeDiscover:
|
|
||||||
l, err = s.handleDiscover(req, resp)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("dhcpv4: handling discover: %s", err)
|
|
||||||
|
|
||||||
return 0
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if l == nil {
|
rCode, l, err := handler(s, req, resp)
|
||||||
return 0
|
if err != nil {
|
||||||
}
|
log.Error("dhcpv4: %s", err)
|
||||||
case dhcpv4.MessageTypeRequest:
|
|
||||||
var toReply bool
|
|
||||||
l, toReply = s.handleRequest(req, resp)
|
|
||||||
if l == nil {
|
|
||||||
if toReply {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return -1 // drop packet
|
|
||||||
}
|
|
||||||
case dhcpv4.MessageTypeDecline:
|
|
||||||
err = s.handleDecline(req, resp)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("dhcpv4: handling decline: %s", err)
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
case dhcpv4.MessageTypeRelease:
|
|
||||||
err = s.handleRelease(req, resp)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("dhcpv4: handling release: %s", err)
|
|
||||||
|
|
||||||
return 0
|
if rCode != 1 {
|
||||||
}
|
return rCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if l != nil {
|
if l != nil {
|
||||||
resp.YourIPAddr = net.IP(l.IP.AsSlice())
|
resp.YourIPAddr = l.IP.AsSlice()
|
||||||
}
|
}
|
||||||
|
|
||||||
s.updateOptions(req, resp)
|
s.updateOptions(req, resp)
|
||||||
|
@ -1162,23 +1210,8 @@ func (s *v4Server) Start() (err error) {
|
||||||
// No available IP addresses which may appear later.
|
// No available IP addresses which may appear later.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Update the value of Domain Name Server option separately from others if
|
|
||||||
// not assigned yet since its value is available only at server's start.
|
|
||||||
//
|
|
||||||
// TODO(e.burkov): Initialize as implicit option with the rest of default
|
|
||||||
// options when it will be possible to do before the call to Start.
|
|
||||||
if !s.explicitOpts.Has(dhcpv4.OptionDomainNameServer) {
|
|
||||||
s.implicitOpts.Update(dhcpv4.OptDNS(dnsIPAddrs...))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ip := range dnsIPAddrs {
|
s.configureDNSIPAddrs(dnsIPAddrs)
|
||||||
ip = ip.To4()
|
|
||||||
if ip == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
s.conf.dnsIPAddrs = append(s.conf.dnsIPAddrs, netip.AddrFrom4(*(*[4]byte)(ip)))
|
|
||||||
}
|
|
||||||
|
|
||||||
var c net.PacketConn
|
var c net.PacketConn
|
||||||
if c, err = s.newDHCPConn(iface); err != nil {
|
if c, err = s.newDHCPConn(iface); err != nil {
|
||||||
|
@ -1199,10 +1232,10 @@ func (s *v4Server) Start() (err error) {
|
||||||
log.Info("dhcpv4: listening")
|
log.Info("dhcpv4: listening")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if serr := s.srv.Serve(); errors.Is(serr, net.ErrClosed) {
|
if sErr := s.srv.Serve(); errors.Is(sErr, net.ErrClosed) {
|
||||||
log.Info("dhcpv4: server is closed")
|
log.Info("dhcpv4: server is closed")
|
||||||
} else if serr != nil {
|
} else if sErr != nil {
|
||||||
log.Error("dhcpv4: srv.Serve: %s", serr)
|
log.Error("dhcpv4: srv.Serve: %s", sErr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -1213,6 +1246,28 @@ func (s *v4Server) Start() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configureDNSIPAddrs updates v4Server configuration with provided slice of
|
||||||
|
// dns IP addresses.
|
||||||
|
func (s *v4Server) configureDNSIPAddrs(dnsIPAddrs []net.IP) {
|
||||||
|
// Update the value of Domain Name Server option separately from others if
|
||||||
|
// not assigned yet since its value is available only at server's start.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Initialize as implicit option with the rest of default
|
||||||
|
// options when it will be possible to do before the call to Start.
|
||||||
|
if !s.explicitOpts.Has(dhcpv4.OptionDomainNameServer) {
|
||||||
|
s.implicitOpts.Update(dhcpv4.OptDNS(dnsIPAddrs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range dnsIPAddrs {
|
||||||
|
vAddr, err := netutil.IPToAddr(ip, netutil.AddrFamilyIPv4)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.conf.dnsIPAddrs = append(s.conf.dnsIPAddrs, vAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Stop - stop server
|
// Stop - stop server
|
||||||
func (s *v4Server) Stop() (err error) {
|
func (s *v4Server) Stop() (err error) {
|
||||||
if s.srv == nil {
|
if s.srv == nil {
|
||||||
|
|
|
@ -227,7 +227,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
|
||||||
},
|
},
|
||||||
name: "with_gateway_ip",
|
name: "with_gateway_ip",
|
||||||
wantErrMsg: "dhcpv4: adding static lease: " +
|
wantErrMsg: "dhcpv4: adding static lease: " +
|
||||||
"can't assign the gateway IP 192.168.10.1 to the lease",
|
`can't assign the gateway IP "192.168.10.1" to the lease`,
|
||||||
}, {
|
}, {
|
||||||
lease: &Lease{
|
lease: &Lease{
|
||||||
Hostname: "ip6.local",
|
Hostname: "ip6.local",
|
||||||
|
@ -236,7 +236,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
|
||||||
},
|
},
|
||||||
name: "ipv6",
|
name: "ipv6",
|
||||||
wantErrMsg: `dhcpv4: adding static lease: ` +
|
wantErrMsg: `dhcpv4: adding static lease: ` +
|
||||||
`invalid ip "ffff::1", only ipv4 is supported`,
|
`invalid IP "ffff::1": only IPv4 is supported`,
|
||||||
}, {
|
}, {
|
||||||
lease: &Lease{
|
lease: &Lease{
|
||||||
Hostname: "bad-mac.local",
|
Hostname: "bad-mac.local",
|
||||||
|
|
|
@ -586,9 +586,31 @@ func (s *v6Server) packetHandler(conn net.PacketConn, peer net.Addr, req dhcpv6.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize RA module
|
// configureDNSIPAddrs updates v6Server configuration with the slice of DNS IP
|
||||||
func (s *v6Server) initRA(iface *net.Interface) error {
|
// addresses of provided interface iface. Initializes RA module.
|
||||||
// choose the source IP address - should be link-local-unicast
|
func (s *v6Server) configureDNSIPAddrs(iface *net.Interface) (ok bool, err error) {
|
||||||
|
dnsIPAddrs, err := aghnet.IfaceDNSIPAddrs(
|
||||||
|
iface,
|
||||||
|
aghnet.IPVersion6,
|
||||||
|
defaultMaxAttempts,
|
||||||
|
defaultBackoff,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("interface %s: %w", iface.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dnsIPAddrs) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.conf.dnsIPAddrs = dnsIPAddrs
|
||||||
|
|
||||||
|
return true, s.initRA(iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initRA initializes RA module.
|
||||||
|
func (s *v6Server) initRA(iface *net.Interface) (err error) {
|
||||||
|
// Choose the source IP address - should be link-local-unicast.
|
||||||
s.ra.ipAddr = s.conf.dnsIPAddrs[0]
|
s.ra.ipAddr = s.conf.dnsIPAddrs[0]
|
||||||
for _, ip := range s.conf.dnsIPAddrs {
|
for _, ip := range s.conf.dnsIPAddrs {
|
||||||
if ip.IsLinkLocalUnicast() {
|
if ip.IsLinkLocalUnicast() {
|
||||||
|
@ -604,6 +626,7 @@ func (s *v6Server) initRA(iface *net.Interface) error {
|
||||||
s.ra.ifaceName = s.conf.InterfaceName
|
s.ra.ifaceName = s.conf.InterfaceName
|
||||||
s.ra.iface = iface
|
s.ra.iface = iface
|
||||||
s.ra.packetSendPeriod = 1 * time.Second
|
s.ra.packetSendPeriod = 1 * time.Second
|
||||||
|
|
||||||
return s.ra.Init()
|
return s.ra.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,37 +646,24 @@ func (s *v6Server) Start() (err error) {
|
||||||
|
|
||||||
log.Debug("dhcpv6: starting...")
|
log.Debug("dhcpv6: starting...")
|
||||||
|
|
||||||
dnsIPAddrs, err := aghnet.IfaceDNSIPAddrs(
|
ok, err := s.configureDNSIPAddrs(iface)
|
||||||
iface,
|
|
||||||
aghnet.IPVersion6,
|
|
||||||
defaultMaxAttempts,
|
|
||||||
defaultBackoff,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("interface %s: %w", ifaceName, err)
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dnsIPAddrs) == 0 {
|
if !ok {
|
||||||
// No available IP addresses which may appear later.
|
// No available IP addresses which may appear later.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s.conf.dnsIPAddrs = dnsIPAddrs
|
// Don't initialize DHCPv6 server if we must force the clients to use SLAAC.
|
||||||
|
|
||||||
err = s.initRA(iface)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't initialize DHCPv6 server if we must force the clients to use SLAAC
|
|
||||||
if s.conf.RASLAACOnly {
|
if s.conf.RASLAACOnly {
|
||||||
log.Debug("not starting dhcpv6 server due to ra_slaac_only=true")
|
log.Debug("not starting dhcpv6 server due to ra_slaac_only=true")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("dhcpv6: listening...")
|
|
||||||
|
|
||||||
err = netutil.ValidateMAC(iface.HardwareAddr)
|
err = netutil.ValidateMAC(iface.HardwareAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validating interface %s: %w", iface.Name, err)
|
return fmt.Errorf("validating interface %s: %w", iface.Name, err)
|
||||||
|
@ -665,20 +675,18 @@ func (s *v6Server) Start() (err error) {
|
||||||
Time: dhcpv6.GetTime(),
|
Time: dhcpv6.GetTime(),
|
||||||
}
|
}
|
||||||
|
|
||||||
laddr := &net.UDPAddr{
|
s.srv, err = server6.NewServer(iface.Name, nil, s.packetHandler, server6.WithDebugLogger())
|
||||||
IP: net.ParseIP("::"),
|
|
||||||
Port: dhcpv6.DefaultServerPort,
|
|
||||||
}
|
|
||||||
s.srv, err = server6.NewServer(iface.Name, laddr, s.packetHandler, server6.WithDebugLogger())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug("dhcpv6: listening...")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if serr := s.srv.Serve(); errors.Is(serr, net.ErrClosed) {
|
if sErr := s.srv.Serve(); errors.Is(sErr, net.ErrClosed) {
|
||||||
log.Info("dhcpv6: server is closed")
|
log.Info("dhcpv6: server is closed")
|
||||||
} else if serr != nil {
|
} else if sErr != nil {
|
||||||
log.Error("dhcpv6: srv.Serve: %s", serr)
|
log.Error("dhcpv6: srv.Serve: %s", sErr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -160,29 +160,7 @@ run_linter "$GO" vet ./...
|
||||||
|
|
||||||
run_linter govulncheck ./...
|
run_linter govulncheck ./...
|
||||||
|
|
||||||
# Apply more lax standards to the code we haven't properly refactored yet.
|
run_linter gocyclo --over 10 .
|
||||||
run_linter gocyclo --over 12 ./internal/dhcpd
|
|
||||||
|
|
||||||
# Apply the normal standards to new or somewhat refactored code.
|
|
||||||
run_linter gocyclo --over 10\
|
|
||||||
./internal/aghio/\
|
|
||||||
./internal/aghnet/\
|
|
||||||
./internal/aghos/\
|
|
||||||
./internal/aghtest/\
|
|
||||||
./internal/dnsforward/\
|
|
||||||
./internal/filtering/\
|
|
||||||
./internal/home/\
|
|
||||||
./internal/next/\
|
|
||||||
./internal/querylog/\
|
|
||||||
./internal/stats/\
|
|
||||||
./internal/tools/\
|
|
||||||
./internal/updater/\
|
|
||||||
./internal/version/\
|
|
||||||
./scripts/blocked-services/\
|
|
||||||
./scripts/vetted-filters/\
|
|
||||||
./scripts/translations/\
|
|
||||||
./main.go\
|
|
||||||
;
|
|
||||||
|
|
||||||
run_linter ineffassign ./...
|
run_linter ineffassign ./...
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue