Pull request 2256: 4923 Better interfaces
Updates #4923. Squashed commit of the following: commit 0e40b41aa1e517a62d6076c4e7a57c607792ef01 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Jul 10 15:28:16 2024 +0300 dhcpsvc: imp code, docs commit 5463fdde473f84caaca229b53027e8183d5c6bdc Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Jul 9 20:31:20 2024 +0300 dhcpsvc: imp ifaces
This commit is contained in:
parent
c0a33ce708
commit
42c7cd6f8e
|
@ -50,7 +50,7 @@ type Interface interface {
|
||||||
IPByHost(host string) (ip netip.Addr)
|
IPByHost(host string) (ip netip.Addr)
|
||||||
|
|
||||||
// Leases returns all the active DHCP leases. The returned slice should be
|
// Leases returns all the active DHCP leases. The returned slice should be
|
||||||
// a clone.
|
// a clone. The order of leases is undefined.
|
||||||
//
|
//
|
||||||
// TODO(e.burkov): Consider implementing iterating methods with appropriate
|
// TODO(e.burkov): Consider implementing iterating methods with appropriate
|
||||||
// signatures instead of cloning the whole list.
|
// signatures instead of cloning the whole list.
|
||||||
|
|
|
@ -3,42 +3,74 @@ package dhcpsvc
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"slices"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// netInterface is a common part of any network interface within the DHCP
|
// macKey contains hardware address as byte array of 6, 8, or 20 bytes.
|
||||||
// server.
|
//
|
||||||
|
// TODO(e.burkov): Move to aghnet or even to netutil.
|
||||||
|
type macKey any
|
||||||
|
|
||||||
|
// macToKey converts mac into macKey, which is used as the key for the lease
|
||||||
|
// maps. mac must be a valid hardware address of length 6, 8, or 20 bytes, see
|
||||||
|
// [netutil.ValidateMAC].
|
||||||
|
func macToKey(mac net.HardwareAddr) (key macKey) {
|
||||||
|
switch len(mac) {
|
||||||
|
case 6:
|
||||||
|
return [6]byte(mac)
|
||||||
|
case 8:
|
||||||
|
return [8]byte(mac)
|
||||||
|
case 20:
|
||||||
|
return [20]byte(mac)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("invalid mac address %#v", mac))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// netInterface is a common part of any interface within the DHCP server.
|
||||||
//
|
//
|
||||||
// TODO(e.burkov): Add other methods as [DHCPServer] evolves.
|
// TODO(e.burkov): Add other methods as [DHCPServer] evolves.
|
||||||
type netInterface struct {
|
type netInterface struct {
|
||||||
// logger logs the events related to the network interface.
|
// logger logs the events related to the network interface.
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
|
||||||
|
// leases is the set of DHCP leases assigned to this interface.
|
||||||
|
leases map[macKey]*Lease
|
||||||
|
|
||||||
// name is the name of the network interface.
|
// name is the name of the network interface.
|
||||||
name string
|
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 is the default Time-To-Live value for leases.
|
||||||
leaseTTL time.Duration
|
leaseTTL time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset clears all the slices in iface for reuse.
|
// newNetInterface creates a new netInterface with the given name, leaseTTL, and
|
||||||
func (iface *netInterface) reset() {
|
// logger.
|
||||||
iface.leases = iface.leases[:0]
|
func newNetInterface(name string, l *slog.Logger, leaseTTL time.Duration) (iface *netInterface) {
|
||||||
|
return &netInterface{
|
||||||
|
logger: l,
|
||||||
|
leases: map[macKey]*Lease{},
|
||||||
|
name: name,
|
||||||
|
leaseTTL: leaseTTL,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertLease inserts the given lease into iface. It returns an error if the
|
// reset clears all the slices in iface for reuse.
|
||||||
|
func (iface *netInterface) reset() {
|
||||||
|
clear(iface.leases)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addLease inserts the given lease into iface. It returns an error if the
|
||||||
// lease can't be inserted.
|
// lease can't be inserted.
|
||||||
func (iface *netInterface) insertLease(l *Lease) (err error) {
|
func (iface *netInterface) addLease(l *Lease) (err error) {
|
||||||
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
|
mk := macToKey(l.HWAddr)
|
||||||
|
_, found := iface.leases[mk]
|
||||||
if found {
|
if found {
|
||||||
return fmt.Errorf("lease for mac %s already exists", l.HWAddr)
|
return fmt.Errorf("lease for mac %s already exists", l.HWAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
iface.leases = slices.Insert(iface.leases, i, l)
|
iface.leases[mk] = l
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -46,12 +78,13 @@ func (iface *netInterface) insertLease(l *Lease) (err error) {
|
||||||
// updateLease replaces an existing lease within iface with the given one. It
|
// updateLease replaces an existing lease within iface with the given one. It
|
||||||
// returns an error if there is no lease with such hardware address.
|
// returns an error if there is no lease with such hardware address.
|
||||||
func (iface *netInterface) updateLease(l *Lease) (prev *Lease, err error) {
|
func (iface *netInterface) updateLease(l *Lease) (prev *Lease, err error) {
|
||||||
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
|
mk := macToKey(l.HWAddr)
|
||||||
|
prev, found := iface.leases[mk]
|
||||||
if !found {
|
if !found {
|
||||||
return nil, fmt.Errorf("no lease for mac %s", l.HWAddr)
|
return nil, fmt.Errorf("no lease for mac %s", l.HWAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
prev, iface.leases[i] = iface.leases[i], l
|
iface.leases[mk] = l
|
||||||
|
|
||||||
return prev, nil
|
return prev, nil
|
||||||
}
|
}
|
||||||
|
@ -59,12 +92,13 @@ func (iface *netInterface) updateLease(l *Lease) (prev *Lease, err error) {
|
||||||
// removeLease removes an existing lease from iface. It returns an error if
|
// removeLease removes an existing lease from iface. It returns an error if
|
||||||
// there is no lease equal to l.
|
// there is no lease equal to l.
|
||||||
func (iface *netInterface) removeLease(l *Lease) (err error) {
|
func (iface *netInterface) removeLease(l *Lease) (err error) {
|
||||||
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
|
mk := macToKey(l.HWAddr)
|
||||||
|
_, found := iface.leases[mk]
|
||||||
if !found {
|
if !found {
|
||||||
return fmt.Errorf("no lease for mac %s", l.HWAddr)
|
return fmt.Errorf("no lease for mac %s", l.HWAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
iface.leases = slices.Delete(iface.leases, i, i+1)
|
delete(iface.leases, mk)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package dhcpsvc
|
package dhcpsvc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
|
@ -45,8 +44,3 @@ func (l *Lease) Clone() (clone *Lease) {
|
||||||
IsStatic: l.IsStatic,
|
IsStatic: l.IsStatic,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compareLeaseMAC compares two [Lease]s by hardware address.
|
|
||||||
func compareLeaseMAC(a, b *Lease) (res int) {
|
|
||||||
return bytes.Compare(a.HWAddr, b.HWAddr)
|
|
||||||
}
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (idx *leaseIndex) add(l *Lease, iface *netInterface) (err error) {
|
||||||
return fmt.Errorf("lease for hostname %s already exists", l.Hostname)
|
return fmt.Errorf("lease for hostname %s already exists", l.Hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = iface.insertLease(l)
|
err = iface.addLease(l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,10 +41,10 @@ type DHCPServer struct {
|
||||||
leases *leaseIndex
|
leases *leaseIndex
|
||||||
|
|
||||||
// interfaces4 is the set of IPv4 interfaces sorted by interface name.
|
// interfaces4 is the set of IPv4 interfaces sorted by interface name.
|
||||||
interfaces4 netInterfacesV4
|
interfaces4 dhcpInterfacesV4
|
||||||
|
|
||||||
// interfaces6 is the set of IPv6 interfaces sorted by interface name.
|
// interfaces6 is the set of IPv6 interfaces sorted by interface name.
|
||||||
interfaces6 netInterfacesV6
|
interfaces6 dhcpInterfacesV6
|
||||||
|
|
||||||
// icmpTimeout is the timeout for checking another DHCP server's presence.
|
// icmpTimeout is the timeout for checking another DHCP server's presence.
|
||||||
icmpTimeout time.Duration
|
icmpTimeout time.Duration
|
||||||
|
@ -63,28 +63,9 @@ func New(ctx context.Context, conf *Config) (srv *DHCPServer, err error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(e.burkov): Add validations scoped to the network interfaces set.
|
ifaces4, ifaces6, err := newInterfaces(ctx, l, conf.Interfaces)
|
||||||
ifaces4 := make(netInterfacesV4, 0, len(conf.Interfaces))
|
|
||||||
ifaces6 := make(netInterfacesV6, 0, len(conf.Interfaces))
|
|
||||||
var errs []error
|
|
||||||
|
|
||||||
mapsutil.SortedRange(conf.Interfaces, func(name string, iface *InterfaceConfig) (cont bool) {
|
|
||||||
var i4 *netInterfaceV4
|
|
||||||
i4, err = newNetInterfaceV4(ctx, l, name, iface.IPv4)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, fmt.Errorf("interface %q: ipv4: %w", name, err))
|
// Don't wrap the error since it's informative enough as is.
|
||||||
} else if i4 != nil {
|
|
||||||
ifaces4 = append(ifaces4, i4)
|
|
||||||
}
|
|
||||||
|
|
||||||
i6 := newNetInterfaceV6(ctx, l, name, iface.IPv6)
|
|
||||||
if i6 != nil {
|
|
||||||
ifaces6 = append(ifaces6, i6)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err = errors.Join(errs...); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +93,43 @@ func New(ctx context.Context, conf *Config) (srv *DHCPServer, err error) {
|
||||||
return srv, nil
|
return srv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newInterfaces creates interfaces for the given map of interface names to
|
||||||
|
// their configurations.
|
||||||
|
func newInterfaces(
|
||||||
|
ctx context.Context,
|
||||||
|
l *slog.Logger,
|
||||||
|
ifaces map[string]*InterfaceConfig,
|
||||||
|
) (v4 dhcpInterfacesV4, v6 dhcpInterfacesV6, err error) {
|
||||||
|
defer func() { err = errors.Annotate(err, "creating interfaces: %w") }()
|
||||||
|
|
||||||
|
// TODO(e.burkov): Add validations scoped to the network interfaces set.
|
||||||
|
v4 = make(dhcpInterfacesV4, 0, len(ifaces))
|
||||||
|
v6 = make(dhcpInterfacesV6, 0, len(ifaces))
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
mapsutil.SortedRange(ifaces, func(name string, iface *InterfaceConfig) (cont bool) {
|
||||||
|
var i4 *dhcpInterfaceV4
|
||||||
|
i4, err = newDHCPInterfaceV4(ctx, l, name, iface.IPv4)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("interface %q: ipv4: %w", name, err))
|
||||||
|
} else if i4 != nil {
|
||||||
|
v4 = append(v4, i4)
|
||||||
|
}
|
||||||
|
|
||||||
|
i6 := newDHCPInterfaceV6(ctx, l, name, iface.IPv6)
|
||||||
|
if i6 != nil {
|
||||||
|
v6 = append(v6, i6)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if err = errors.Join(errs...); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v4, v6, nil
|
||||||
|
}
|
||||||
|
|
||||||
// type check
|
// type check
|
||||||
//
|
//
|
||||||
// TODO(e.burkov): Uncomment when the [Interface] interface is implemented.
|
// TODO(e.burkov): Uncomment when the [Interface] interface is implemented.
|
||||||
|
@ -127,16 +145,11 @@ func (srv *DHCPServer) Leases() (leases []*Lease) {
|
||||||
srv.leasesMu.RLock()
|
srv.leasesMu.RLock()
|
||||||
defer srv.leasesMu.RUnlock()
|
defer srv.leasesMu.RUnlock()
|
||||||
|
|
||||||
for _, iface := range srv.interfaces4 {
|
srv.leases.rangeLeases(func(l *Lease) (cont bool) {
|
||||||
for _, lease := range iface.leases {
|
leases = append(leases, l.Clone())
|
||||||
leases = append(leases, lease.Clone())
|
|
||||||
}
|
return true
|
||||||
}
|
})
|
||||||
for _, iface := range srv.interfaces6 {
|
|
||||||
for _, lease := range iface.leases {
|
|
||||||
leases = append(leases, lease.Clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return leases
|
return leases
|
||||||
}
|
}
|
||||||
|
@ -200,10 +213,10 @@ func (srv *DHCPServer) Reset(ctx context.Context) (err error) {
|
||||||
// expects the DHCPServer.leasesMu to be locked.
|
// expects the DHCPServer.leasesMu to be locked.
|
||||||
func (srv *DHCPServer) resetLeases() {
|
func (srv *DHCPServer) resetLeases() {
|
||||||
for _, iface := range srv.interfaces4 {
|
for _, iface := range srv.interfaces4 {
|
||||||
iface.reset()
|
iface.common.reset()
|
||||||
}
|
}
|
||||||
for _, iface := range srv.interfaces6 {
|
for _, iface := range srv.interfaces6 {
|
||||||
iface.reset()
|
iface.common.reset()
|
||||||
}
|
}
|
||||||
srv.leases.clear()
|
srv.leases.clear()
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ func TestNew(t *testing.T) {
|
||||||
DBFilePath: leasesPath,
|
DBFilePath: leasesPath,
|
||||||
},
|
},
|
||||||
name: "gateway_within_range",
|
name: "gateway_within_range",
|
||||||
wantErrMsg: `interface "eth0": ipv4: ` +
|
wantErrMsg: `creating interfaces: interface "eth0": ipv4: ` +
|
||||||
`gateway ip 192.168.0.100 in the ip range 192.168.0.1-192.168.0.254`,
|
`gateway ip 192.168.0.100 in the ip range 192.168.0.1-192.168.0.254`,
|
||||||
}, {
|
}, {
|
||||||
conf: &dhcpsvc.Config{
|
conf: &dhcpsvc.Config{
|
||||||
|
@ -138,7 +138,7 @@ func TestNew(t *testing.T) {
|
||||||
DBFilePath: leasesPath,
|
DBFilePath: leasesPath,
|
||||||
},
|
},
|
||||||
name: "bad_start",
|
name: "bad_start",
|
||||||
wantErrMsg: `interface "eth0": ipv4: ` +
|
wantErrMsg: `creating interfaces: interface "eth0": ipv4: ` +
|
||||||
`range start 127.0.0.1 is not within 192.168.0.1/24`,
|
`range start 127.0.0.1 is not within 192.168.0.1/24`,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@ -568,5 +568,5 @@ func TestServer_Leases(t *testing.T) {
|
||||||
HWAddr: mustParseMAC(t, "BB:BB:BB:BB:BB:BB"),
|
HWAddr: mustParseMAC(t, "BB:BB:BB:BB:BB:BB"),
|
||||||
IsStatic: true,
|
IsStatic: true,
|
||||||
}}
|
}}
|
||||||
assert.Equal(t, wantLeases, srv.Leases())
|
assert.ElementsMatch(t, wantLeases, srv.Leases())
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,8 +82,12 @@ func (c *IPv4Config) validate() (err error) {
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// netInterfaceV4 is a DHCP interface for IPv4 address family.
|
// dhcpInterfaceV4 is a DHCP interface for IPv4 address family.
|
||||||
type netInterfaceV4 struct {
|
type dhcpInterfaceV4 struct {
|
||||||
|
// common is the common part of any network interface within the DHCP
|
||||||
|
// server.
|
||||||
|
common *netInterface
|
||||||
|
|
||||||
// gateway is the IP address of the network gateway.
|
// gateway is the IP address of the network gateway.
|
||||||
gateway netip.Addr
|
gateway netip.Addr
|
||||||
|
|
||||||
|
@ -101,21 +105,17 @@ type netInterfaceV4 struct {
|
||||||
// explicitOpts are the user-configured options. It must not have
|
// explicitOpts are the user-configured options. It must not have
|
||||||
// intersections with implicitOpts.
|
// intersections with implicitOpts.
|
||||||
explicitOpts layers.DHCPOptions
|
explicitOpts layers.DHCPOptions
|
||||||
|
|
||||||
// netInterface is embedded here to provide some common network interface
|
|
||||||
// logic.
|
|
||||||
netInterface
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNetInterfaceV4 creates a new DHCP interface for IPv4 address family with
|
// newDHCPInterfaceV4 creates a new DHCP interface for IPv4 address family with
|
||||||
// the given configuration. It returns an error if the given configuration
|
// the given configuration. It returns an error if the given configuration
|
||||||
// can't be used.
|
// can't be used.
|
||||||
func newNetInterfaceV4(
|
func newDHCPInterfaceV4(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
l *slog.Logger,
|
l *slog.Logger,
|
||||||
name string,
|
name string,
|
||||||
conf *IPv4Config,
|
conf *IPv4Config,
|
||||||
) (i *netInterfaceV4, err error) {
|
) (i *dhcpInterfaceV4, err error) {
|
||||||
l = l.With(
|
l = l.With(
|
||||||
keyInterface, name,
|
keyInterface, name,
|
||||||
keyFamily, netutil.AddrFamilyIPv4,
|
keyFamily, netutil.AddrFamilyIPv4,
|
||||||
|
@ -144,35 +144,31 @@ func newNetInterfaceV4(
|
||||||
return nil, fmt.Errorf("gateway ip %s in the ip range %s", conf.GatewayIP, addrSpace)
|
return nil, fmt.Errorf("gateway ip %s in the ip range %s", conf.GatewayIP, addrSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
i = &netInterfaceV4{
|
i = &dhcpInterfaceV4{
|
||||||
gateway: conf.GatewayIP,
|
gateway: conf.GatewayIP,
|
||||||
subnet: subnet,
|
subnet: subnet,
|
||||||
addrSpace: addrSpace,
|
addrSpace: addrSpace,
|
||||||
netInterface: netInterface{
|
common: newNetInterface(name, l, conf.LeaseDuration),
|
||||||
name: name,
|
|
||||||
leaseTTL: conf.LeaseDuration,
|
|
||||||
logger: l,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
i.implicitOpts, i.explicitOpts = conf.options(ctx, l)
|
i.implicitOpts, i.explicitOpts = conf.options(ctx, l)
|
||||||
|
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// netInterfacesV4 is a slice of network interfaces of IPv4 address family.
|
// dhcpInterfacesV4 is a slice of network interfaces of IPv4 address family.
|
||||||
type netInterfacesV4 []*netInterfaceV4
|
type dhcpInterfacesV4 []*dhcpInterfaceV4
|
||||||
|
|
||||||
// find returns the first network interface within ifaces containing ip. It
|
// find returns the first network interface within ifaces containing ip. It
|
||||||
// returns false if there is no such interface.
|
// returns false if there is no such interface.
|
||||||
func (ifaces netInterfacesV4) find(ip netip.Addr) (iface4 *netInterface, ok bool) {
|
func (ifaces dhcpInterfacesV4) find(ip netip.Addr) (iface4 *netInterface, ok bool) {
|
||||||
i := slices.IndexFunc(ifaces, func(iface *netInterfaceV4) (contains bool) {
|
i := slices.IndexFunc(ifaces, func(iface *dhcpInterfaceV4) (contains bool) {
|
||||||
return iface.subnet.Contains(ip)
|
return iface.subnet.Contains(ip)
|
||||||
})
|
})
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ifaces[i].netInterface, true
|
return ifaces[i].common, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// options returns the implicit and explicit options for the interface. The two
|
// options returns the implicit and explicit options for the interface. The two
|
||||||
|
|
|
@ -62,10 +62,12 @@ func (c *IPv6Config) validate() (err error) {
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// netInterfaceV6 is a DHCP interface for IPv6 address family.
|
// dhcpInterfaceV6 is a DHCP interface for IPv6 address family.
|
||||||
//
|
type dhcpInterfaceV6 struct {
|
||||||
// TODO(e.burkov): Add options.
|
// common is the common part of any network interface within the DHCP
|
||||||
type netInterfaceV6 struct {
|
// server.
|
||||||
|
common *netInterface
|
||||||
|
|
||||||
// rangeStart is the first IP address in the range.
|
// rangeStart is the first IP address in the range.
|
||||||
rangeStart netip.Addr
|
rangeStart netip.Addr
|
||||||
|
|
||||||
|
@ -78,10 +80,6 @@ type netInterfaceV6 struct {
|
||||||
// intersections with implicitOpts.
|
// intersections with implicitOpts.
|
||||||
explicitOpts layers.DHCPv6Options
|
explicitOpts layers.DHCPv6Options
|
||||||
|
|
||||||
// netInterface is embedded here to provide some common network interface
|
|
||||||
// logic.
|
|
||||||
netInterface
|
|
||||||
|
|
||||||
// raSLAACOnly defines if DHCP should send ICMPv6.RA packets without MO
|
// raSLAACOnly defines if DHCP should send ICMPv6.RA packets without MO
|
||||||
// flags.
|
// flags.
|
||||||
raSLAACOnly bool
|
raSLAACOnly bool
|
||||||
|
@ -90,16 +88,16 @@ type netInterfaceV6 struct {
|
||||||
raAllowSLAAC bool
|
raAllowSLAAC bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNetInterfaceV6 creates a new DHCP interface for IPv6 address family with
|
// newDHCPInterfaceV6 creates a new DHCP interface for IPv6 address family with
|
||||||
// the given configuration.
|
// the given configuration.
|
||||||
//
|
//
|
||||||
// TODO(e.burkov): Validate properly.
|
// TODO(e.burkov): Validate properly.
|
||||||
func newNetInterfaceV6(
|
func newDHCPInterfaceV6(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
l *slog.Logger,
|
l *slog.Logger,
|
||||||
name string,
|
name string,
|
||||||
conf *IPv6Config,
|
conf *IPv6Config,
|
||||||
) (i *netInterfaceV6) {
|
) (i *dhcpInterfaceV6) {
|
||||||
l = l.With(keyInterface, name, keyFamily, netutil.AddrFamilyIPv6)
|
l = l.With(keyInterface, name, keyFamily, netutil.AddrFamilyIPv6)
|
||||||
if !conf.Enabled {
|
if !conf.Enabled {
|
||||||
l.DebugContext(ctx, "disabled")
|
l.DebugContext(ctx, "disabled")
|
||||||
|
@ -107,13 +105,9 @@ func newNetInterfaceV6(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
i = &netInterfaceV6{
|
i = &dhcpInterfaceV6{
|
||||||
rangeStart: conf.RangeStart,
|
rangeStart: conf.RangeStart,
|
||||||
netInterface: netInterface{
|
common: newNetInterface(name, l, conf.LeaseDuration),
|
||||||
name: name,
|
|
||||||
leaseTTL: conf.LeaseDuration,
|
|
||||||
logger: l,
|
|
||||||
},
|
|
||||||
raSLAACOnly: conf.RASLAACOnly,
|
raSLAACOnly: conf.RASLAACOnly,
|
||||||
raAllowSLAAC: conf.RAAllowSLAAC,
|
raAllowSLAAC: conf.RAAllowSLAAC,
|
||||||
}
|
}
|
||||||
|
@ -122,12 +116,12 @@ func newNetInterfaceV6(
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
// netInterfacesV4 is a slice of network interfaces of IPv4 address family.
|
// dhcpInterfacesV6 is a slice of network interfaces of IPv6 address family.
|
||||||
type netInterfacesV6 []*netInterfaceV6
|
type dhcpInterfacesV6 []*dhcpInterfaceV6
|
||||||
|
|
||||||
// find returns the first network interface within ifaces containing ip. It
|
// find returns the first network interface within ifaces containing ip. It
|
||||||
// returns false if there is no such interface.
|
// returns false if there is no such interface.
|
||||||
func (ifaces netInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool) {
|
func (ifaces dhcpInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool) {
|
||||||
// prefLen is the length of prefix to match ip against.
|
// prefLen is the length of prefix to match ip against.
|
||||||
//
|
//
|
||||||
// TODO(e.burkov): DHCPv6 inherits the weird behavior of legacy
|
// TODO(e.burkov): DHCPv6 inherits the weird behavior of legacy
|
||||||
|
@ -136,7 +130,7 @@ func (ifaces netInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool
|
||||||
// be used instead.
|
// be used instead.
|
||||||
const prefLen = netutil.IPv6BitLen - 8
|
const prefLen = netutil.IPv6BitLen - 8
|
||||||
|
|
||||||
i := slices.IndexFunc(ifaces, func(iface *netInterfaceV6) (contains bool) {
|
i := slices.IndexFunc(ifaces, func(iface *dhcpInterfaceV6) (contains bool) {
|
||||||
return !ip.Less(iface.rangeStart) &&
|
return !ip.Less(iface.rangeStart) &&
|
||||||
netip.PrefixFrom(iface.rangeStart, prefLen).Contains(ip)
|
netip.PrefixFrom(iface.rangeStart, prefLen).Contains(ip)
|
||||||
})
|
})
|
||||||
|
@ -144,7 +138,7 @@ func (ifaces netInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ifaces[i].netInterface, true
|
return ifaces[i].common, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// options returns the implicit and explicit options for the interface. The two
|
// options returns the implicit and explicit options for the interface. The two
|
||||||
|
|
Loading…
Reference in New Issue