Skip to content

Commit

Permalink
Add ability to set a higher maximum block size for TFTP (#93)
Browse files Browse the repository at this point in the history
## Description

This PR adds the ability to set a higher maximum block size for TFTP transfers.

## Why is this needed

We are troubleshooting some misbehaving NICs that might benefit from support for larger block sizes. They appear to request 1468 which makes sense on a link with MTU of 1500.

## How Has This Been Tested?

Tested with atftp

## How are existing users impacted? What migration steps/scripts do we need?

The default of 512 is preserved and made explicit in this codebase.
  • Loading branch information
mergify[bot] authored Oct 5, 2023
2 parents c92a076 + 80c98a1 commit 152536c
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 22 deletions.
21 changes: 13 additions & 8 deletions cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
type Command struct {
// TFTPAddr is the TFTP server address:port.
TFTPAddr string `validate:"required,hostname_port"`
// TFTPBlockSize is the maximum block size for serving individual TFTP requests.
TFTPBlockSize int `validate:"required,gte=512"`
// TFTPTimeout is the timeout for serving individual TFTP requests.
TFTPTimeout time.Duration `validate:"required,gte=1s"`
// HTTPAddr is the HTTP server address:port.
Expand Down Expand Up @@ -72,12 +74,13 @@ func Execute(ctx context.Context, args []string) error {
// Run listens and serves the TFTP and HTTP services.
func (c *Command) Run(ctx context.Context) error {
defaults := Command{
TFTPAddr: "0.0.0.0:69",
TFTPTimeout: 5 * time.Second,
HTTPAddr: "0.0.0.0:8080",
HTTPTimeout: 5 * time.Second,
Log: logr.Discard(),
LogLevel: "info",
TFTPAddr: "0.0.0.0:69",
TFTPBlockSize: 512,
TFTPTimeout: 5 * time.Second,
HTTPAddr: "0.0.0.0:8080",
HTTPTimeout: 5 * time.Second,
Log: logr.Discard(),
LogLevel: "info",
}

err := mergo.Merge(c, defaults)
Expand All @@ -94,8 +97,9 @@ func (c *Command) Run(ctx context.Context) error {
}
srv := Server{
TFTP: ServerSpec{
Addr: tAddr,
Timeout: c.TFTPTimeout,
Addr: tAddr,
BlockSize: c.TFTPBlockSize,
Timeout: c.TFTPTimeout,
},
HTTP: ServerSpec{
Addr: hAddr,
Expand All @@ -110,6 +114,7 @@ func (c *Command) Run(ctx context.Context) error {
// RegisterFlags registers a flag set for the ipxe command.
func (c *Command) RegisterFlags(f *flag.FlagSet) {
f.StringVar(&c.TFTPAddr, "tftp-addr", "0.0.0.0:69", "TFTP server address")
f.IntVar(&c.TFTPBlockSize, "tftp-blocksize", 512, "TFTP server maximum block size")
f.DurationVar(&c.TFTPTimeout, "tftp-timeout", time.Second*5, "TFTP server timeout")
f.StringVar(&c.HTTPAddr, "http-addr", "0.0.0.0:8080", "HTTP server address")
f.DurationVar(&c.HTTPTimeout, "http-timeout", time.Second*5, "HTTP server timeout")
Expand Down
25 changes: 14 additions & 11 deletions cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func TestCommand_RegisterFlags(t *testing.T) {
c := &Command{}
fs := flag.NewFlagSet("ipxe", flag.ExitOnError)
fs.StringVar(&c.TFTPAddr, "tftp-addr", "0.0.0.0:69", "TFTP server address")
fs.IntVar(&c.TFTPBlockSize, "tftp-blocksize", 512, "TFTP server maximum block size")
fs.DurationVar(&c.TFTPTimeout, "tftp-timeout", time.Second*5, "TFTP server timeout")
fs.StringVar(&c.HTTPAddr, "http-addr", "0.0.0.0:8080", "HTTP server address")
fs.DurationVar(&c.HTTPTimeout, "http-timeout", time.Second*5, "HTTP server timeout")
Expand Down Expand Up @@ -87,19 +88,21 @@ func TestCommand_Validate(t *testing.T) {
wantErr error
}{
{"success", &Command{
TFTPAddr: "0.0.0.0:69",
TFTPTimeout: 5 * time.Second,
HTTPAddr: "0.0.0.0:8080",
HTTPTimeout: 5 * time.Second,
Log: logr.Discard(),
LogLevel: "info",
TFTPAddr: "0.0.0.0:69",
TFTPBlockSize: 512,
TFTPTimeout: 5 * time.Second,
HTTPAddr: "0.0.0.0:8080",
HTTPTimeout: 5 * time.Second,
Log: logr.Discard(),
LogLevel: "info",
}, nil},
{"fail", &Command{
TFTPTimeout: 5 * time.Second,
HTTPAddr: "0.0.0.0:8080",
HTTPTimeout: 5 * time.Second,
Log: logr.Discard(),
LogLevel: "info",
TFTPBlockSize: 512,
TFTPTimeout: 5 * time.Second,
HTTPAddr: "0.0.0.0:8080",
HTTPTimeout: 5 * time.Second,
Log: logr.Discard(),
LogLevel: "info",
}, fmt.Errorf(`Key: 'Command.TFTPAddr' Error:Field validation for 'TFTPAddr' failed on the 'required' tag`)},
}
for _, tt := range tests {
Expand Down
12 changes: 9 additions & 3 deletions ipxedust.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type ServerSpec struct {
Timeout time.Duration
// Disabled allows a server to be disabled. Useful, for example, to disable TFTP.
Disabled bool
// BlockSize allows setting a larger maximum block size for TFTP
BlockSize int
// The patch to apply to the iPXE binary.
Patch []byte
}
Expand All @@ -59,6 +61,8 @@ var errNilListener = fmt.Errorf("listener must not be nil")
//
// Default TFTP listen address is ":69".
//
// Default TFTP block size is 512.
//
// Default HTTP listen address is ":8080".
//
// Default request timeout for both is 5 seconds.
Expand All @@ -67,7 +71,7 @@ var errNilListener = fmt.Errorf("listener must not be nil")
// See binary/binary.go for the iPXE files that are served.
func (c *Server) ListenAndServe(ctx context.Context) error {
defaults := Server{
TFTP: ServerSpec{Addr: netip.AddrPortFrom(netip.IPv4Unspecified(), 69), Timeout: 5 * time.Second},
TFTP: ServerSpec{Addr: netip.AddrPortFrom(netip.IPv4Unspecified(), 69), Timeout: 5 * time.Second, BlockSize: 512},
HTTP: ServerSpec{Addr: netip.AddrPortFrom(netip.IPv4Unspecified(), 8080), Timeout: 5 * time.Second},
Log: logr.Discard(),
}
Expand Down Expand Up @@ -190,10 +194,11 @@ func (c *Server) listenAndServeTFTP(ctx context.Context) error {
h := &itftp.Handler{Log: c.Log, Patch: c.TFTP.Patch}
ts := tftp.NewServer(h.HandleRead, h.HandleWrite)
ts.SetTimeout(c.TFTP.Timeout)
ts.SetBlockSize(c.TFTP.BlockSize)
if c.EnableTFTPSinglePort {
ts.EnableSinglePort()
}
c.Log.Info("serving iPXE binaries via TFTP", "addr", c.TFTP.Addr, "timeout", c.TFTP.Timeout, "singlePortEnabled", c.EnableTFTPSinglePort)
c.Log.Info("serving iPXE binaries via TFTP", "addr", c.TFTP.Addr, "blocksize", c.TFTP.BlockSize, "timeout", c.TFTP.Timeout, "singlePortEnabled", c.EnableTFTPSinglePort)
go func() {
<-ctx.Done()
conn.Close()
Expand All @@ -210,10 +215,11 @@ func (c *Server) serveTFTP(ctx context.Context, conn net.PacketConn) error {
h := &itftp.Handler{Log: c.Log, Patch: c.TFTP.Patch}
ts := tftp.NewServer(h.HandleRead, h.HandleWrite)
ts.SetTimeout(c.TFTP.Timeout)
ts.SetBlockSize(c.TFTP.BlockSize)
if c.EnableTFTPSinglePort {
ts.EnableSinglePort()
}
c.Log.Info("serving iPXE binaries via TFTP", "addr", conn.LocalAddr().String(), "timeout", c.TFTP.Timeout, "singlePortEnabled", c.EnableTFTPSinglePort)
c.Log.Info("serving iPXE binaries via TFTP", "addr", conn.LocalAddr().String(), "blocksize", c.TFTP.BlockSize, "timeout", c.TFTP.Timeout, "singlePortEnabled", c.EnableTFTPSinglePort)
go func() {
<-ctx.Done()
conn.Close()
Expand Down

0 comments on commit 152536c

Please sign in to comment.