all: imp code, resolve todos

This commit is contained in:
Eugene Burkov 2025-10-15 21:52:15 +03:00
parent e032b722a6
commit e9fc8535ed
4 changed files with 85 additions and 19 deletions

View File

@ -34,7 +34,7 @@ func macToKey(mac net.HardwareAddr) (key macKey) {
type netInterface struct {
// logger logs the events related to the network interface.
//
// TODO(e.burkov): Consider removing it and using the valud from context.
// TODO(e.burkov): Consider removing it and using the value from context.
logger *slog.Logger
// leases is the set of DHCP leases assigned to this interface.

View File

@ -13,15 +13,12 @@ import (
"github.com/google/gopacket/layers"
)
// options returns the implicit and explicit options for the interface. The two
// lists are disjoint and the implicit options are initialized with default
// values.
//
// TODO(e.burkov): DRY with the IPv6 version.
func (c *IPv4Config) options(ctx context.Context, l *slog.Logger) (imp, exp layers.DHCPOptions) {
// implicitOptions returns the implicit options for the interface, sorted by
// code.
func (c *IPv4Config) implicitOptions() (opts layers.DHCPOptions) {
// Set default values of host configuration parameters listed in Appendix A
// of RFC-2131.
imp = layers.DHCPOptions{
opts = layers.DHCPOptions{
// Values From Configuration
layers.NewDHCPOption(layers.DHCPOptSubnetMask, c.SubnetMask.AsSlice()),
@ -177,7 +174,20 @@ func (c *IPv4Config) options(ctx context.Context, l *slog.Logger) (imp, exp laye
// See https://datatracker.ietf.org/doc/html/rfc1122#section-4.2.3.6.
layers.NewDHCPOption(layers.DHCPOptTCPKeepAliveGarbage, []byte{0x1}),
}
slices.SortFunc(imp, compareV4OptionCodes)
slices.SortFunc(opts, compareV4OptionCodes)
return opts
}
// options returns the implicit and explicit options for the interface. The two
// lists are disjoint and the implicit options are initialized with default
// values. All options within exp which have a nil Data field should be treated
// as instruction to remove those from responses.
//
// TODO(e.burkov): DRY with the IPv6 version.
func (c *IPv4Config) options(ctx context.Context, l *slog.Logger) (imp, exp layers.DHCPOptions) {
// Set values of implicit options.
imp = c.implicitOptions()
// Set values for explicitly configured options.
for _, o := range c.Options {
@ -187,10 +197,10 @@ func (c *IPv4Config) options(ctx context.Context, l *slog.Logger) (imp, exp laye
}
i, found = slices.BinarySearchFunc(exp, o, compareV4OptionCodes)
if o.Length > 0 {
if found {
exp[i].Data, exp[i].Length = o.Data, o.Length
} else {
exp = slices.Insert(exp, i, o)
} else if found {
exp = slices.Delete(exp, i, i+1)
}
}
@ -205,11 +215,44 @@ func compareV4OptionCodes(a, b layers.DHCPOption) (res int) {
}
// updateOptions updates the options of the response in accordance with the
// request and RFC 2131.
// requested parameters. req and resp must not be nil.
//
// See https://datatracker.ietf.org/doc/html/rfc2131#section-4.3.1.
func (iface *dhcpInterfaceV4) updateOptions(req, resp *layers.DHCPv4) {
// TODO(e.burkov): !! Implement
// If the server recognizes the parameter as a parameter defined in the Host
// Requirements Document, the server MUST include the default value for that
// parameter.
optWithCode := layers.DHCPOption{}
for _, code := range requestedOptions(req) {
optWithCode.Type = code
i, has := slices.BinarySearchFunc(iface.implicitOpts, optWithCode, compareV4OptionCodes)
if has {
// The client MAY list the options in order of preference. The DHCP
// server is not required to return the options in the requested
// order, but MUST try to insert the requested options in the order
// requested by the client.
//
// See https://datatracker.ietf.org/doc/html/rfc2132#section-9.8.
resp.Options = append(resp.Options, iface.implicitOpts[i])
}
}
// If the server has been explicitly configured with a default value for the
// parameter or the parameter has a non-default value on the client's
// subnet, the server MUST include that value in an appropriate option.
for _, opt := range iface.explicitOpts {
if opt.Data != nil {
resp.Options = append(resp.Options, opt)
continue
}
// Remove options explicitly configured to be removed, in case they are
// already set.
resp.Options = slices.DeleteFunc(resp.Options, func(o layers.DHCPOption) (ok bool) {
return o.Type == opt.Type
})
}
}
// appendLeaseTime appends the lease time option to the response.
@ -268,3 +311,23 @@ func hostname4(msg *layers.DHCPv4) (hostname string) {
return ""
}
// requestedOptions returns the list of options requested in DHCPv4 message, if
// any.
func requestedOptions(msg *layers.DHCPv4) (opts []layers.DHCPOpt) {
for _, opt := range msg.Options {
l := len(opt.Data)
if opt.Type != layers.DHCPOptParamsRequest || l == 0 {
continue
}
opts = make([]layers.DHCPOpt, 0, l)
for _, code := range opt.Data {
opts = append(opts, layers.DHCPOpt(code))
}
return opts
}
return nil
}

View File

@ -49,7 +49,9 @@ func TestIPv4Config_Options(t *testing.T) {
layers.NewDHCPOption(layers.DHCPOptBroadcastAddr, nil),
},
},
wantExplicit: nil,
wantExplicit: layers.DHCPOptions{
layers.NewDHCPOption(layers.DHCPOptBroadcastAddr, nil),
},
}, {
name: "rewritten_del",
conf: &IPv4Config{

View File

@ -41,9 +41,9 @@ type IPv4Config struct {
RangeEnd netip.Addr
// Options is the list of explicitly configured DHCP options to send to
// clients. The options having a zero value within the Length field are
// treated as deletions of the corresponding options, either implicit or
// explicit.
// clients. Options with nil Data field are removed from responses.
//
// TODO(e.burkov): Validate.
Options layers.DHCPOptions
// LeaseDuration is the TTL of a DHCP lease. It should be positive.
@ -146,7 +146,8 @@ type dhcpInterfaceV4 struct {
implicitOpts layers.DHCPOptions
// explicitOpts are the user-configured options. It must not have
// intersections with implicitOpts.
// intersections with implicitOpts. Options with nil Data field are removed
// from responses.
explicitOpts layers.DHCPOptions
}