From 9c79c7470c26bd76b7df0ed486be6241fa043363 Mon Sep 17 00:00:00 2001 From: Travis Raines <571832+rainest@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:06:39 -0800 Subject: [PATCH 1/3] fix: correct format directive Use %s instead of %w for non-fmt.Errorf() calls. --- bootloop/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootloop/main.go b/bootloop/main.go index 1ecdfa7..bce39b5 100644 --- a/bootloop/main.go +++ b/bootloop/main.go @@ -196,7 +196,7 @@ func (p *PluginState) Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) dhcpv4.WithServerIP(resp.ServerIPAddr), ) if err != nil { - log.Errorf("failed to create new %s message: %w", dhcpv4.MessageTypeNak, err) + log.Errorf("failed to create new %s message: %s", dhcpv4.MessageTypeNak, err) return resp, true } err = p.deleteIPAddress(req.ClientHWAddr) @@ -205,7 +205,7 @@ func (p *PluginState) Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) } delete(p.Recordsv4, req.ClientHWAddr.String()) if err := p.allocator.Free(net.IPNet{IP: record.IP}); err != nil { - log.Warnf("unable to delete IP %s: %w", record.IP.String(), err) + log.Warnf("unable to delete IP %s: %s", record.IP.String(), err) } log.Printf("MAC %s already exists with IP %s, sending %s to reinitiate DHCP handshake", req.ClientHWAddr.String(), record.IP, dhcpv4.MessageTypeNak) } From 271a30aba6b851d687541348c5431f72a26350a1 Mon Sep 17 00:00:00 2001 From: Travis Raines <571832+rainest@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:26:39 -0800 Subject: [PATCH 2/3] feat: add single-port mode for TFTP Upgrade the pin/tftp package to the v3 release. This provides single port mode. Refactor the TFTP server plugin wrapper to use a struct with settings for port, address, etc. Convert several constants embedded in code to actual constants. Add a single port mode argument to TFTP server. --- coresmd/main.go | 27 +++++++++++++++++++++++---- coresmd/tftp.go | 23 ++++++++++++++++++----- go.mod | 8 ++++++-- go.sum | 25 +++++++++++++++++++++++-- 4 files changed, 70 insertions(+), 13 deletions(-) diff --git a/coresmd/main.go b/coresmd/main.go index 13c8fb0..f3baa98 100644 --- a/coresmd/main.go +++ b/coresmd/main.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/url" + "strconv" "strings" "time" @@ -38,6 +39,12 @@ var ( baseURL *url.URL bootScriptBaseURL *url.URL leaseDuration time.Duration + singlePort bool +) + +const ( + defaultTFTPDirectory = "/tftpboot" + defaultTFTPPort = 69 ) func setup6(args ...string) (handler.Handler6, error) { @@ -48,8 +55,8 @@ func setup4(args ...string) (handler.Handler4, error) { log.Infof("initializing coresmd/coresmd %s (%s), built %s", version.Version, version.GitCommit, version.BuildTime) // Ensure all required args were passed - if len(args) != 5 { - return nil, errors.New("expected 5 arguments: base URL, boot script base URL, CA certificate path, cache duration, lease duration") + if len(args) != 6 { + return nil, errors.New("expected 6 arguments: base URL, boot script base URL, CA certificate path, cache duration, lease duration, single port mode") } // Create new SmdClient using first argument (base URL) @@ -96,11 +103,23 @@ func setup4(args ...string) (handler.Handler4, error) { return nil, fmt.Errorf("failed to parse lease duration: %w", err) } + log.Debug("determining port mode") + singlePort, err = strconv.ParseBool(args[5]) + if err != nil { + return nil, fmt.Errorf("invalid single port toggle '%s', use 'true' or 'false'", args[5]) + } + cache.RefreshLoop() // Start tftpserver - log.Info("starting TFTP server on port 69 with directory /tftpboot") - go startTFTPServer("/tftpboot") + log.Infof("starting TFTP server on port %d with directory %s", defaultTFTPPort, defaultTFTPDirectory) + server := &tftpServer{ + directory: defaultTFTPDirectory, + port: defaultTFTPPort, + singlePort: singlePort, + } + + go server.Start() log.Infof("coresmd plugin initialized with base URL %s and validity duration %s", smdClient.BaseURL, cache.Duration.String()) diff --git a/coresmd/tftp.go b/coresmd/tftp.go index 3a3ae03..bad5856 100644 --- a/coresmd/tftp.go +++ b/coresmd/tftp.go @@ -1,11 +1,12 @@ package coresmd import ( + "fmt" "io" "os" "path/filepath" - "github.com/pin/tftp" + "github.com/pin/tftp/v3" ) const defaultScriptName = "default" @@ -21,9 +22,21 @@ func (sr ScriptReader) Read(b []byte) (int, error) { return nBytes, io.EOF } -func startTFTPServer(directory string) { - s := tftp.NewServer(readHandler(directory), nil) - err := s.ListenAndServe(":69") // default TFTP port +// tftpServer configures and starts a TFTP server. +type tftpServer struct { + address string + directory string + port int + singlePort bool +} + +// Start creates, configures, and starts the TFTP server implementation. +func (t *tftpServer) Start() { + s := tftp.NewServer(readHandler(t.directory), nil) + if t.singlePort { + s.EnableSinglePort() + } + err := s.ListenAndServe(fmt.Sprintf("%s:%d", t.address, t.port)) if err != nil { log.Fatalf("failed to start TFTP server: %v", err) } @@ -42,7 +55,7 @@ func readHandler(directory string) func(string, io.ReaderFrom) error { raddr = raptr.IP.String() } if filename == defaultScriptName { - log.Infof("tftp: %s requested default script") + log.Infof("tftp: %s requested default script", raddr) var sr ScriptReader nbytes, err := rf.ReadFrom(sr) log.Infof("tftp: sent %d bytes of default script to %s", nbytes, raddr) diff --git a/go.mod b/go.mod index 129eca8..2cadb26 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,15 @@ require ( github.com/coredhcp/coredhcp v0.0.0-20240908184240-576af8676ffa github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 github.com/mattn/go-sqlite3 v1.14.22 + github.com/pin/tftp/v3 v3.1.0 github.com/sirupsen/logrus v1.9.3 ) -require github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c // indirect +require ( + github.com/google/gopacket v1.1.19 // indirect + github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c + golang.org/x/net v0.27.0 // indirect +) require ( github.com/bits-and-blooms/bitset v1.14.2 // indirect @@ -25,7 +30,6 @@ require ( github.com/nxadm/tail v1.4.11 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect - github.com/pin/tftp v2.1.0+incompatible github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect diff --git a/go.sum b/go.sum index 27a37fe..23c8361 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas= @@ -35,6 +37,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY= +github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -50,8 +56,8 @@ github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pin/tftp v2.1.0+incompatible h1:Yng4J7jv6lOc6IF4XoB5mnd3P7ZrF60XQq+my3FAMus= -github.com/pin/tftp v2.1.0+incompatible/go.mod h1:xVpZOMCXTy+A5QMjEVN0Glwa1sUvaJhFXbr/aAxuxGY= +github.com/pin/tftp/v3 v3.1.0 h1:rQaxd4pGwcAJnpId8zC+O2NX3B2/NscjDZQaqEjuE7c= +github.com/pin/tftp/v3 v3.1.0/go.mod h1:xwQaN4viYL019tM4i8iecm++5cGxSqen6AJEOEyEI0w= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -94,12 +100,24 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJ github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -109,8 +127,11 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 11b6267de596392914960963dd61e09d94e6a841 Mon Sep 17 00:00:00 2001 From: Travis Raines <571832+rainest@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:02:38 -0800 Subject: [PATCH 3/3] chore: update example configuration for single port --- resources/config.example.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/config.example.yaml b/resources/config.example.yaml index 6d3d456..ba76a81 100644 --- a/resources/config.example.yaml +++ b/resources/config.example.yaml @@ -46,7 +46,10 @@ server4: # 4. Cache validity duration. Coresmd uses a pull-through cache to store # network information and this is the duration to refresh that cache. # 5. Lease duration. - - coresmd: https://foobar.openchami.cluster http://172.16.0.253:8081 /root_ca/root_ca.crt 30s 1h + # 6. Whether to use single port mode. In this mode, the TFTP server will + # send responses to the client's source port. This mode allows the + # server to operate in a NATed environment. + - coresmd: https://foobar.openchami.cluster http://172.16.0.253:8081 /root_ca/root_ca.crt 30s 1h false # Any requests reaching this point are unknown to SMD and it is up to the # administrator to decide how to handle unknown packets.