dhcpd: add lease json form

This commit is contained in:
Stanislav Chzhen 2023-04-05 16:53:15 +03:00
parent 9ebc246ed4
commit ecd244a608
3 changed files with 104 additions and 27 deletions

View File

@ -102,9 +102,8 @@ func (l Lease) MarshalJSON() ([]byte, error) {
type lease Lease type lease Lease
return json.Marshal(&struct { return json.Marshal(&struct {
HWAddr string `json:"mac"` HWAddr string `json:"mac"`
Expiry string `json:"expires,omitempty"` Expiry string `json:"expires,omitempty"`
IsStatic bool `json:"static,omitempty"`
lease lease
}{ }{
HWAddr: l.HWAddr.String(), HWAddr: l.HWAddr.String(),

View File

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"net/netip" "net/netip"
"os" "os"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
@ -57,12 +58,77 @@ func v6JSONToServerConf(j *v6ServerConfJSON) V6ServerConf {
// dhcpStatusResponse is the response for /control/dhcp/status endpoint. // dhcpStatusResponse is the response for /control/dhcp/status endpoint.
type dhcpStatusResponse struct { type dhcpStatusResponse struct {
IfaceName string `json:"interface_name"` IfaceName string `json:"interface_name"`
V4 V4ServerConf `json:"v4"` V4 V4ServerConf `json:"v4"`
V6 V6ServerConf `json:"v6"` V6 V6ServerConf `json:"v6"`
Leases []*Lease `json:"leases"` Leases []*leaseDynamic `json:"leases"`
StaticLeases []*Lease `json:"static_leases"` StaticLeases []*leaseStatic `json:"static_leases"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
}
// leaseStatic is the JSON form of static DHCP lease.
type leaseStatic struct {
HWAddr string `json:"mac"`
IP netip.Addr `json:"ip"`
Hostname string `json:"hostname"`
}
// leasesToStatic converts list of leases to their JSON form.
func leasesToStatic(leases []*Lease) (static []*leaseStatic) {
static = make([]*leaseStatic, len(leases))
for i, l := range leases {
static[i] = &leaseStatic{
HWAddr: l.HWAddr.String(),
IP: l.IP,
Hostname: l.Hostname,
}
}
return static
}
// Unwrap converts leaseStatic to Lease or returns error.
func (l *leaseStatic) Unwrap() (lease *Lease, err error) {
addr, err := net.ParseMAC(l.HWAddr)
if err != nil {
return nil, fmt.Errorf("couldn't parse MAC address: %w", err)
}
return &Lease{
HWAddr: addr,
IP: l.IP,
Hostname: l.Hostname,
IsStatic: true,
}, nil
}
// leaseDynamic is the JSON form of dynamic DHCP lease.
type leaseDynamic struct {
HWAddr string `json:"mac"`
IP netip.Addr `json:"ip"`
Hostname string `json:"hostname"`
Expiry string `json:"expires"`
}
// leasesToDynamic converts list of leases to their JSON form.
func leasesToDynamic(leases []*Lease) (dynamic []*leaseDynamic) {
dynamic = make([]*leaseDynamic, len(leases))
for i, l := range leases {
dynamic[i] = &leaseDynamic{
HWAddr: l.HWAddr.String(),
IP: l.IP,
Hostname: l.Hostname,
// The front-end is waiting for RFC 3999 format of the time
// value.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2692.
Expiry: l.Expiry.Format(time.RFC3339),
}
}
return dynamic
} }
func (s *server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) { func (s *server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
@ -76,8 +142,8 @@ func (s *server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
s.srv4.WriteDiskConfig4(&status.V4) s.srv4.WriteDiskConfig4(&status.V4)
s.srv6.WriteDiskConfig6(&status.V6) s.srv6.WriteDiskConfig6(&status.V6)
status.Leases = s.Leases(LeasesDynamic) status.Leases = leasesToDynamic(s.Leases(LeasesDynamic))
status.StaticLeases = s.Leases(LeasesStatic) status.StaticLeases = leasesToStatic(s.Leases(LeasesStatic))
_ = aghhttp.WriteJSONResponse(w, r, status) _ = aghhttp.WriteJSONResponse(w, r, status)
} }
@ -488,7 +554,7 @@ func setOtherDHCPResult(ifaceName string, result *dhcpSearchResult) {
} }
func (s *server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) { func (s *server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) {
l := &Lease{} l := &leaseStatic{}
err := json.NewDecoder(r.Body).Decode(l) err := json.NewDecoder(r.Body).Decode(l)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err) aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
@ -511,7 +577,14 @@ func (s *server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
srv = s.srv6 srv = s.srv6
} }
err = srv.AddStaticLease(l) lease, err := l.Unwrap()
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "parsing: %s", err)
return
}
err = srv.AddStaticLease(lease)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err) aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@ -520,7 +593,7 @@ func (s *server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
} }
func (s *server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) { func (s *server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) {
l := &Lease{} l := &leaseStatic{}
err := json.NewDecoder(r.Body).Decode(l) err := json.NewDecoder(r.Body).Decode(l)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err) aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
@ -543,7 +616,14 @@ func (s *server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Requ
srv = s.srv6 srv = s.srv6
} }
err = srv.RemoveStaticLease(l) lease, err := l.Unwrap()
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "parsing: %s", err)
return
}
err = srv.RemoveStaticLease(lease)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err) aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)

View File

@ -5,29 +5,27 @@ package dhcpd
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/netip" "net/netip"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestServer_handleDHCPStatus(t *testing.T) { func TestServer_handleDHCPStatus(t *testing.T) {
const staticName = "static-client" const (
staticName = "static-client"
staticMAC = "aa:aa:aa:aa:aa:aa"
)
staticIP := netip.MustParseAddr("192.168.10.10") staticIP := netip.MustParseAddr("192.168.10.10")
staticMAC := net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}
staticLease := &Lease{ staticLease := &leaseStatic{
Expiry: time.Unix(leaseExpireStatic, 0),
Hostname: staticName,
HWAddr: staticMAC, HWAddr: staticMAC,
IP: staticIP, IP: staticIP,
IsStatic: true, Hostname: staticName,
} }
s, err := Create(&ServerConfig{ s, err := Create(&ServerConfig{
@ -66,8 +64,8 @@ func TestServer_handleDHCPStatus(t *testing.T) {
resp := &dhcpStatusResponse{ resp := &dhcpStatusResponse{
V4: *conf4, V4: *conf4,
V6: V6ServerConf{}, V6: V6ServerConf{},
Leases: []*Lease{}, Leases: []*leaseDynamic{},
StaticLeases: []*Lease{}, StaticLeases: []*leaseStatic{},
Enabled: true, Enabled: true,
} }
@ -96,7 +94,7 @@ func TestServer_handleDHCPStatus(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
resp := defaultResponse() resp := defaultResponse()
resp.StaticLeases = []*Lease{staticLease} resp.StaticLeases = []*leaseStatic{staticLease}
checkStatus(t, resp) checkStatus(t, resp)
}) })
@ -107,7 +105,7 @@ func TestServer_handleDHCPStatus(t *testing.T) {
b := &bytes.Buffer{} b := &bytes.Buffer{}
err = json.NewEncoder(b).Encode(&Lease{}) err = json.NewEncoder(b).Encode(&leaseStatic{})
require.NoError(t, err) require.NoError(t, err)
var r *http.Request var r *http.Request