Skip to content
This repository was archived by the owner on Jan 18, 2024. It is now read-only.

Commit 762c0e4

Browse files
WIP: Update to use ipv4.PacketConn:
ipv4.PacketConn allows us to have the interface from which the packet was received. This is helpful as we must listen to all interfaces in order to get broadcast traffic. Signed-off-by: Jacob Weinstock <[email protected]>
1 parent f36db36 commit 762c0e4

File tree

18 files changed

+438
-186
lines changed

18 files changed

+438
-186
lines changed

.golangci.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ linters:
102102
- asciicheck
103103
- bodyclose
104104
- cyclop
105-
- deadcode
106105
- dogsled
107106
- dupl
108107
- durationcheck
@@ -124,7 +123,6 @@ linters:
124123
- goprintffuncname
125124
- gosimple
126125
- govet
127-
- ifshort
128126
- importas
129127
- ineffassign
130128
- makezero
@@ -141,15 +139,13 @@ linters:
141139
- rowserrcheck
142140
- sqlclosecheck
143141
- staticcheck
144-
- structcheck
145142
- stylecheck
146143
- thelper
147144
- tparallel
148145
- typecheck
149146
- unconvert
150147
- unparam
151148
- unused
152-
- varcheck
153149
- wastedassign
154150
- whitespace
155151

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ LINTERS :=
5151
FIXERS :=
5252

5353
GOLANGCI_LINT_CONFIG := $(LINT_ROOT)/.golangci.yml
54-
GOLANGCI_LINT_VERSION ?= v1.53.3
54+
GOLANGCI_LINT_VERSION ?= v1.54.2
5555
GOLANGCI_LINT_BIN := out/linters/golangci-lint-$(GOLANGCI_LINT_VERSION)-$(LINT_ARCH)
5656
$(GOLANGCI_LINT_BIN):
5757
mkdir -p out/linters
@@ -61,11 +61,11 @@ $(GOLANGCI_LINT_BIN):
6161

6262
LINTERS += golangci-lint-lint
6363
golangci-lint-lint: $(GOLANGCI_LINT_BIN)
64-
find . -name go.mod -execdir sh -c '"$(GOLANGCI_LINT_BIN)" run -c "$(GOLINT_CONFIG)"' '{}' '+'
64+
find . -name go.mod -execdir sh -c '"$(GOLANGCI_LINT_BIN)" run -c "$(GOLANGCI_LINT_CONFIG)"' '{}' '+'
6565

6666
FIXERS += golangci-lint-fix
6767
golangci-lint-fix: $(GOLANGCI_LINT_BIN)
68-
find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLINT_CONFIG)" --fix \;
68+
find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLANGCI_LINT_CONFIG)" --fix \;
6969

7070
.PHONY: _lint $(LINTERS)
7171
_lint: $(LINTERS)

backend/kube/error.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package kube
2+
3+
type hardwareNotFoundError struct{}
4+
5+
func (hardwareNotFoundError) NotFound() bool { return true }
6+
7+
func (hardwareNotFoundError) Error() string { return "hardware not found" }

backend/kube/kube.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (b *Backend) GetByMac(ctx context.Context, mac net.HardwareAddr) (*data.DHC
8282
}
8383

8484
if len(hardwareList.Items) == 0 {
85-
err := errors.New("no hardware found")
85+
err := hardwareNotFoundError{}
8686
span.SetStatus(codes.Error, err.Error())
8787

8888
return nil, nil, err
@@ -139,7 +139,7 @@ func (b *Backend) GetByIP(ctx context.Context, ip net.IP) (*data.DHCP, *data.Net
139139
}
140140

141141
if len(hardwareList.Items) == 0 {
142-
err := errors.New("no hardware found")
142+
err := hardwareNotFoundError{}
143143
span.SetStatus(codes.Error, err.Error())
144144

145145
return nil, nil, err

data/data.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,28 @@ import (
77
"net/url"
88
"strings"
99

10+
"github.com/insomniacslk/dhcp/dhcpv4"
1011
"go.opentelemetry.io/otel/attribute"
1112
)
1213

14+
// Packet holds the data that is passed to a DHCP handler.
15+
type Packet struct {
16+
// Peer is the address of the client that sent the DHCP message.
17+
Peer net.Addr
18+
// Pkt is the DHCP message.
19+
Pkt *dhcpv4.DHCPv4
20+
// Md is the metadata that was passed to the DHCP server.
21+
Md *Metadata
22+
}
23+
24+
// Metadata holds metadata about the DHCP packet that was received.
25+
type Metadata struct {
26+
// IfName is the name of the interface that the DHCP message was received on.
27+
IfName string
28+
// IfIndex is the index of the interface that the DHCP message was received on.
29+
IfIndex int
30+
}
31+
1332
// DHCP holds the DHCP headers and options to be set in a DHCP handler response.
1433
// This is the API between a DHCP handler and a backend.
1534
type DHCP struct {

dhcp.go

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@
22
package dhcp
33

44
import (
5-
"context"
6-
"errors"
75
"fmt"
86
"net"
97
"net/netip"
108

11-
"dario.cat/mergo"
12-
"github.com/insomniacslk/dhcp/dhcpv4"
139
"github.com/insomniacslk/dhcp/dhcpv4/server4"
14-
"github.com/tinkerbell/dhcp/handler/noop"
1510
)
1611

12+
/*
1713
// ErrNoConn is an error im still not sure i want to use.
1814
var ErrNoConn = &noConnError{}
1915
@@ -23,25 +19,28 @@ func (e *noConnError) Error() string {
2319
return "no connection specified"
2420
}
2521
22+
23+
2624
// Listener is a DHCPv4 server.
2725
type Listener struct {
2826
Addr netip.AddrPort
29-
srv *server4.Server
27+
Log logr.Logger
28+
srv *Server
3029
handlers []Handler
3130
}
3231
3332
// Handler is the interface is responsible for responding to DHCP messages.
34-
type Handler interface {
35-
// Handle is used for how to respond to DHCP messages.
36-
Handle(net.PacketConn, net.Addr, *dhcpv4.DHCPv4)
37-
}
33+
//type Handler interface {
34+
// Handle is used for how to respond to DHCP messages.
35+
// Handle(net.PacketConn, net.Addr, *dhcpv4.DHCPv4)
36+
//}
3837
3938
// Handler is the main handler passed to the server4 function.
4039
// Internally it allows for multiple handlers to be defined.
4140
// Each handler in l.handlers then executed for every received packet.
42-
func (l *Listener) Handler(conn net.PacketConn, peer net.Addr, pkt *dhcpv4.DHCPv4) {
43-
for _, h := range l.handlers {
44-
h.Handle(conn, peer, pkt)
41+
func (l *Listener) Handler(ctx context.Context, conn net.PacketConn, data data.Packet) {
42+
for _, handle := range l.handlers {
43+
handle(ctx, conn, data)
4544
}
4645
}
4746
@@ -56,19 +55,20 @@ func Serve(ctx context.Context, c net.PacketConn, h ...Handler) error {
5655
// If no handler is specified, a Noop handler will be used.
5756
func (l *Listener) Serve(ctx context.Context, c net.PacketConn) error {
5857
if len(l.handlers) == 0 {
59-
l.handlers = append(l.handlers, &noop.Handler{})
58+
nop := &noop.Handler{}
59+
l.handlers = append(l.handlers, nop.Handle)
6060
}
6161
if c == nil {
6262
return ErrNoConn
6363
}
64-
dhcp, err := server4.NewServer("", nil, l.Handler, server4.WithConn(c))
64+
dhcp, err := NewServer("", nil, l.handlers, WithConn(c), WithLogger(l.Log))
6565
if err != nil {
6666
return fmt.Errorf("failed to create dhcpv4 server: %w", err)
6767
}
6868
6969
errCh := make(chan error, 1)
7070
go func() {
71-
err = dhcp.Serve()
71+
err = dhcp.Serve(ctx)
7272
if err != nil {
7373
errCh <- err
7474
}
@@ -85,7 +85,8 @@ func (l *Listener) Serve(ctx context.Context, c net.PacketConn) error {
8585
// ListenAndServe will listen for DHCP messages and call the given handler for each.
8686
func (l *Listener) ListenAndServe(ctx context.Context, h ...Handler) error {
8787
if len(h) == 0 {
88-
l.handlers = append(l.handlers, &noop.Handler{})
88+
nop := &noop.Handler{}
89+
l.handlers = append(l.handlers, nop.Handle)
8990
}
9091
l.handlers = h
9192
defaults := &Listener{
@@ -98,6 +99,8 @@ func (l *Listener) ListenAndServe(ctx context.Context, h ...Handler) error {
9899
addr := &net.UDPAddr{
99100
IP: l.Addr.Addr().AsSlice(),
100101
Port: int(l.Addr.Port()),
102+
//IP: defaults.Addr.Addr().AsSlice(),
103+
//Port: int(defaults.Addr.Port()),
101104
}
102105
conn, err := server4.NewIPv4UDPConn("", addr)
103106
if err != nil {
@@ -115,3 +118,14 @@ func (l *Listener) Shutdown() error {
115118
116119
return l.srv.Close()
117120
}
121+
*/
122+
123+
// NewConn creates a new UDP connection.
124+
func NewConn(addr netip.AddrPort) (net.PacketConn, error) {
125+
conn, err := server4.NewIPv4UDPConn("", &net.UDPAddr{IP: addr.Addr().AsSlice(), Port: int(addr.Port())})
126+
if err != nil {
127+
return nil, fmt.Errorf("failed to create udp connection: %w", err)
128+
}
129+
130+
return conn, nil
131+
}

dhcp_test.go

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
11
package dhcp
22

3-
import (
4-
"context"
5-
"errors"
6-
"net"
7-
"net/netip"
8-
"testing"
9-
"time"
10-
11-
"github.com/go-logr/logr"
12-
"github.com/google/go-cmp/cmp"
13-
"github.com/insomniacslk/dhcp/dhcpv4"
14-
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
15-
"github.com/tinkerbell/dhcp/handler/noop"
16-
"golang.org/x/net/nettest"
17-
)
18-
3+
/*
194
type mock struct {
205
Log logr.Logger
216
ServerIP net.IP
@@ -26,13 +11,13 @@ type mock struct {
2611
Router net.IP
2712
}
2813
29-
func (m *mock) Handle(conn net.PacketConn, peer net.Addr, pkt *dhcpv4.DHCPv4) {
14+
func (m *mock) Handle(ctx context.Context, conn net.PacketConn, d data.Packet) {
3015
if m.Log.GetSink() == nil {
3116
m.Log = logr.Discard()
3217
}
3318
3419
mods := m.setOpts()
35-
switch mt := pkt.MessageType(); mt {
20+
switch mt := d.Pkt.MessageType(); mt {
3621
case dhcpv4.MessageTypeDiscover:
3722
mods = append(mods, dhcpv4.WithMessageType(dhcpv4.MessageTypeOffer))
3823
case dhcpv4.MessageTypeRequest:
@@ -43,12 +28,12 @@ func (m *mock) Handle(conn net.PacketConn, peer net.Addr, pkt *dhcpv4.DHCPv4) {
4328
m.Log.Info("unsupported message type", "type", mt.String())
4429
return
4530
}
46-
reply, err := dhcpv4.NewReplyFromRequest(pkt, mods...)
31+
reply, err := dhcpv4.NewReplyFromRequest(d.Pkt, mods...)
4732
if err != nil {
4833
m.Log.Error(err, "error creating reply")
4934
return
5035
}
51-
if _, err := conn.WriteTo(reply.ToBytes(), peer); err != nil {
36+
if _, err := conn.WriteTo(reply.ToBytes(), d.Peer); err != nil {
5237
m.Log.Error(err, "failed to send reply")
5338
return
5439
}
@@ -69,6 +54,8 @@ func (m *mock) setOpts() []dhcpv4.Modifier {
6954
return mods
7055
}
7156
57+
58+
7259
func dhcp(ctx context.Context) (*dhcpv4.DHCPv4, error) {
7360
rifs, err := nettest.RoutedInterface("ip", net.FlagUp|net.FlagBroadcast)
7461
if err != nil {
@@ -86,24 +73,22 @@ func dhcp(ctx context.Context) (*dhcpv4.DHCPv4, error) {
8673
return c.DiscoverOffer(ctx)
8774
}
8875
76+
8977
func TestListenAndServe(t *testing.T) {
9078
// test if the server is listening on the correct address and port
9179
tests := map[string]struct {
9280
h Handler
9381
addr netip.AddrPort
9482
wantListener *Listener
9583
}{
96-
"success": {addr: netip.MustParseAddrPort("127.0.0.1:7676"), h: &mock{}},
84+
"success": {addr: netip.MustParseAddrPort("127.0.0.1:7676"), h: func() Handler { m := &mock{}; return m.Handle }()},
9785
}
9886
for name, tt := range tests {
9987
t.Run(name, func(t *testing.T) {
10088
s := &Listener{Addr: tt.addr}
10189
t.Logf("before: %+v", s)
10290
ctx, done := context.WithCancel(context.Background())
10391
defer done()
104-
go func() {
105-
<-ctx.Done()
106-
}()
10792
10893
go s.ListenAndServe(ctx, tt.h)
10994
@@ -125,10 +110,10 @@ func TestListenerAndServe(t *testing.T) {
125110
addr netip.AddrPort
126111
err error
127112
}{
128-
"noop handler": {h: &noop.Handler{}, addr: netip.MustParseAddrPort("0.0.0.0:7678")},
113+
"noop handler": {h: func() Handler { m := &noop.Handler{}; return m.Handle }(), addr: netip.MustParseAddrPort("0.0.0.0:7678")},
129114
"no handler": {addr: netip.MustParseAddrPort("0.0.0.0:7678")},
130-
"mock handler": {h: &mock{}, addr: netip.MustParseAddrPort("0.0.0.0:7678")},
131-
"success use default addr": {h: &mock{}},
115+
"mock handler": {h: func() Handler { m := &mock{}; return m.Handle }(), addr: netip.MustParseAddrPort("0.0.0.0:7678")},
116+
"success use default addr": {h: func() Handler { m := &mock{}; return m.Handle }()},
132117
}
133118
for name, tt := range tests {
134119
t.Run(name, func(t *testing.T) {
@@ -139,21 +124,30 @@ func TestListenerAndServe(t *testing.T) {
139124
defer done()
140125
141126
err := s.ListenAndServe(ctx, tt.h)
142-
if err != tt.err && err.Error() != "failed to create udp connection: cannot bind to port 67: permission denied" && !errors.Is(err, ErrNoConn) { //nolint:errorlint // nil pointer dereference without this.
127+
if err != tt.err && err.Error() != "failed to create udp connection: cannot bind to port 67: permission denied" && !errors.Is(err, ErrNoConn) && !closeConnErr(err) { //nolint:errorlint // nil pointer dereference without this.
143128
t.Log(err)
144129
t.Fatalf("got: %T, wanted: %T or ErrNoConn", err, &net.OpError{})
145130
}
146131
})
147132
}
148133
}
149134
135+
func closeConnErr(err error) bool {
136+
l, ok := err.(*net.OpError)
137+
if !ok {
138+
return false
139+
}
140+
141+
return l.Op == "close"
142+
}
143+
150144
func TestServe(t *testing.T) {
151145
tests := map[string]struct {
152146
h Handler
153147
addr netip.AddrPort
154148
err error
155149
}{
156-
"noop handler": {addr: netip.MustParseAddrPort("0.0.0.0:7676"), h: &noop.Handler{}},
150+
"noop handler": {addr: netip.MustParseAddrPort("0.0.0.0:7676"), h: func() Handler { m := &noop.Handler{}; return m.Handle }()},
157151
"no handler": {addr: netip.MustParseAddrPort("0.0.0.0:7678")},
158152
}
159153
for name, tt := range tests {
@@ -183,3 +177,4 @@ func TestNoConnError(t *testing.T) {
183177
t.Fatal(diff)
184178
}
185179
}
180+
*/

example/fileBackend/main.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,17 @@ func main() {
4747
OTELEnabled: true,
4848
Backend: backend,
4949
}
50-
listener := &dhcp.Listener{}
50+
conn, err := dhcp.NewConn(netip.MustParseAddrPort("0.0.0.0:67"))
51+
if err != nil {
52+
panic(err)
53+
}
54+
55+
defer func() {
56+
_ = conn.Close()
57+
}()
58+
server := &dhcp.Server{Logger: l, Conn: conn, Handlers: []dhcp.Handler{h}}
5159
l.Info("starting server", "addr", h.IPAddr)
52-
l.Error(listener.ListenAndServe(ctx, h), "done")
60+
l.Error(server.Serve(ctx), "done")
5361
l.Info("done")
5462
}
5563

0 commit comments

Comments
 (0)