transport

package
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 8, 2026 License: GPL-3.0 Imports: 10 Imported by: 0

Documentation

Overview

Package transport implements the iSCSI TCP transport layer: connection management, PDU framing over TCP streams, concurrent read/write pumps, ITT-based response routing, and buffer pool management.

Index

Constants

View Source
const (
	HookSend    uint8 = 0
	HookReceive uint8 = 1
)

PDU hook direction constants. These live in the transport package to avoid a circular dependency between transport and session.

View Source
const DefaultRouterBufDepth = 64

Router manages ITT-based PDU dispatch. When a command goroutine sends a request, it registers an ITT via Register and waits on the returned channel. When the read pump receives a response, it calls Dispatch with the ITT from the BHS to deliver the response to the correct waiter. DefaultRouterBufDepth is the default persistent channel buffer depth.

Variables

This section is empty.

Functions

func GetBHS

func GetBHS() *[pdu.BHSLength]byte

GetBHS returns a reusable 48-byte BHS buffer from the pool. The caller must call PutBHS when done.

func GetBuffer

func GetBuffer(size int) []byte

GetBuffer returns a byte slice of at least size bytes from a size-class pool. The returned slice may be larger than requested. The caller must call PutBuffer when done. The returned slice is NOT zeroed.

func PutBHS

func PutBHS(b *[pdu.BHSLength]byte)

PutBHS returns a BHS buffer to the pool for reuse.

func PutBuffer

func PutBuffer(b []byte)

PutBuffer returns a buffer to the appropriate size-class pool. Buffers larger than the largest pool class are not returned.

func ReadPump

func ReadPump(ctx context.Context, r io.Reader, router *Router,
	unsolicitedCh chan<- *RawPDU, digestHeader, digestData bool,
	logger *slog.Logger, pduHook func(uint8, *RawPDU), maxRecvDSL uint32, digestByteOrder binary.ByteOrder) error

ReadPump continuously reads PDUs from r and dispatches them by ITT. PDUs with the reserved ITT 0xFFFFFFFF (unsolicited target PDUs such as NOP-In pings and async messages) are sent to unsolicitedCh. All other PDUs are delivered via router.Dispatch. Returns when the read fails (connection closed) or ctx is cancelled.

func WithReadFaultAfter

func WithReadFaultAfter(n int64, err error) func(int64) error

WithReadFaultAfter returns a fault function that triggers the given error once cumulative bytes read reaches or exceeds n.

func WithWriteFaultAfter

func WithWriteFaultAfter(n int64, err error) func(int64) error

WithWriteFaultAfter returns a fault function that triggers the given error once cumulative bytes written reaches or exceeds n.

func WritePump

func WritePump(ctx context.Context, w io.Writer, writeCh <-chan *RawPDU,
	logger *slog.Logger, pduHook func(uint8, *RawPDU), digestByteOrder binary.ByteOrder) error

WritePump owns all writes to the underlying connection. It receives RawPDUs from writeCh and serializes them to w one at a time, preventing TCP byte interleaving (Pitfall 7). Returns when ctx is cancelled or writeCh is closed.

func WriteRawPDU

func WriteRawPDU(w io.Writer, p *RawPDU, digestByteOrder ...binary.ByteOrder) error

WriteRawPDU writes a complete iSCSI PDU to w as a single contiguous write. This prevents TCP byte interleaving when used with the write pump (Pitfall 7).

Types

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn wraps a net.Conn with iSCSI transport-level state such as digest negotiation flags and MaxRecvDataSegmentLength enforcement.

func Dial

func Dial(ctx context.Context, addr string) (*Conn, error)

Dial connects to an iSCSI target at the given TCP address using the provided context for timeout/cancellation control.

func NewConnFromNetConn

func NewConnFromNetConn(nc net.Conn) *Conn

NewConnFromNetConn wraps an existing net.Conn as a transport Conn. Used by the session layer and tests when the TCP connection is already established (e.g., after login).

func (*Conn) Close

func (c *Conn) Close() error

Close shuts down the underlying TCP connection.

func (*Conn) DigestByteOrder

func (c *Conn) DigestByteOrder() binary.ByteOrder

DigestByteOrder returns the configured digest byte order. Returns LittleEndian if not explicitly set.

func (*Conn) DigestData

func (c *Conn) DigestData() bool

DigestData reports whether data digests are enabled.

func (*Conn) DigestHeader

func (c *Conn) DigestHeader() bool

DigestHeader reports whether header digests are enabled.

func (*Conn) MaxRecvDSL

func (c *Conn) MaxRecvDSL() uint32

MaxRecvDSL returns the negotiated MaxRecvDataSegmentLength. Zero means not yet negotiated (no limit enforced).

func (*Conn) NetConn

func (c *Conn) NetConn() net.Conn

NetConn returns the underlying net.Conn. Used for testing and low-level access.

func (*Conn) SetDeadline

func (c *Conn) SetDeadline(t time.Time) error

SetDeadline sets the read and write deadlines on the underlying connection.

func (*Conn) SetDigestByteOrder

func (c *Conn) SetDigestByteOrder(bo binary.ByteOrder)

SetDigestByteOrder configures the byte order used for reading and writing CRC32C digest values on the wire. Defaults to LittleEndian, which matches the Linux kernel iSCSI target (LIO) and open-iscsi initiator.

Some targets (notably certain enterprise SAN firmware) use BigEndian. RFC 7143 Section 12.1 does not specify byte order for digest values; this is an interoperability parameter.

func (*Conn) SetDigests

func (c *Conn) SetDigests(header, data bool)

SetDigests configures whether header and data digests are active on this connection. Called after login negotiation completes.

func (*Conn) SetMaxRecvDSL

func (c *Conn) SetMaxRecvDSL(maxDSL uint32)

SetMaxRecvDSL sets the MaxRecvDataSegmentLength for this connection. The transport layer enforces this limit when framing incoming PDUs.

type FaultConn

type FaultConn struct {
	net.Conn
	// contains filtered or unexported fields
}

FaultConn wraps a net.Conn with injectable read/write faults for deterministic error injection in tests. Faults trigger based on cumulative byte counts, allowing precise control over when errors occur during iSCSI PDU exchanges.

func NewFaultConn

func NewFaultConn(conn net.Conn, readFault, writeFault func(int64) error) *FaultConn

NewFaultConn wraps conn with optional read and write fault functions. If readFault or writeFault is nil, the corresponding operation passes through to the underlying connection without fault injection.

func (*FaultConn) Read

func (fc *FaultConn) Read(p []byte) (int, error)

Read implements io.Reader with fault injection. Before each read, it checks the readFault function (if set) against the cumulative byte count.

func (*FaultConn) SetReadFault

func (fc *FaultConn) SetReadFault(f func(int64) error)

SetReadFault sets or replaces the read fault function at runtime.

func (*FaultConn) SetWriteFault

func (fc *FaultConn) SetWriteFault(f func(int64) error)

SetWriteFault sets or replaces the write fault function at runtime.

func (*FaultConn) Write

func (fc *FaultConn) Write(p []byte) (int, error)

Write implements io.Writer with fault injection. Before each write, it checks the writeFault function (if set) against the cumulative byte count.

type RawPDU

type RawPDU struct {
	BHS          [pdu.BHSLength]byte
	AHS          []byte // nil if no AHS
	DataSegment  []byte // copied out, caller-owned
	HeaderDigest uint32 // 0 if not present
	DataDigest   uint32 // 0 if not present
	HasHDigest   bool
	HasDDigest   bool
}

RawPDU holds the raw bytes of an iSCSI PDU as read from or to be written to the wire. The DataSegment is copied into caller-owned memory (not pooled).

func ReadRawPDU

func ReadRawPDU(r io.Reader, digestHeader, digestData bool, maxRecvDSL uint32, digestByteOrder ...binary.ByteOrder) (*RawPDU, error)

ReadRawPDU reads a complete iSCSI PDU from r. It uses io.ReadFull exclusively to handle partial TCP reads correctly (Pitfall 6). The digest booleans control whether header and data digests are expected on the wire.

The returned RawPDU's DataSegment is a freshly allocated slice (caller-owned). Pool scratch buffers are used internally and returned after copying.

type Router

type Router struct {
	// contains filtered or unexported fields
}

func NewRouter

func NewRouter(persistentDepth int) *Router

NewRouter creates a Router with an empty pending map. persistentDepth sets the buffer depth for persistent registrations (0 = DefaultRouterBufDepth).

func (*Router) AllocateITT

func (r *Router) AllocateITT() uint32

AllocateITT allocates the next available ITT without registering a channel. The caller registers separately via RegisterPersistent.

func (*Router) Dispatch

func (r *Router) Dispatch(itt uint32, pdu *RawPDU) bool

Dispatch delivers pdu to the channel registered for the given ITT. For non-persistent entries, it removes the entry after delivery. Returns true if the ITT was found, false otherwise.

func (*Router) PendingCount

func (r *Router) PendingCount() int

PendingCount returns the number of ITTs currently awaiting responses. Intended for diagnostics and testing.

func (*Router) Register

func (r *Router) Register() (uint32, <-chan *RawPDU)

Register allocates the next available ITT (skipping the reserved value 0xFFFFFFFF) and returns it along with a receive-only channel that will carry the response PDU. The channel has capacity 1 so the dispatcher never blocks. The entry is removed after the first Dispatch.

func (*Router) RegisterPersistent

func (r *Router) RegisterPersistent(itt uint32) <-chan *RawPDU

RegisterPersistent creates a persistent registration for the given ITT. Unlike Register, the entry is NOT removed after Dispatch -- it survives multiple PDU deliveries. The session layer uses this for SCSI commands that receive multiple Data-In PDUs before completion. The caller must call Unregister when the command completes.

func (*Router) Unregister

func (r *Router) Unregister(itt uint32)

Unregister removes a pending ITT entry without delivering a PDU. Used for timeout/cancellation cleanup.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL