dhcpsvc: imp code, names, docs

This commit is contained in:
Eugene Burkov 2024-01-25 20:44:09 +03:00
parent a92f44c752
commit 120c0472f3
5 changed files with 95 additions and 78 deletions

View File

@ -0,0 +1,42 @@
package dhcpsvc
import (
"fmt"
"time"
"golang.org/x/exp/slices"
)
// netInterface is a common part of any network interface within the DHCP
// server.
//
// TODO(e.burkov): Add other methods as [DHCPServer] evolves.
type netInterface struct {
// name is the name of the network interface.
name string
// leases is a set of leases sorted by hardware address.
leases []*Lease
// leaseTTL is the default Time-To-Live value for leases.
leaseTTL time.Duration
}
// reset clears all the slices in the interface for reuse.
func (iface *netInterface) reset() {
iface.leases = iface.leases[:0]
}
// insertLease inserts the given lease into the interface. It returns an
// error if the lease can't be inserted. The error should be informative
// enough to be returned as is.
func (iface *netInterface) insertLease(l *Lease) (err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
if found {
return fmt.Errorf("lease for mac %s already exists", l.HWAddr)
}
iface.leases = slices.Insert(iface.leases, i, l)
return nil
}

View File

@ -2,7 +2,6 @@ package dhcpsvc
import (
"bytes"
"fmt"
"net"
"net/netip"
"time"
@ -48,38 +47,7 @@ func (l *Lease) Clone() (clone *Lease) {
}
}
// leaseHandler is an interface for handler of leases. It's used to access
// leases in a generic way.
//
// TODO(e.burkov): Add other methods as [DHCPServer] evolves.
type leaseHandler struct {
// leases is a set of leases sorted by hardware address.
leases []*Lease
// leaseTTL is the default Time-To-Live value for leases.
leaseTTL time.Duration
}
// reset clears all the slices in the interface for reuse.
func (lh *leaseHandler) reset() {
lh.leases = lh.leases[:0]
}
// compareLeaseMAC compares two [Lease]s by hardware address.
func compareLeaseMAC(a, b *Lease) (res int) {
return bytes.Compare(a.HWAddr, b.HWAddr)
}
// insertLease inserts the given lease into the interface. It returns an
// error if the lease can't be inserted. The error should be informative
// enough to be returned as is.
func (lh *leaseHandler) insertLease(l *Lease) (err error) {
i, found := slices.BinarySearchFunc(lh.leases, l, compareLeaseMAC)
if found {
return fmt.Errorf("lease for mac %s already exists", l.HWAddr)
}
lh.leases = slices.Insert(lh.leases, i, l)
return nil
}

View File

@ -179,43 +179,56 @@ func (srv *DHCPServer) Reset() (err error) {
return nil
}
// leasesHandlerForAddr returns the corresponding leasesHandler for the given IP
// address.
func (srv *DHCPServer) leasesHandlerForAddr(addr netip.Addr) (iface *leaseHandler, err error) {
switch {
case addr.Is4():
i := slices.IndexFunc(srv.interfaces4, func(i *netInterfaceV4) (ok bool) {
return i.Contains(addr)
})
if i >= 0 {
return &srv.interfaces4[i].leaseHandler, nil
}
case addr.Is6():
i := slices.IndexFunc(srv.interfaces6, func(i *netInterfaceV6) (ok bool) {
return i.Contains(addr)
})
if i >= 0 {
return &srv.interfaces6[i].leaseHandler, nil
}
default:
return nil, fmt.Errorf("invalid IP address %s", addr)
}
return nil, fmt.Errorf("no interface for IP address %s", addr)
}
// AddLease implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) AddLease(l *Lease) (err error) {
leases, err := srv.leasesHandlerForAddr(l.IP)
if err != nil {
return err
if l.IP.Is4() {
err = srv.addLease4(l)
} else {
err = srv.addLease6(l)
}
return err
}
// addLease4 adds l to the corresponding network interface of srv. The address
// of l must have the IPv4 family.
func (srv *DHCPServer) addLease4(l *Lease) (err error) {
i := slices.IndexFunc(srv.interfaces4, func(iface *netInterfaceV4) (ok bool) {
return iface.Contains(l.IP)
})
if i < 0 {
return fmt.Errorf("no interface for IP address %s", l.IP)
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
if err = leases.insertLease(l); err != nil {
// Don't wrap the error, since it's already informative enough as is.
err = srv.interfaces4[i].insertLease(l)
if err != nil {
return err
}
srv.leaseByIP[l.IP] = l
srv.leaseByName[strings.ToLower(l.Hostname)] = l
return nil
}
// addLease6 adds l to the corresponding network interface of srv. The address
// of l must have the IPv6 family.
func (srv *DHCPServer) addLease6(l *Lease) (err error) {
i := slices.IndexFunc(srv.interfaces6, func(iface *netInterfaceV6) (ok bool) {
return iface.Contains(l.IP)
})
if i < 0 {
return fmt.Errorf("no interface for IP address %s", l.IP)
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
err = srv.interfaces6[i].insertLease(l)
if err != nil {
return err
}

View File

@ -267,9 +267,6 @@ type netInterfaceV4 struct {
// addrSpace is the IPv4 address space allocated for leasing.
addrSpace ipRange
// name is the name of the interface.
name string
// implicitOpts are the options listed in Appendix A of RFC 2131 and
// initialized with default values. It must not have intersections with
// explicitOpts.
@ -279,9 +276,9 @@ type netInterfaceV4 struct {
// intersections with implicitOpts.
explicitOpts layers.DHCPOptions
// leaseHandler stores and handles leases. It should only be accessed under
// the common lock.
leaseHandler
// netInterface is embedded here to provide some common network interface
// logic.
netInterface
}
// newNetInterfaceV4 creates a new DHCP interface for IPv4 address family with
@ -310,11 +307,11 @@ func newNetInterfaceV4(name string, conf *IPv4Config) (i *netInterfaceV4, err er
}
i = &netInterfaceV4{
name: name,
gateway: conf.GatewayIP,
subnet: subnet,
addrSpace: addrSpace,
leaseHandler: leaseHandler{
netInterface: netInterface{
name: name,
leaseTTL: conf.LeaseDuration,
},
}

View File

@ -90,9 +90,6 @@ type netInterfaceV6 struct {
// rangeStart is the first IP address in the range.
rangeStart netip.Addr
// name is the name of the interface.
name string
// implicitOpts are the DHCPv6 options listed in RFC 8415 (and others) and
// initialized with default values. It must not have intersections with
// explicitOpts.
@ -102,9 +99,9 @@ type netInterfaceV6 struct {
// intersections with implicitOpts.
explicitOpts layers.DHCPv6Options
// leaseHandler stores and handles leases. It should only be accessed under
// the common lock.
leaseHandler
// netInterface is embedded here to provide some common network interface
// logic.
netInterface
// raSLAACOnly defines if DHCP should send ICMPv6.RA packets without MO
// flags.
@ -124,9 +121,9 @@ func newNetInterfaceV6(name string, conf *IPv6Config) (i *netInterfaceV6) {
}
i = &netInterfaceV6{
name: name,
rangeStart: conf.RangeStart,
leaseHandler: leaseHandler{
netInterface: netInterface{
name: name,
leaseTTL: conf.LeaseDuration,
},
raSLAACOnly: conf.RASLAACOnly,