* POST /control/dhcp/find_active_dhcp: add dhcpv6 server info
This commit is contained in:
parent
23752377b7
commit
e7bef3a448
|
@ -500,6 +500,7 @@ Response:
|
||||||
200 OK
|
200 OK
|
||||||
|
|
||||||
{
|
{
|
||||||
|
v4: {
|
||||||
"other_server": {
|
"other_server": {
|
||||||
"found": "yes|no|error",
|
"found": "yes|no|error",
|
||||||
"error": "Error message", // set if found=error
|
"error": "Error message", // set if found=error
|
||||||
|
@ -509,6 +510,13 @@ Response:
|
||||||
"ip": "<Current dynamic IP address>", // set if static=no
|
"ip": "<Current dynamic IP address>", // set if static=no
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
v6: {
|
||||||
|
"other_server": {
|
||||||
|
"found": "yes|no|error",
|
||||||
|
"error": "Error message", // set if found=error
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
If `other_server.found` is:
|
If `other_server.found` is:
|
||||||
* `no`: everything is fine - there is no other DHCP server
|
* `no`: everything is fine - there is no other DHCP server
|
||||||
|
|
|
@ -13,12 +13,14 @@ import (
|
||||||
"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/dhcpv4/nclient4"
|
||||||
|
"github.com/insomniacslk/dhcp/dhcpv6"
|
||||||
|
"github.com/insomniacslk/dhcp/dhcpv6/nclient6"
|
||||||
"github.com/insomniacslk/dhcp/iana"
|
"github.com/insomniacslk/dhcp/iana"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckIfOtherDHCPServersPresent sends a DHCP request to the specified network interface,
|
// CheckIfOtherDHCPServersPresentV4 sends a DHCP request to the specified network interface,
|
||||||
// and waits for a response for a period defined by defaultDiscoverTime
|
// and waits for a response for a period defined by defaultDiscoverTime
|
||||||
func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
|
func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
|
||||||
iface, err := net.InterfaceByName(ifaceName)
|
iface, err := net.InterfaceByName(ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, wrapErrPrint(err, "Couldn't find interface by name %s", ifaceName)
|
return false, wrapErrPrint(err, "Couldn't find interface by name %s", ifaceName)
|
||||||
|
@ -66,12 +68,12 @@ 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 := nclient4.NewRawUDPConn(ifaceName, 68)
|
c, err := nclient4.NewRawUDPConn(ifaceName, 68)
|
||||||
if c != nil {
|
|
||||||
defer c.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, wrapErrPrint(err, "Couldn't listen on :68")
|
return false, wrapErrPrint(err, "Couldn't listen on :68")
|
||||||
}
|
}
|
||||||
|
if c != nil {
|
||||||
|
defer c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// send to 255.255.255.255:67
|
// send to 255.255.255.255:67
|
||||||
_, err = c.WriteTo(req.ToBytes(), dstAddr)
|
_, err = c.WriteTo(req.ToBytes(), dstAddr)
|
||||||
|
@ -119,3 +121,95 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckIfOtherDHCPServersPresentV6 sends a DHCP request to the specified network interface,
|
||||||
|
// and waits for a response for a period defined by defaultDiscoverTime
|
||||||
|
func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
|
||||||
|
iface, err := net.InterfaceByName(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("DHCPv6: net.InterfaceByName: %s: %s", ifaceName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ifaceIPNet := getIfaceIPv6(*iface)
|
||||||
|
if len(ifaceIPNet) == 0 {
|
||||||
|
return false, fmt.Errorf("DHCPv6: couldn't find IPv6 address of interface %s %+v", ifaceName, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcIP := ifaceIPNet[0]
|
||||||
|
src := net.JoinHostPort(srcIP.String(), "546")
|
||||||
|
dst := "[ff02::1:2]:547"
|
||||||
|
|
||||||
|
req, err := dhcpv6.NewSolicit(iface.HardwareAddr)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("DHCPv6: dhcpv6.NewSolicit: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp6", src)
|
||||||
|
if err != nil {
|
||||||
|
return false, wrapErrPrint(err, "DHCPv6: Couldn't resolve UDP address %s", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !udpAddr.IP.To16().Equal(srcIP) {
|
||||||
|
return false, wrapErrPrint(err, "DHCPv6: Resolved UDP address is not %s", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
dstAddr, err := net.ResolveUDPAddr("udp6", dst)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("DHCPv6: Couldn't resolve UDP address %s: %s", dst, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("DHCPv6: Listening to udp6 %+v", udpAddr)
|
||||||
|
c, err := nclient6.NewIPv6UDPConn(ifaceName, dhcpv6.DefaultClientPort)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("DHCPv6: Couldn't listen on :546: %s", err)
|
||||||
|
}
|
||||||
|
if c != nil {
|
||||||
|
defer c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.WriteTo(req.ToBytes(), dstAddr)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("DHCPv6: Couldn't send a packet to %s: %s", dst, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
log.Debug("DHCPv6: Waiting %v for an answer", defaultDiscoverTime)
|
||||||
|
b := make([]byte, 4096)
|
||||||
|
_ = c.SetReadDeadline(time.Now().Add(defaultDiscoverTime))
|
||||||
|
n, _, err := c.ReadFrom(b)
|
||||||
|
if isTimeout(err) {
|
||||||
|
log.Debug("DHCPv6: didn't receive DHCP response")
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, wrapErrPrint(err, "Couldn't receive packet")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("DHCPv6: Received packet (%v bytes)", n)
|
||||||
|
|
||||||
|
resp, err := dhcpv6.FromBytes(b[:n])
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("DHCPv6: dhcpv6.FromBytes: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("DHCPv6: received message from server: %s", resp.Summary())
|
||||||
|
|
||||||
|
cid := req.Options.ClientID()
|
||||||
|
msg, err := resp.GetInnerMessage()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("DHCPv6: resp.GetInnerMessage: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rcid := msg.Options.ClientID()
|
||||||
|
if resp.Type() == dhcpv6.MessageTypeAdvertise &&
|
||||||
|
msg.TransactionID == req.TransactionID &&
|
||||||
|
rcid != nil &&
|
||||||
|
cid.Equal(*rcid) {
|
||||||
|
log.Debug("DHCPv6: The packet is from an active DHCP server")
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("DHCPv6: received message from server doesn't match our request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@ package dhcpd
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
|
func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
|
||||||
|
return false, fmt.Errorf("not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
|
||||||
return false, fmt.Errorf("not supported")
|
return false, fmt.Errorf("not supported")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/util"
|
"github.com/AdguardTeam/AdGuardHome/util"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/golibs/jsonutil"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -129,17 +130,27 @@ type dhcpServerConfigJSON struct {
|
||||||
|
|
||||||
func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
newconfig := dhcpServerConfigJSON{}
|
newconfig := dhcpServerConfigJSON{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&newconfig)
|
newconfig.Enabled = s.conf.Enabled
|
||||||
|
newconfig.InterfaceName = s.conf.InterfaceName
|
||||||
|
|
||||||
|
js, err := jsonutil.DecodeObject(&newconfig, r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(r, w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err)
|
httpError(r, w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var s4 DHCPServer
|
||||||
|
var s6 DHCPServer
|
||||||
|
v4Enabled := false
|
||||||
|
v6Enabled := false
|
||||||
|
|
||||||
|
if js.Exists("v4") {
|
||||||
v4conf := v4JSONToServerConf(newconfig.V4)
|
v4conf := v4JSONToServerConf(newconfig.V4)
|
||||||
v4conf.Enabled = newconfig.Enabled
|
v4conf.Enabled = newconfig.Enabled
|
||||||
if len(v4conf.RangeStart) == 0 {
|
if len(v4conf.RangeStart) == 0 {
|
||||||
v4conf.Enabled = false
|
v4conf.Enabled = false
|
||||||
}
|
}
|
||||||
|
v4Enabled = v4conf.Enabled
|
||||||
v4conf.InterfaceName = newconfig.InterfaceName
|
v4conf.InterfaceName = newconfig.InterfaceName
|
||||||
|
|
||||||
c4 := V4ServerConf{}
|
c4 := V4ServerConf{}
|
||||||
|
@ -147,40 +158,54 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
v4conf.notify = c4.notify
|
v4conf.notify = c4.notify
|
||||||
v4conf.ICMPTimeout = c4.ICMPTimeout
|
v4conf.ICMPTimeout = c4.ICMPTimeout
|
||||||
|
|
||||||
s4, err := v4Create(v4conf)
|
s4, err = v4Create(v4conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(r, w, http.StatusBadRequest, "Invalid DHCPv4 configuration: %s", err)
|
httpError(r, w, http.StatusBadRequest, "Invalid DHCPv4 configuration: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if js.Exists("v6") {
|
||||||
v6conf := v6JSONToServerConf(newconfig.V6)
|
v6conf := v6JSONToServerConf(newconfig.V6)
|
||||||
v6conf.Enabled = newconfig.Enabled
|
v6conf.Enabled = newconfig.Enabled
|
||||||
if len(v6conf.RangeStart) == 0 {
|
if len(v6conf.RangeStart) == 0 {
|
||||||
v6conf.Enabled = false
|
v6conf.Enabled = false
|
||||||
}
|
}
|
||||||
|
v6Enabled = v6conf.Enabled
|
||||||
v6conf.InterfaceName = newconfig.InterfaceName
|
v6conf.InterfaceName = newconfig.InterfaceName
|
||||||
v6conf.notify = s.onNotify
|
v6conf.notify = s.onNotify
|
||||||
s6, err := v6Create(v6conf)
|
s6, err = v6Create(v6conf)
|
||||||
if s6 == nil {
|
if s6 == nil {
|
||||||
httpError(r, w, http.StatusBadRequest, "Invalid DHCPv6 configuration: %s", err)
|
httpError(r, w, http.StatusBadRequest, "Invalid DHCPv6 configuration: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if newconfig.Enabled && !v4conf.Enabled && !v6conf.Enabled {
|
if newconfig.Enabled && !v4Enabled && !v6Enabled {
|
||||||
httpError(r, w, http.StatusBadRequest, "DHCPv4 or DHCPv6 configuration must be complete")
|
httpError(r, w, http.StatusBadRequest, "DHCPv4 or DHCPv6 configuration must be complete")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Stop()
|
s.Stop()
|
||||||
|
|
||||||
|
if js.Exists("enabled") {
|
||||||
s.conf.Enabled = newconfig.Enabled
|
s.conf.Enabled = newconfig.Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if js.Exists("interface_name") {
|
||||||
s.conf.InterfaceName = newconfig.InterfaceName
|
s.conf.InterfaceName = newconfig.InterfaceName
|
||||||
|
}
|
||||||
|
|
||||||
|
if s4 != nil {
|
||||||
s.srv4 = s4
|
s.srv4 = s4
|
||||||
|
}
|
||||||
|
if s6 != nil {
|
||||||
s.srv6 = s6
|
s.srv6 = s6
|
||||||
|
}
|
||||||
s.conf.ConfigModified()
|
s.conf.ConfigModified()
|
||||||
|
s.dbLoad()
|
||||||
|
|
||||||
if newconfig.Enabled {
|
if s.conf.Enabled {
|
||||||
staticIP, err := HasStaticIP(newconfig.InterfaceName)
|
staticIP, err := HasStaticIP(newconfig.InterfaceName)
|
||||||
if !staticIP && err == nil {
|
if !staticIP && err == nil {
|
||||||
err = SetStaticIP(newconfig.InterfaceName)
|
err = SetStaticIP(newconfig.InterfaceName)
|
||||||
|
@ -291,17 +316,7 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
found, err := CheckIfOtherDHCPServersPresent(interfaceName)
|
found4, err4 := CheckIfOtherDHCPServersPresentV4(interfaceName)
|
||||||
|
|
||||||
othSrv := map[string]interface{}{}
|
|
||||||
foundVal := "no"
|
|
||||||
if found {
|
|
||||||
foundVal = "yes"
|
|
||||||
} else if err != nil {
|
|
||||||
foundVal = "error"
|
|
||||||
othSrv["error"] = err.Error()
|
|
||||||
}
|
|
||||||
othSrv["found"] = foundVal
|
|
||||||
|
|
||||||
staticIP := map[string]interface{}{}
|
staticIP := map[string]interface{}{}
|
||||||
isStaticIP, err := HasStaticIP(interfaceName)
|
isStaticIP, err := HasStaticIP(interfaceName)
|
||||||
|
@ -315,9 +330,36 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
|
||||||
}
|
}
|
||||||
staticIP["static"] = staticIPStatus
|
staticIP["static"] = staticIPStatus
|
||||||
|
|
||||||
|
v4 := map[string]interface{}{}
|
||||||
|
othSrv := map[string]interface{}{}
|
||||||
|
foundVal := "no"
|
||||||
|
if found4 {
|
||||||
|
foundVal = "yes"
|
||||||
|
} else if err != nil {
|
||||||
|
foundVal = "error"
|
||||||
|
othSrv["error"] = err4.Error()
|
||||||
|
}
|
||||||
|
othSrv["found"] = foundVal
|
||||||
|
v4["other_server"] = othSrv
|
||||||
|
v4["static_ip"] = staticIP
|
||||||
|
|
||||||
|
found6, err6 := CheckIfOtherDHCPServersPresentV6(interfaceName)
|
||||||
|
|
||||||
|
v6 := map[string]interface{}{}
|
||||||
|
othSrv = map[string]interface{}{}
|
||||||
|
foundVal = "no"
|
||||||
|
if found6 {
|
||||||
|
foundVal = "yes"
|
||||||
|
} else if err6 != nil {
|
||||||
|
foundVal = "error"
|
||||||
|
othSrv["error"] = err6.Error()
|
||||||
|
}
|
||||||
|
othSrv["found"] = foundVal
|
||||||
|
v6["other_server"] = othSrv
|
||||||
|
|
||||||
result := map[string]interface{}{}
|
result := map[string]interface{}{}
|
||||||
result["other_server"] = othSrv
|
result["v4"] = v4
|
||||||
result["static_ip"] = staticIP
|
result["v6"] = v6
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
err = json.NewEncoder(w).Encode(result)
|
err = json.NewEncoder(w).Encode(result)
|
||||||
|
|
|
@ -2,7 +2,7 @@ openapi: 3.0.3
|
||||||
info:
|
info:
|
||||||
title: AdGuard Home
|
title: AdGuard Home
|
||||||
description: AdGuard Home REST API. Admin web interface is built on top of this REST API.
|
description: AdGuard Home REST API. Admin web interface is built on top of this REST API.
|
||||||
version: "0.102"
|
version: "0.104"
|
||||||
contact:
|
contact:
|
||||||
name: "AdGuard Home"
|
name: "AdGuard Home"
|
||||||
url: "https://github.com/AdguardTeam/AdGuardHome"
|
url: "https://github.com/AdguardTeam/AdGuardHome"
|
||||||
|
@ -1356,14 +1356,30 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/DhcpStaticLease"
|
$ref: "#/components/schemas/DhcpStaticLease"
|
||||||
|
|
||||||
DhcpSearchResult:
|
DhcpSearchResult:
|
||||||
type: object
|
type: object
|
||||||
description: Information about a DHCP server discovered in the current network
|
description: Information about a DHCP server discovered in the current network
|
||||||
|
properties:
|
||||||
|
v4:
|
||||||
|
$ref: "#/components/schemas/DhcpSearchV4"
|
||||||
|
v6:
|
||||||
|
$ref: "#/components/schemas/DhcpSearchV6"
|
||||||
|
|
||||||
|
DhcpSearchV4:
|
||||||
|
type: object
|
||||||
properties:
|
properties:
|
||||||
other_server:
|
other_server:
|
||||||
$ref: "#/components/schemas/DhcpSearchResultOtherServer"
|
$ref: "#/components/schemas/DhcpSearchResultOtherServer"
|
||||||
static_ip:
|
static_ip:
|
||||||
$ref: "#/components/schemas/DhcpSearchResultStaticIP"
|
$ref: "#/components/schemas/DhcpSearchResultStaticIP"
|
||||||
|
|
||||||
|
DhcpSearchV6:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
other_server:
|
||||||
|
$ref: "#/components/schemas/DhcpSearchResultOtherServer"
|
||||||
|
|
||||||
DhcpSearchResultOtherServer:
|
DhcpSearchResultOtherServer:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -1375,6 +1391,7 @@ components:
|
||||||
type: string
|
type: string
|
||||||
description: Set if found=error
|
description: Set if found=error
|
||||||
example: ""
|
example: ""
|
||||||
|
|
||||||
DhcpSearchResultStaticIP:
|
DhcpSearchResultStaticIP:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -1386,6 +1403,7 @@ components:
|
||||||
type: string
|
type: string
|
||||||
description: Set if static=no
|
description: Set if static=no
|
||||||
example: ""
|
example: ""
|
||||||
|
|
||||||
DnsAnswer:
|
DnsAnswer:
|
||||||
type: object
|
type: object
|
||||||
description: DNS answer section
|
description: DNS answer section
|
||||||
|
|
Loading…
Reference in New Issue