From 80c98a104f3d872a3adc3798a94edd3bc4068abe Mon Sep 17 00:00:00 2001 From: Nahum Shalman Date: Wed, 4 Oct 2023 14:25:12 +0000 Subject: [PATCH] Add ability to set a higher maximum block size for TFTP Signed-off-by: Nahum Shalman --- cmd.go | 21 +++++++++++++-------- cmd_test.go | 25 ++++++++++++++----------- ipxedust.go | 12 +++++++++--- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/cmd.go b/cmd.go index 0e328cc..5f710fa 100644 --- a/cmd.go +++ b/cmd.go @@ -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. @@ -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) @@ -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, @@ -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") diff --git a/cmd_test.go b/cmd_test.go index b954831..751c48d 100644 --- a/cmd_test.go +++ b/cmd_test.go @@ -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") @@ -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 { diff --git a/ipxedust.go b/ipxedust.go index 8910b7a..bc78f97 100644 --- a/ipxedust.go +++ b/ipxedust.go @@ -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 } @@ -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. @@ -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(), } @@ -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() @@ -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()