Skip to content

Commit

Permalink
Merge pull request #39 from hugelgupf/remove-async
Browse files Browse the repository at this point in the history
dhcpv4: switch to nclient4
  • Loading branch information
jparise authored Nov 26, 2023
2 parents e967c42 + 92e3771 commit fed87b3
Showing 3 changed files with 18 additions and 81 deletions.
17 changes: 3 additions & 14 deletions dhcpv4/TUTORIAL.md
Original file line number Diff line number Diff line change
@@ -174,7 +174,7 @@ import (
"time"

"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/async"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/insomniacslk/dhcp/iana"
"github.com/pinterest/bender"
bdhcpv4 "github.com/pinterest/bender/dhcpv4"
@@ -236,27 +236,16 @@ func main() {
if err != nil {
log.Fatalln("Error getting address:", err)
}
client := async.NewClient()
client.LocalAddr = &net.UDPAddr{IP: ip, Port: async.DefaultServerPort, Zone: ""}
client.RemoteAddr = addr

err = client.Open(100)
client, err := nclient4.New("eth0", nclient4.WithServerAddr(addr))
if err != nil {
log.Fatalln("Error opening client:", err)
}
defer client.Close()

// Subscribe to client errors channel
go func() {
for err := range client.Errors() {
log.Println("Client error:", err)
}
}()

// Load test
intervals := bender.ExponentialIntervalGenerator(10)
requests := SyntheticDHCPv4Requests(100)
exec, err := bdhcpv4.CreateExecutor(client, validator)
exec, err := bdhcpv4.CreateExecutor(client, ip, validator)
if err != nil {
log.Fatalln("Error creating executor:", err)
}
66 changes: 13 additions & 53 deletions dhcpv4/dhcpv4.go
Original file line number Diff line number Diff line change
@@ -1,83 +1,43 @@
package dhcpv4

import (
"errors"
"context"
"fmt"
"net"
"time"

"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/async"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/pinterest/bender"
)

// ResponseValidator validates a DHCPv4 response.
type ResponseValidator func(request, response *dhcpv4.DHCPv4) error

// CreateExecutor creates a new DHCPv4 RequestExecutor.
func CreateExecutor(client *async.Client, validator ResponseValidator) (bender.RequestExecutor, error) {
send, err := newSendFunc(client)
if err != nil {
return nil, err
}
//
// relayIP is the IP used as the gateway IP.
func CreateExecutor(client *nclient4.Client, relayIP net.IP, validator ResponseValidator) (bender.RequestExecutor, error) {
return func(_ int64, request interface{}) (interface{}, error) {
dis, ok := request.(*dhcpv4.DHCPv4)
if !ok {
return nil, fmt.Errorf("invalid request type %T, want: *dhcpv4.DHCPv4", request)
}
off, err := send(dis, dhcpv4.MessageTypeOffer)
ctx := context.Background()
relayMod := dhcpv4.WithRelay(relayIP)
relayMod(dis)
off, err := client.SendAndRead(ctx, client.RemoteAddr(), dis, nclient4.IsMessageType(dhcpv4.MessageTypeOffer))
if err != nil {
return nil, err
return nil, fmt.Errorf("error receiving DHCP offer: %w", err)
}
req, err := dhcpv4.NewRequestFromOffer(off)
req, err := dhcpv4.NewRequestFromOffer(off, relayMod)
if err != nil {
return nil, err
}
ack, err := send(req, dhcpv4.MessageTypeAck)
ack, err := client.SendAndRead(ctx, client.RemoteAddr(), req, nclient4.IsMessageType(dhcpv4.MessageTypeAck))
if err != nil {
return nil, err
return nil, fmt.Errorf("error receiving DHCP ACK: %w", err)
}
err = validator(dis, ack)
return ack, err
}, nil
}

// sendFunc represents a function used to send dhcpv4 datagrams
type sendFunc = func(*dhcpv4.DHCPv4, dhcpv4.MessageType) (*dhcpv4.DHCPv4, error)

// newSendFunc creates a function which will send messages using the given client
func newSendFunc(client *async.Client) (sendFunc, error) {
addr, ok := client.LocalAddr.(*net.UDPAddr)
if !ok {
return nil, fmt.Errorf("invalid local address %T, want *net.UDPAddr", client.LocalAddr)
}
m := dhcpv4.WithRelay(addr.IP)
t := uint(client.ReadTimeout / time.Millisecond)

// send a message and check that the response is of a given type
return func(d *dhcpv4.DHCPv4, mt dhcpv4.MessageType) (*dhcpv4.DHCPv4, error) {
response, err, timeout := client.Send(d, m).GetOrTimeout(t)
if timeout {
return nil, errors.New("timeout")
} else if err != nil {
return nil, err
}
res, ok := response.(*dhcpv4.DHCPv4)
if !ok {
return nil, fmt.Errorf("invalid response type %T, want: *dhcpv4.DHCPv4", response)
}
if err := assertMessageType(res, mt); err != nil {
return nil, err
}
return res, nil
}, nil
}

// assertMessageType extracts message type and checks if it matches expected
func assertMessageType(d *dhcpv4.DHCPv4, mt dhcpv4.MessageType) error {
t := d.MessageType()
if t != mt {
return fmt.Errorf("invalid message type %s, want: %s", t.String(), mt.String())
}
return nil
}
16 changes: 2 additions & 14 deletions dhcpv4/dhcpv4_test.go
Original file line number Diff line number Diff line change
@@ -5,27 +5,15 @@ import (
"testing"

"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/async"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
)

func validator(_, _ *dhcpv4.DHCPv4) error {
return nil
}

func TestCreateExecutorAddressCheck(t *testing.T) {
client := async.NewClient()
_, err := CreateExecutor(client, validator)
if err == nil || err.Error() != "invalid local address <nil>, want *net.UDPAddr" {
t.Errorf("Expected CreateExecutor to fail with invalid address error, got (%s)", err)
}
}

func TestExecutorTypeCheck(t *testing.T) {
addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:54321")
if err != nil {
t.Errorf("Expected no error when resolving udp address, got (%v)", err)
}
executor, err := CreateExecutor(&async.Client{LocalAddr: addr}, validator)
executor, err := CreateExecutor(&nclient4.Client{}, net.IP{1, 1, 1, 1}, validator)
if err != nil {
t.Errorf("Expected no error when creating executor, got (%v)", err)
}

0 comments on commit fed87b3

Please sign in to comment.