Skip to content

add dhcp.ClientState and it's tests #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions eth/dhcp/clientstate_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions eth/dhcp/dhcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,35 @@ const (
DefaultServerPort = 67
)

// ClientState represents the state of a DHCP client.
// Below is a state diagram of a DHCP client.
type ClientState uint8

// IsValid returns true if the state is within the valid range.
func (s ClientState) IsValid() bool {
return s > 0 || s < ClientState(s)
}

// install stringer with `go install golang.org/x/tools/cmd/stringer@latest`
//
//go:generate stringer -type=ClientState -trimprefix=State
const (
_ ClientState = iota
// On clean slate boot, abort, NAK or decline enter the INIT state.
StateInit
// After sending out a Discover enter SELECTING.
StateSelecting
// After receiving a worthy offer enter REQUESTING.
StateRequesting
// On reboot enter INIT-REBOOT state.
// StateInitReboot
// On sending out a Request enter REBOOTING.
// StateRebooting
// On ACK to Request enter BOUND.
StateBound
numStates uint8 = iota
)

type Option struct {
Num OptNum
Data []byte
Expand Down
2 changes: 1 addition & 1 deletion eth/dns/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const (
// An RCode is a DNS response status code.
type RCode uint16

//go:generate stringer -type=RCode -trimprefix=RCode -output=rcode_string.go
// /go:generate stringer -type=RCode -trimprefix=RCode -output=rcode_string.go
const (
RCodeSuccess RCode = 0 // No error condition.
RCodeFormatError RCode = 1 // Format error - The name server was unable to interpret the query.
Expand Down
20 changes: 20 additions & 0 deletions stacks/dhcp_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,30 @@ func (d *DHCPClient) BeginRequest(cfg DHCPRequestConfig) error {
return d.stack.FlagPendingUDP(d.port)
}

// IsDone
//
// Deprecated: Use d.State()==dhcp.StateBound instead.
func (d *DHCPClient) IsDone() bool {
return d.state == dhcpStateDone || d.state == dhcpStateNaked
}

// State returns the current state of the DHCP client.
func (d *DHCPClient) State() dhcp.ClientState {
switch d.state {
case dhcpStateNone:
return dhcp.StateInit
case dhcpStateWaitOffer, dhcpStateGotOffer:
return dhcp.StateSelecting
case dhcpStateWaitAck:
return dhcp.StateRequesting
case dhcpStateDone:
return dhcp.StateBound
case dhcpStateNaked:
return dhcp.StateInit
}
return 0
}

// DHCPServer IP address field of the DHCP packet. Is the siaddr field of the DHCP packet, which can be overriden with the Server IP option.
func (d *DHCPClient) DHCPServer() netip.Addr {
return ipv4orInvalid(d.svip)
Expand Down
13 changes: 13 additions & 0 deletions stacks/stacks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/soypat/seqs"
"github.com/soypat/seqs/eth"
"github.com/soypat/seqs/eth/dhcp"
"github.com/soypat/seqs/eth/dns"
"github.com/soypat/seqs/stacks"
)
Expand Down Expand Up @@ -89,6 +90,13 @@ func TestDHCP(t *testing.T) {
}

func testDHCP(t *testing.T, cl *stacks.DHCPClient, sv *stacks.DHCPServer) {
checkClientState := func(t *testing.T, want dhcp.ClientState) {
t.Helper()
if cl.State() != want {
t.Fatalf("client state=%s want=%s", cl.State().String(), want.String())
}
}
checkClientState(t, dhcp.StateInit)
var requestedIP = netip.AddrFrom4([4]byte{192, 168, 1, 69})
cstack := cl.PortStack()
sstack := sv.PortStack()
Expand All @@ -103,6 +111,7 @@ func testDHCP(t *testing.T, cl *stacks.DHCPClient, sv *stacks.DHCPServer) {
if err != nil {
t.Fatal(err)
}

checkClientNotDone := func(msg string) {
t.Helper()
if cl.IsDone() {
Expand All @@ -116,6 +125,7 @@ func testDHCP(t *testing.T, cl *stacks.DHCPClient, sv *stacks.DHCPServer) {
if n < minDHCPSize {
t.Errorf("ex[%d] sent=%d want>=%d", ex, n, minDHCPSize)
}
checkClientState(t, dhcp.StateSelecting)
checkNoMoreDataSent(t, "after cl DISCOVER send", egr)
checkClientNotDone("after DISCOVER send")
egr.HandleRx(t)
Expand All @@ -125,6 +135,7 @@ func testDHCP(t *testing.T, cl *stacks.DHCPClient, sv *stacks.DHCPServer) {
if n < minDHCPSize {
t.Errorf("ex[%d] sent=%d want>=%d", ex, n, minDHCPSize)
}
checkClientState(t, dhcp.StateSelecting)
checkNoMoreDataSent(t, "after sv OFFER send", egr)
egr.HandleRx(t) // Client receives OFFER.
checkClientNotDone("after OFFER recv")
Expand All @@ -134,6 +145,7 @@ func testDHCP(t *testing.T, cl *stacks.DHCPClient, sv *stacks.DHCPServer) {
if n < minDHCPSize {
t.Errorf("ex[%d] sent=%d want>=%d", ex, n, minDHCPSize)
}
checkClientState(t, dhcp.StateRequesting)
checkNoMoreDataSent(t, "after client REQUEST send", egr)
checkClientNotDone("after REQUEST send")
egr.HandleRx(t) // Server receives REQUEST.
Expand All @@ -148,6 +160,7 @@ func testDHCP(t *testing.T, cl *stacks.DHCPClient, sv *stacks.DHCPServer) {
if !cl.IsDone() {
t.Fatal("client not processed ACK yet")
}
checkClientState(t, dhcp.StateBound)
}

func TestARP(t *testing.T) {
Expand Down
Loading