uiscsi

package module
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: 13 Imported by: 0

README

uiscsi

A pure-userspace iSCSI initiator library for Go.

Status: v1.3.0 -- full RFC 7143 compliance with 87 wire-level conformance tests and 21 E2E tests against real LIO kernel targets. Grouped Session API. Bounded-memory streaming I/O. Configurable performance tuning.

Overview

uiscsi implements the iSCSI protocol (RFC 7143) entirely in userspace. There are no kernel modules, no open-iscsi dependency, and no external tools. Go applications import the library and communicate directly with iSCSI targets over TCP.

The library provides a grouped API organized by concern:

  • sess.SCSI() -- typed SCSI commands (ReadBlocks, Inquiry, ModeSelect, etc.)
  • sess.Raw() -- raw CDB pass-through with bounded-memory streaming
  • sess.TMF() -- task management (AbortTask, LUNReset, etc.)
  • sess.Protocol() -- low-level iSCSI protocol operations

It supports CHAP and mutual CHAP authentication, header and data digest negotiation with CRC32C, and error recovery levels 0 through 2.

Features

  • Pure userspace -- no kernel iSCSI stack, no iscsiadm, no external tools
  • RFC 7143 compliant -- PDU codec, login negotiation, full feature phase
  • Grouped API -- organized by concern (SCSI, TMF, Raw, Protocol) for discoverability
  • Go-idiomatic -- context.Context, io.Reader/io.Writer, functional options
  • Authentication -- CHAP and mutual CHAP
  • Block I/O -- ReadBlocks/WriteBlocks via sess.SCSI()
  • Raw CDB pass-through -- Execute (buffered) and StreamExecute (bounded-memory streaming) via sess.Raw()
  • Streaming I/O -- StreamExecute streams Data-In PDUs via bounded-memory channel, suitable for tape drives at 400+ MB/s
  • Tunable PDU size -- WithMaxRecvDataSegmentLength for high-throughput workloads (default 8KB, recommended 256KB for tape)
  • Mode pages -- ModeSense6/10 and ModeSelect6/10 for device configuration
  • Error recovery -- ERL 0 (session reconnect), ERL 1 (SNACK), ERL 2 (connection replace)
  • Task management -- ABORT TASK, LUN RESET, TARGET WARM/COLD RESET via sess.TMF()
  • Observability -- slog structured logging, PDU hooks, metrics callbacks
  • Digests -- CRC32C header and data digest negotiation and verification
  • Discovery -- SendTargets enumeration, multi-portal support
  • CLI tool -- uiscsi-ls for lsscsi-style target discovery from the command line

Quick Start

go get github.com/rkujawa/uiscsi
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/rkujawa/uiscsi"
)

func main() {
    ctx := context.Background()

    sess, err := uiscsi.Dial(ctx, "192.168.1.100:3260",
        uiscsi.WithTarget("iqn.2026-03.com.example:storage"),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer sess.Close()

    // Typed SCSI commands via sess.SCSI()
    data, err := sess.SCSI().ReadBlocks(ctx, 0, 0, 1, 512)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Read %d bytes from LBA 0\n", len(data))
}

API Reference

Full documentation is available on pkg.go.dev.

Session Accessors
Accessor Returns Purpose
sess.SCSI() *SCSIOps Typed SCSI commands with automatic status/sense handling
sess.Raw() *RawOps Raw CDB pass-through (caller interprets status)
sess.TMF() *TMFOps Task management functions
sess.Protocol() *ProtocolOps Low-level iSCSI protocol operations
SCSI Commands (sess.SCSI())
Method Description
ReadBlocks Read blocks from a LUN
WriteBlocks Write blocks to a LUN
Inquiry SCSI INQUIRY
ReadCapacity Query LUN capacity
TestUnitReady Check LUN readiness
ModeSense6 / ModeSense10 Query mode pages
ModeSelect6 / ModeSelect10 Set mode pages
ReportLuns Enumerate LUNs
SynchronizeCache Flush volatile cache
Verify Verify LBA range
WriteSame Fill LBA range
Unmap Thin provisioning deallocate
CompareAndWrite Atomic read-compare-write
StartStopUnit Power management
PersistReserveIn / PersistReserveOut Persistent reservations
Raw CDB (sess.Raw())
Method Description
Execute Send any CDB, returns buffered *RawResult with []byte data
StreamExecute Send any CDB, returns streaming *StreamResult with io.Reader data
WithDataIn Configure read response allocation
WithDataOut Configure write data
Helpers
Function Description
ParseSenseData Parse raw sense bytes into SenseInfo
CheckStatus Convert SCSI status + sense into *SCSIError
DecodeLUN Decode SAM LUN encoding
DeviceTypeName Human-readable device type
Performance Tuning

For high-throughput workloads (tape drives, large sequential I/O):

sess, err := uiscsi.Dial(ctx, addr,
    uiscsi.WithTarget(iqn),
    uiscsi.WithStreamBufDepth(128),              // streaming PDU buffer (default 128)
    uiscsi.WithRouterBufDepth(64),               // dispatch buffer (default 64)
    uiscsi.WithMaxRecvDataSegmentLength(262144),  // max PDU size (default 8KB)
    uiscsi.WithMaxBurstLength(524288),            // write burst size (default 256KB)
    uiscsi.WithFirstBurstLength(131072),          // unsolicited write (default 64KB)
)

StreamBufDepth and RouterBufDepth control internal PDU buffering. These are critical for tape drives: shallow buffers cause TCP backpressure during GC pauses, stopping the tape drive (shoe-shining). The defaults (128 + 64) provide ~1.5MB of buffering at 8KB MRDSL — enough to absorb 50+ ms of consumer stalls.

Error Types
Type Description
SCSIError SCSI command failure with sense data
TransportError iSCSI transport/connection failure
AuthError Authentication failure

Testing

The library includes three test tiers:

  • Unit tests -- table-driven tests for PDU codec, serial arithmetic, sense parsing (go test ./...)
  • Conformance tests -- 87 wire-level tests against an in-process mock iSCSI target with PDU capture (test/conformance/). Covers 84% of the UNH-IOL Initiator Full Feature Phase test suite (see doc/test_matrix_initiator_ffp.md).
  • E2E tests -- 21 tests against a real Linux LIO kernel target via configfs (sudo go test -tags e2e ./test/e2e/)

Requirements

  • Go 1.25 or later
  • No external runtime dependencies (stdlib only)
  • E2E tests require Linux with target_core_mod and iscsi_target_mod kernel modules

Documentation

Overview

errors.go defines the public error hierarchy for the uiscsi package. All errors support errors.As/errors.Is for inspection.

options.go defines functional options for Dial and Discover.

sense.go provides public sense data parsing and status checking helpers for callers of Execute and StreamExecute.

session.go defines the Session type, accessor methods for grouped APIs, and deprecated flat methods for backward compatibility.

types.go defines the public wrapper types for the uiscsi package. All types are defined in the root package to avoid exposing internal types.

Package uiscsi provides a pure-userspace iSCSI initiator library. It handles TCP connection, login negotiation, and SCSI command transport over iSCSI PDUs entirely in userspace.

Quick Start

sess, err := uiscsi.Dial(ctx, "192.168.1.100:3260",
    uiscsi.WithTarget("iqn.2026-03.com.example:storage"),
)
if err != nil { ... }
defer sess.Close()

data, err := sess.SCSI().ReadBlocks(ctx, 0, 0, 1, 512)

API Tiers

uiscsi provides three tiers of SCSI command access, each with different error handling. Choose based on your use case:

## Tier 1: Typed SCSI Methods (recommended for common operations)

Access via Session.SCSI. Returns parsed Go types. CHECK CONDITION is automatically converted to *SCSIError.

data, err := sess.SCSI().ReadBlocks(ctx, lun, lba, blocks, blockSize)
inq, err := sess.SCSI().Inquiry(ctx, lun)
err := sess.SCSI().ModeSelect6(ctx, lun, modeData)

## Tier 2: Buffered Raw CDB (for device-specific commands)

Access via Session.Raw. Returns raw status + sense bytes. The caller interprets SCSI status. Use CheckStatus or ParseSenseData for convenience.

result, err := sess.Raw().Execute(ctx, lun, cdb, uiscsi.WithDataIn(256))
if err := uiscsi.CheckStatus(result.Status, result.SenseData); err != nil { ... }

## Tier 3: Streaming Raw CDB (for high-throughput sequential I/O)

Access via Session.Raw. Response data delivered as io.Reader with bounded memory (~64KB). Critical for tape drives and large block transfers.

sr, err := sess.Raw().StreamExecute(ctx, lun, cdb, uiscsi.WithDataIn(blockSize))
_, err = io.Copy(dst, sr.Data)
status, sense, err := sr.Wait()

Performance Tuning

The default MaxRecvDataSegmentLength is 8192 bytes (8KB per PDU). For high-throughput workloads (tape, large block I/O), increase it with WithMaxRecvDataSegmentLength:

sess, err := uiscsi.Dial(ctx, addr,
    uiscsi.WithTarget(iqn),
    uiscsi.WithMaxRecvDataSegmentLength(262144), // 256KB per PDU
)

This reduces per-PDU overhead and improves streaming throughput. With StreamExecute, the bounded-memory window scales with MRDSL: 8 chunks × MRDSL bytes (e.g., 8 × 256KB = 2MB in-flight with 256KB MRDSL).

Other API Groups

Task management: Session.TMF — AbortTask, LUNReset, TargetWarmReset, etc.

Protocol operations: Session.Protocol — Logout, SendExpStatSNConfirmation.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckStatus added in v1.1.2

func CheckStatus(status uint8, senseData []byte) error

CheckStatus inspects a SCSI status byte and optional raw sense data, returning nil for GOOD status (0x00) and a *SCSIError for anything else. This is the same interpretation that typed Session methods (Session.ReadBlocks, Session.Inquiry, etc.) apply internally.

Use this with Session.Execute or StreamResult.Wait when you want the same error-wrapping behavior without reimplementing it:

result, err := sess.Execute(ctx, lun, cdb, uiscsi.WithDataIn(256))
if err != nil { return err }
if err := uiscsi.CheckStatus(result.Status, result.SenseData); err != nil {
    var se *uiscsi.SCSIError
    if errors.As(err, &se) { /* se.SenseKey, se.ASC, se.ASCQ */ }
    return err
}

func DecodeLUN

func DecodeLUN(raw uint64) uint64

DecodeLUN extracts the flat LUN number from an 8-byte SAM LUN encoding as returned by Session.ReportLuns. SAM-5 Section 4.6.1 defines the encoding: the first two bytes carry the address method and LUN value.

For peripheral device addressing (method 00b), the LUN is in the low 8 bits of the first two bytes. For flat space addressing (method 01b), the LUN is a 14-bit value. Other methods return the raw upper 16 bits.

func DeviceTypeName

func DeviceTypeName(code uint8) string

DeviceTypeName returns the short human-readable name for a SCSI peripheral device type code as found in [InquiryData.DeviceType]. The names follow the SPC-4 Table 49 convention used by lsscsi. Unknown or unmapped codes return "unknown".

Types

type AsyncEvent

type AsyncEvent struct {
	EventCode  uint8
	VendorCode uint8
	Parameter1 uint16
	Parameter2 uint16
	Parameter3 uint16
	Data       []byte
}

AsyncEvent carries an asynchronous event from the target.

type AuthError

type AuthError struct {
	StatusClass  uint8
	StatusDetail uint8
	Message      string
}

AuthError represents an authentication failure during login.

func (*AuthError) Error

func (e *AuthError) Error() string

Error returns a human-readable description of the auth error.

type Capacity

type Capacity struct {
	LBA           uint64
	BlockSize     uint32
	LogicalBlocks uint64
}

Capacity holds a parsed READ CAPACITY response (unified for 10/16).

type ExecuteOption

type ExecuteOption = executeOption

ExecuteOption configures raw CDB execution.

func WithDataIn

func WithDataIn(allocLen uint32) ExecuteOption

WithDataIn configures a read response of up to allocLen bytes.

func WithDataOut

func WithDataOut(r io.Reader, length uint32) ExecuteOption

WithDataOut configures data to send with the command.

type InquiryData

type InquiryData struct {
	DeviceType uint8
	VendorID   string
	ProductID  string
	Revision   string
}

InquiryData holds a parsed INQUIRY response.

type MetricEvent

type MetricEvent struct {
	Type    MetricEventType
	OpCode  uint8
	Bytes   uint64
	Latency time.Duration
}

MetricEvent carries a single metric observation.

type MetricEventType

type MetricEventType uint8

MetricEventType discriminates metric event kinds.

Metric event type constants, mirroring internal session values.

type Option

type Option func(*dialConfig)

Option configures a Dial or Discover call via the functional options pattern.

func WithAsyncHandler

func WithAsyncHandler(h func(context.Context, AsyncEvent)) Option

WithAsyncHandler registers an async event callback.

func WithCHAP

func WithCHAP(user, secret string) Option

WithCHAP enables CHAP authentication.

Example
package main

import (
	"context"

	"github.com/rkujawa/uiscsi"
)

func main() {
	ctx := context.Background()
	_, _ = uiscsi.Dial(ctx, "192.168.1.100:3260",
		uiscsi.WithTarget("iqn.2026-03.com.example:storage"),
		uiscsi.WithCHAP("initiator-user", "s3cret"),
	)
}

func WithDataDigest

func WithDataDigest(prefs ...string) Option

WithDataDigest sets data digest preferences.

func WithDigestByteOrder

func WithDigestByteOrder(bo binary.ByteOrder) Option

WithDigestByteOrder sets the byte order for CRC32C digest values on the wire. Default is LittleEndian (matches Linux LIO target). Set to binary.BigEndian for targets that use network byte order for digests.

func WithFirstBurstLength added in v1.3.0

func WithFirstBurstLength(size uint32) Option

WithFirstBurstLength sets the maximum unsolicited Data-Out size for write operations. Default is 65536 (64KB). Data up to this size is sent immediately with the SCSI Command PDU, without waiting for an R2T.

func WithHeaderDigest

func WithHeaderDigest(prefs ...string) Option

WithHeaderDigest sets header digest preferences.

func WithInitiatorName

func WithInitiatorName(iqn string) Option

WithInitiatorName sets the initiator IQN.

func WithKeepaliveInterval

func WithKeepaliveInterval(d time.Duration) Option

WithKeepaliveInterval sets the keepalive ping interval.

func WithKeepaliveTimeout

func WithKeepaliveTimeout(d time.Duration) Option

WithKeepaliveTimeout sets the keepalive timeout.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the slog.Logger for both session and login diagnostics.

Example
package main

import (
	"context"
	"log/slog"
	"os"

	"github.com/rkujawa/uiscsi"
)

func main() {
	logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()
	_, _ = uiscsi.Dial(ctx, "192.168.1.100:3260",
		uiscsi.WithTarget("iqn.2026-03.com.example:storage"),
		uiscsi.WithLogger(logger),
	)
}

func WithMaxBurstLength added in v1.3.0

func WithMaxBurstLength(size uint32) Option

WithMaxBurstLength sets the maximum data burst size for solicited Data-Out transfers (write operations). Default is 262144 (256KB). Increasing this allows larger write bursts per R2T, reducing R2T round-trips for large writes.

func WithMaxReconnectAttempts

func WithMaxReconnectAttempts(n int) Option

WithMaxReconnectAttempts sets the maximum number of ERL 0 reconnect attempts.

func WithMaxRecvDataSegmentLength added in v1.3.0

func WithMaxRecvDataSegmentLength(size uint32) Option

WithMaxRecvDataSegmentLength sets the maximum data segment size (in bytes) that the initiator is willing to receive per PDU. The target uses this to limit Data-In PDU sizes. Default is 8192 (8KB).

For high-throughput workloads (tape drives, large block I/O), increasing this to 65536 (64KB) or 262144 (256KB) significantly reduces per-PDU overhead and improves streaming performance. The value must be between 512 and 16777215 per RFC 7143.

This controls the initiator's declared value. The target independently declares its own MaxRecvDataSegmentLength (limiting Data-Out PDU sizes from the initiator). Both directions are negotiated independently.

func WithMetricsHook

func WithMetricsHook(h func(MetricEvent)) Option

WithMetricsHook registers a metrics callback.

func WithMutualCHAP

func WithMutualCHAP(user, secret, targetSecret string) Option

WithMutualCHAP enables mutual CHAP authentication.

func WithOperationalOverrides

func WithOperationalOverrides(overrides map[string]string) Option

WithOperationalOverrides overrides login negotiation parameters. Keys must match RFC 7143 Section 13 key names exactly (e.g., "InitialR2T", "ImmediateData", "MaxBurstLength", "ErrorRecoveryLevel"). Values replace the defaults proposed during login negotiation.

func WithPDUHook

func WithPDUHook(h func(context.Context, PDUDirection, []byte)) Option

WithPDUHook registers a PDU send/receive hook. The []byte argument is the concatenation of BHS (48 bytes) + DataSegment from the internal transport.RawPDU. This avoids exposing internal transport types.

func WithReconnectBackoff

func WithReconnectBackoff(base time.Duration) Option

WithReconnectBackoff sets the reconnect backoff duration.

func WithRouterBufDepth added in v1.3.0

func WithRouterBufDepth(depth int) Option

WithRouterBufDepth sets the PDU dispatch buffer depth per task. Higher values absorb read pump stalls. Default is 64 slots.

func WithSNACKTimeout

func WithSNACKTimeout(d time.Duration) Option

WithSNACKTimeout sets the timeout for SNACK-based PDU retransmission at ERL >= 1. When no Data-In arrives within this duration, a Status SNACK is sent to request the missing response (tail loss recovery). Default is 5 seconds.

func WithStreamBufDepth added in v1.3.0

func WithStreamBufDepth(depth int) Option

WithStreamBufDepth sets the streaming Data-In buffer depth (number of PDU chunks buffered in the chanReader channel). Higher values absorb consumer stalls without triggering TCP backpressure — critical for tape drives that stop streaming on any TCP stall. Default is 128 slots.

func WithTarget

func WithTarget(iqn string) Option

WithTarget sets the target IQN for login.

type PDUDirection

type PDUDirection uint8

PDUDirection indicates whether a PDU was sent or received.

PDU direction constants, mirroring internal session values.

type Portal

type Portal struct {
	Address  string
	Port     int
	GroupTag int
}

Portal represents a target portal address.

type ProtocolOps added in v1.2.0

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

ProtocolOps provides low-level iSCSI protocol methods. Obtain via Session.Protocol.

Most callers should use Session.Close for session teardown. These methods are for implementors who need explicit control over RFC 7143 PDU exchanges.

func (*ProtocolOps) Logout added in v1.2.0

func (o *ProtocolOps) Logout(ctx context.Context) error

Logout performs a graceful session logout. It waits for in-flight commands to complete, then exchanges Logout/LogoutResp PDUs with the target before shutting down. Per RFC 7143 Section 11.14. Most callers should use Session.Close instead, which calls Logout internally.

func (*ProtocolOps) SendExpStatSNConfirmation added in v1.2.0

func (o *ProtocolOps) SendExpStatSNConfirmation() error

SendExpStatSNConfirmation sends a NOP-Out that confirms ExpStatSN to the target without expecting a response. Per RFC 7143 Section 11.18: ITT=0xFFFFFFFF (no response), TTT=0xFFFFFFFF, Immediate=true. CmdSN is carried but NOT advanced. This is an advanced operation; most callers do not need it.

type RawOps added in v1.2.0

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

RawOps provides raw CDB pass-through methods. Obtain via Session.Raw.

Unlike the typed methods on SCSIOps, raw methods do NOT interpret the SCSI status. CHECK CONDITION (0x02) is returned as-is in [RawResult.Status] or via StreamResult.Wait. Use CheckStatus or ParseSenseData for convenience.

func (*RawOps) Execute added in v1.2.0

func (o *RawOps) Execute(ctx context.Context, lun uint64, cdb []byte, opts ...ExecuteOption) (*RawResult, error)

Execute sends a raw CDB to the target and returns the buffered result. The entire response is read into [RawResult.Data] as []byte. For high-throughput streaming, use RawOps.StreamExecute instead.

func (*RawOps) StreamExecute added in v1.2.0

func (o *RawOps) StreamExecute(ctx context.Context, lun uint64, cdb []byte, opts ...ExecuteOption) (*StreamResult, error)

StreamExecute sends a raw CDB and returns a streaming result. Response data is delivered via [StreamResult.Data] as an io.Reader with bounded memory (~64KB). Call StreamResult.Wait after consuming Data to retrieve the final SCSI status and sense data.

type RawResult

type RawResult struct {
	Status    uint8
	Data      []byte
	SenseData []byte
}

RawResult carries the raw SCSI command outcome for Execute().

type SCSIError

type SCSIError struct {
	Status   uint8
	SenseKey uint8
	ASC      uint8
	ASCQ     uint8
	Message  string
}

SCSIError represents a SCSI command failure with status and optional sense data.

func (*SCSIError) Error

func (e *SCSIError) Error() string

Error returns a human-readable description of the SCSI error.

type SCSIOps added in v1.2.0

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

SCSIOps provides typed SCSI command methods. Obtain via Session.SCSI.

func (*SCSIOps) CompareAndWrite added in v1.2.0

func (o *SCSIOps) CompareAndWrite(ctx context.Context, lun uint64, lba uint64, blocks uint8, blockSize uint32, data []byte) error

CompareAndWrite performs an atomic read-compare-write operation.

func (*SCSIOps) Inquiry added in v1.2.0

func (o *SCSIOps) Inquiry(ctx context.Context, lun uint64) (*InquiryData, error)

Inquiry sends a standard INQUIRY command and returns the parsed response.

func (*SCSIOps) ModeSelect6 added in v1.2.0

func (o *SCSIOps) ModeSelect6(ctx context.Context, lun uint64, data []byte) error

ModeSelect6 sends a MODE SELECT(6) command with the given parameter data. The data must include the mode parameter header, block descriptor(s), and any mode pages. PF (Page Format) bit is set automatically.

func (*SCSIOps) ModeSelect10 added in v1.2.0

func (o *SCSIOps) ModeSelect10(ctx context.Context, lun uint64, data []byte) error

ModeSelect10 sends a MODE SELECT(10) command with the given parameter data.

func (*SCSIOps) ModeSense6 added in v1.2.0

func (o *SCSIOps) ModeSense6(ctx context.Context, lun uint64, pageCode, subPageCode uint8) ([]byte, error)

ModeSense6 sends a MODE SENSE(6) command and returns the raw mode page bytes.

func (*SCSIOps) ModeSense10 added in v1.2.0

func (o *SCSIOps) ModeSense10(ctx context.Context, lun uint64, pageCode, subPageCode uint8) ([]byte, error)

ModeSense10 sends a MODE SENSE(10) command and returns the raw mode page bytes.

func (*SCSIOps) PersistReserveIn added in v1.2.0

func (o *SCSIOps) PersistReserveIn(ctx context.Context, lun uint64, serviceAction uint8) ([]byte, error)

PersistReserveIn sends a PERSISTENT RESERVE IN command and returns the raw response.

func (*SCSIOps) PersistReserveOut added in v1.2.0

func (o *SCSIOps) PersistReserveOut(ctx context.Context, lun uint64, serviceAction, scopeType uint8, key, saKey uint64) error

PersistReserveOut sends a PERSISTENT RESERVE OUT command.

func (*SCSIOps) ReadBlocks added in v1.2.0

func (o *SCSIOps) ReadBlocks(ctx context.Context, lun uint64, lba uint64, blocks uint32, blockSize uint32) ([]byte, error)

ReadBlocks reads blocks from the target using READ(16).

func (*SCSIOps) ReadCapacity added in v1.2.0

func (o *SCSIOps) ReadCapacity(ctx context.Context, lun uint64) (*Capacity, error)

ReadCapacity returns the capacity of the specified LUN using READ CAPACITY(16) with fallback to READ CAPACITY(10).

func (*SCSIOps) ReportLuns added in v1.2.0

func (o *SCSIOps) ReportLuns(ctx context.Context) ([]uint64, error)

ReportLuns returns all LUNs reported by the target.

func (*SCSIOps) RequestSense added in v1.2.0

func (o *SCSIOps) RequestSense(ctx context.Context, lun uint64) (*SenseInfo, error)

RequestSense sends a REQUEST SENSE command and returns the parsed sense data.

func (*SCSIOps) StartStopUnit added in v1.2.0

func (o *SCSIOps) StartStopUnit(ctx context.Context, lun uint64, powerCondition uint8, start, loadEject bool) error

StartStopUnit sends a START STOP UNIT command.

func (*SCSIOps) SynchronizeCache added in v1.2.0

func (o *SCSIOps) SynchronizeCache(ctx context.Context, lun uint64) error

SynchronizeCache flushes the target's volatile cache for the entire LUN.

func (*SCSIOps) TestUnitReady added in v1.2.0

func (o *SCSIOps) TestUnitReady(ctx context.Context, lun uint64) error

TestUnitReady checks whether the specified LUN is ready.

func (*SCSIOps) Unmap added in v1.2.0

func (o *SCSIOps) Unmap(ctx context.Context, lun uint64, descriptors []UnmapBlockDescriptor) error

Unmap deallocates the specified LBA ranges (thin provisioning).

func (*SCSIOps) Verify added in v1.2.0

func (o *SCSIOps) Verify(ctx context.Context, lun uint64, lba uint64, blocks uint32) error

Verify requests the target to verify the specified LBA range.

func (*SCSIOps) WriteBlocks added in v1.2.0

func (o *SCSIOps) WriteBlocks(ctx context.Context, lun uint64, lba uint64, blocks uint32, blockSize uint32, data []byte) error

WriteBlocks writes blocks to the target using WRITE(16).

func (*SCSIOps) WriteSame added in v1.2.0

func (o *SCSIOps) WriteSame(ctx context.Context, lun uint64, lba uint64, blocks uint32, blockSize uint32, data []byte) error

WriteSame writes the same block of data to the specified LBA range.

type SenseInfo

type SenseInfo struct {
	Key         uint8
	ASC         uint8
	ASCQ        uint8
	Description string
	Filemark    bool
	EOM         bool
	ILI         bool
}

SenseInfo holds parsed sense data.

func ParseSenseData added in v1.1.2

func ParseSenseData(raw []byte) (*SenseInfo, error)

ParseSenseData parses raw SCSI sense bytes (as returned in [RawResult.SenseData] or StreamResult.Wait) into a SenseInfo. Returns nil, nil if data is empty. Returns an error if the sense format is unrecognizable.

This is the same parser used internally by the typed Session methods. It supports both fixed (0x70/0x71) and descriptor (0x72/0x73) formats per SPC-4 Section 4.5.

type Session

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

Session represents an active iSCSI session. It wraps the internal session and provides grouped APIs for SCSI commands, task management, raw CDB pass-through, and protocol operations.

Use the accessor methods to access each API group:

sess.SCSI()     — typed SCSI commands (Inquiry, ReadBlocks, ModeSelect, etc.)
sess.TMF()      — task management (AbortTask, LUNReset, etc.)
sess.Raw()      — raw CDB pass-through (Execute, StreamExecute)
sess.Protocol() — low-level iSCSI protocol (Logout, SendExpStatSNConfirmation)

func Dial

func Dial(ctx context.Context, addr string, opts ...Option) (*Session, error)

Dial connects to an iSCSI target at addr, performs login negotiation, and returns a Session ready for SCSI commands. The caller must call Session.Close when done.

Example
package main

import (
	"context"
	"fmt"

	"github.com/rkujawa/uiscsi"
)

func main() {
	ctx := context.Background()
	sess, err := uiscsi.Dial(ctx, "192.168.1.100:3260",
		uiscsi.WithTarget("iqn.2026-03.com.example:storage"),
	)
	if err != nil {
		fmt.Println("dial:", err)
		return
	}
	defer sess.Close()
	fmt.Println("connected")
}

func (*Session) AbortTask deprecated

func (s *Session) AbortTask(ctx context.Context, taskTag uint32) (*TMFResult, error)

Deprecated: Use sess.TMF().AbortTask.

func (*Session) AbortTaskSet deprecated

func (s *Session) AbortTaskSet(ctx context.Context, lun uint64) (*TMFResult, error)

Deprecated: Use sess.TMF().AbortTaskSet.

func (*Session) ClearTaskSet deprecated

func (s *Session) ClearTaskSet(ctx context.Context, lun uint64) (*TMFResult, error)

Deprecated: Use sess.TMF().ClearTaskSet.

func (*Session) Close

func (s *Session) Close() error

Close shuts down the session, performing a graceful logout if possible.

func (*Session) CompareAndWrite deprecated

func (s *Session) CompareAndWrite(ctx context.Context, lun uint64, lba uint64, blocks uint8, blockSize uint32, data []byte) error

Deprecated: Use sess.SCSI().CompareAndWrite.

func (*Session) Execute deprecated

func (s *Session) Execute(ctx context.Context, lun uint64, cdb []byte, opts ...ExecuteOption) (*RawResult, error)

Deprecated: Use sess.Raw().Execute.

Example
package main

import (
	"context"
	"fmt"

	"github.com/rkujawa/uiscsi"
)

func main() {
	// Raw CDB pass-through: TEST UNIT READY.
	ctx := context.Background()
	sess, err := uiscsi.Dial(ctx, "192.168.1.100:3260",
		uiscsi.WithTarget("iqn.2026-03.com.example:storage"),
	)
	if err != nil {
		return
	}
	defer sess.Close()

	turCDB := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // TEST UNIT READY
	result, err := sess.Execute(ctx, 0, turCDB)
	if err != nil {
		fmt.Println("execute:", err)
		return
	}
	fmt.Printf("status: 0x%02X\n", result.Status)
}

func (*Session) Inquiry deprecated

func (s *Session) Inquiry(ctx context.Context, lun uint64) (*InquiryData, error)

Deprecated: Use sess.SCSI().Inquiry.

func (*Session) LUNReset deprecated

func (s *Session) LUNReset(ctx context.Context, lun uint64) (*TMFResult, error)

Deprecated: Use sess.TMF().LUNReset.

func (*Session) Logout deprecated

func (s *Session) Logout(ctx context.Context) error

Deprecated: Use sess.Protocol().Logout.

func (*Session) ModeSense6 deprecated

func (s *Session) ModeSense6(ctx context.Context, lun uint64, pageCode, subPageCode uint8) ([]byte, error)

Deprecated: Use sess.SCSI().ModeSense6.

func (*Session) ModeSense10 deprecated

func (s *Session) ModeSense10(ctx context.Context, lun uint64, pageCode, subPageCode uint8) ([]byte, error)

Deprecated: Use sess.SCSI().ModeSense10.

func (*Session) PersistReserveIn deprecated

func (s *Session) PersistReserveIn(ctx context.Context, lun uint64, serviceAction uint8) ([]byte, error)

Deprecated: Use sess.SCSI().PersistReserveIn.

func (*Session) PersistReserveOut deprecated

func (s *Session) PersistReserveOut(ctx context.Context, lun uint64, serviceAction, scopeType uint8, key, saKey uint64) error

Deprecated: Use sess.SCSI().PersistReserveOut.

func (*Session) Protocol added in v1.2.0

func (s *Session) Protocol() *ProtocolOps

Protocol returns the low-level iSCSI protocol interface.

func (*Session) Raw added in v1.2.0

func (s *Session) Raw() *RawOps

Raw returns the raw CDB pass-through interface.

func (*Session) ReadBlocks deprecated

func (s *Session) ReadBlocks(ctx context.Context, lun uint64, lba uint64, blocks uint32, blockSize uint32) ([]byte, error)

Deprecated: Use sess.SCSI().ReadBlocks.

Example
package main

import (
	"context"
	"fmt"

	"github.com/rkujawa/uiscsi"
)

func main() {
	ctx := context.Background()
	sess, err := uiscsi.Dial(ctx, "192.168.1.100:3260",
		uiscsi.WithTarget("iqn.2026-03.com.example:storage"),
	)
	if err != nil {
		return
	}
	defer sess.Close()

	data, err := sess.ReadBlocks(ctx, 0, 0, 1, 512)
	if err != nil {
		fmt.Println("read:", err)
		return
	}
	fmt.Printf("read %d bytes\n", len(data))
}

func (*Session) ReadCapacity deprecated

func (s *Session) ReadCapacity(ctx context.Context, lun uint64) (*Capacity, error)

Deprecated: Use sess.SCSI().ReadCapacity.

func (*Session) ReportLuns deprecated

func (s *Session) ReportLuns(ctx context.Context) ([]uint64, error)

Deprecated: Use sess.SCSI().ReportLuns.

func (*Session) RequestSense deprecated

func (s *Session) RequestSense(ctx context.Context, lun uint64) (*SenseInfo, error)

Deprecated: Use sess.SCSI().RequestSense.

func (*Session) SCSI added in v1.2.0

func (s *Session) SCSI() *SCSIOps

SCSI returns the typed SCSI command interface.

func (*Session) SendExpStatSNConfirmation deprecated

func (s *Session) SendExpStatSNConfirmation() error

Deprecated: Use sess.Protocol().SendExpStatSNConfirmation.

func (*Session) StartStopUnit deprecated

func (s *Session) StartStopUnit(ctx context.Context, lun uint64, powerCondition uint8, start, loadEject bool) error

Deprecated: Use sess.SCSI().StartStopUnit.

func (*Session) StreamExecute deprecated added in v1.1.2

func (s *Session) StreamExecute(ctx context.Context, lun uint64, cdb []byte, opts ...ExecuteOption) (*StreamResult, error)

Deprecated: Use sess.Raw().StreamExecute.

func (*Session) SynchronizeCache deprecated

func (s *Session) SynchronizeCache(ctx context.Context, lun uint64) error

Deprecated: Use sess.SCSI().SynchronizeCache.

func (*Session) TMF added in v1.2.0

func (s *Session) TMF() *TMFOps

TMF returns the task management function interface.

func (*Session) TargetColdReset deprecated

func (s *Session) TargetColdReset(ctx context.Context) (*TMFResult, error)

Deprecated: Use sess.TMF().TargetColdReset.

func (*Session) TargetWarmReset deprecated

func (s *Session) TargetWarmReset(ctx context.Context) (*TMFResult, error)

Deprecated: Use sess.TMF().TargetWarmReset.

func (*Session) TestUnitReady deprecated

func (s *Session) TestUnitReady(ctx context.Context, lun uint64) error

Deprecated: Use sess.SCSI().TestUnitReady.

func (*Session) Unmap deprecated

func (s *Session) Unmap(ctx context.Context, lun uint64, descriptors []UnmapBlockDescriptor) error

Deprecated: Use sess.SCSI().Unmap.

func (*Session) Verify deprecated

func (s *Session) Verify(ctx context.Context, lun uint64, lba uint64, blocks uint32) error

Deprecated: Use sess.SCSI().Verify.

func (*Session) WriteBlocks deprecated

func (s *Session) WriteBlocks(ctx context.Context, lun uint64, lba uint64, blocks uint32, blockSize uint32, data []byte) error

Deprecated: Use sess.SCSI().WriteBlocks.

Example
package main

import (
	"context"
	"fmt"

	"github.com/rkujawa/uiscsi"
)

func main() {
	// Show write + readback verification pattern.
	ctx := context.Background()
	sess, err := uiscsi.Dial(ctx, "192.168.1.100:3260",
		uiscsi.WithTarget("iqn.2026-03.com.example:storage"),
	)
	if err != nil {
		return
	}
	defer sess.Close()

	data := make([]byte, 512)
	copy(data, []byte("hello iSCSI"))
	if err := sess.WriteBlocks(ctx, 0, 0, 1, 512, data); err != nil {
		fmt.Println("write:", err)
	}
}

func (*Session) WriteSame deprecated

func (s *Session) WriteSame(ctx context.Context, lun uint64, lba uint64, blocks uint32, blockSize uint32, data []byte) error

Deprecated: Use sess.SCSI().WriteSame.

type StreamResult added in v1.1.2

type StreamResult struct {
	// Data streams the SCSI read response as PDUs arrive from the target.
	// Nil for non-read commands. Must be fully consumed before calling Wait.
	Data io.Reader
	// contains filtered or unexported fields
}

StreamResult carries the outcome of a streaming raw SCSI command via Session.StreamExecute. Unlike RawResult, Data is an io.Reader that streams the response as PDUs arrive, without buffering the entire response into []byte. This is critical for high-throughput sequential devices (tape drives, etc.) where large blocks (256KB–4MB) at sustained rates (400+ MB/s) make double-buffering expensive.

Memory usage is bounded to a small number of PDU-sized chunks regardless of the total transfer size (typically ~64KB with default negotiated parameters).

The caller must fully consume Data (or drain to io.Discard), then call StreamResult.Wait to retrieve the final SCSI status and sense data.

Usage:

sr, err := session.StreamExecute(ctx, lun, cdb, uiscsi.WithDataIn(blockSize))
if err != nil { ... }
_, err = io.Copy(dst, sr.Data)     // stream data
status, sense, err := sr.Wait()    // get final status

func (*StreamResult) Wait added in v1.1.2

func (sr *StreamResult) Wait() (status uint8, senseData []byte, err error)

Wait blocks until the SCSI command completes and returns the final status and sense data. The caller should fully consume (or discard) Data before calling Wait; otherwise Wait may block indefinitely due to flow-control backpressure. If the transport fails mid-transfer, Data.Read returns the error, and Wait returns it as well.

type TMFOps added in v1.2.0

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

TMFOps provides task management function methods. Obtain via Session.TMF.

func (*TMFOps) AbortTask added in v1.2.0

func (o *TMFOps) AbortTask(ctx context.Context, taskTag uint32) (*TMFResult, error)

AbortTask aborts a single task identified by its initiator task tag.

func (*TMFOps) AbortTaskSet added in v1.2.0

func (o *TMFOps) AbortTaskSet(ctx context.Context, lun uint64) (*TMFResult, error)

AbortTaskSet aborts all tasks on the specified LUN.

func (*TMFOps) ClearTaskSet added in v1.2.0

func (o *TMFOps) ClearTaskSet(ctx context.Context, lun uint64) (*TMFResult, error)

ClearTaskSet clears all tasks on the specified LUN.

func (*TMFOps) LUNReset added in v1.2.0

func (o *TMFOps) LUNReset(ctx context.Context, lun uint64) (*TMFResult, error)

LUNReset resets the specified LUN.

func (*TMFOps) TargetColdReset added in v1.2.0

func (o *TMFOps) TargetColdReset(ctx context.Context) (*TMFResult, error)

TargetColdReset performs a target cold reset.

func (*TMFOps) TargetWarmReset added in v1.2.0

func (o *TMFOps) TargetWarmReset(ctx context.Context) (*TMFResult, error)

TargetWarmReset performs a target warm reset.

type TMFResult

type TMFResult struct {
	Response uint8
}

TMFResult carries a task management function outcome.

type Target

type Target struct {
	Name    string
	Portals []Portal
}

Target represents a discovered iSCSI target.

func Discover

func Discover(ctx context.Context, addr string, opts ...Option) ([]Target, error)

Discover performs a SendTargets discovery against the iSCSI target at addr. It dials, performs a Discovery session login, issues SendTargets, logs out, and returns the discovered targets.

Example
package main

import (
	"context"
	"fmt"

	"github.com/rkujawa/uiscsi"
)

func main() {
	ctx := context.Background()
	targets, err := uiscsi.Discover(ctx, "192.168.1.100:3260")
	if err != nil {
		fmt.Println("discover:", err)
		return
	}
	for _, t := range targets {
		fmt.Printf("target: %s (%d portals)\n", t.Name, len(t.Portals))
	}
}

type TransportError

type TransportError struct {
	Op  string // "dial", "login", "submit", "read", etc.
	Err error  // underlying error
}

TransportError represents an iSCSI transport/connection failure.

func (*TransportError) Error

func (e *TransportError) Error() string

Error returns a human-readable description of the transport error.

func (*TransportError) Unwrap

func (e *TransportError) Unwrap() error

Unwrap returns the underlying error, enabling errors.As/errors.Is chains.

type UnmapBlockDescriptor

type UnmapBlockDescriptor struct {
	LBA    uint64
	Blocks uint32
}

UnmapBlockDescriptor describes a single LBA range to deallocate.

Directories

Path Synopsis
examples
discover-read command
Command discover-read demonstrates iSCSI target discovery followed by login, capacity query, and block read.
Command discover-read demonstrates iSCSI target discovery followed by login, capacity query, and block read.
error-handling command
Command error-handling demonstrates typed error handling and recovery patterns with the uiscsi library.
Command error-handling demonstrates typed error handling and recovery patterns with the uiscsi library.
raw-cdb command
Command raw-cdb demonstrates sending raw SCSI CDBs via the Execute pass-through API for custom or vendor-specific SCSI commands.
Command raw-cdb demonstrates sending raw SCSI CDBs via the Execute pass-through API for custom or vendor-specific SCSI commands.
write-verify command
Command write-verify demonstrates writing blocks to an iSCSI target and reading them back to verify correctness.
Command write-verify demonstrates writing blocks to an iSCSI target and reading them back to verify correctness.
internal
digest
Package digest provides CRC32C digest computation for iSCSI header and data digests as specified in RFC 7143 Section 12.1.
Package digest provides CRC32C digest computation for iSCSI header and data digests as specified in RFC 7143 Section 12.1.
login
Package login implements iSCSI login phase authentication and negotiation.
Package login implements iSCSI login phase authentication and negotiation.
pdu
Package pdu provides iSCSI PDU encoding, decoding, and helpers.
Package pdu provides iSCSI PDU encoding, decoding, and helpers.
scsi
Package scsi constructs SCSI Command Descriptor Blocks (CDBs) and parses responses for standard SCSI commands per SPC-4 and SBC-3.
Package scsi constructs SCSI Command Descriptor Blocks (CDBs) and parses responses for standard SCSI commands per SPC-4 and SBC-3.
serial
Package serial implements RFC 1982 serial number arithmetic for iSCSI sequence number comparisons.
Package serial implements RFC 1982 serial number arithmetic for iSCSI sequence number comparisons.
session
Package session implements the iSCSI session layer: command dispatch, CmdSN flow control, Data-In reassembly, and session lifecycle management per RFC 7143.
Package session implements the iSCSI session layer: command dispatch, CmdSN flow control, Data-In reassembly, and session lifecycle management per RFC 7143.
transport
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.
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.
Package test provides an in-process mock iSCSI target for testing.
Package test provides an in-process mock iSCSI target for testing.
pducapture
Package pducapture provides a PDU capture framework for iSCSI test assertions.
Package pducapture provides a PDU capture framework for iSCSI test assertions.

Jump to

Keyboard shortcuts

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