bmc

package
v0.9.1 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package bmc holds the runtime state of a Baseboard Management Controller.

Nothing in this package does I/O; it is pure in-memory state backed by the abstractions in pkg/hal. The server layer (pkg/server) wires transport, clock, and HAL together with this state to produce a working BMC.

Index

Constants

View Source
const (
	// SOLMaxInstances is the number of simultaneously activatable SOL payload
	// instances (spec v2.0 Table 24-6, 1-based). One instance matches the
	// single shared serial port of the reference hardware model (§15.3).
	SOLMaxInstances = 1

	// SOLMaxPayloadChars bounds character data per SOL packet. The Accepted
	// Character Count field is one byte (Table 15-2) and the 4-byte SOL
	// packet header shares the 255-byte reported payload size.
	SOLMaxPayloadChars = 251

	// SOLRXBufferCap bounds console output buffered between remote-console
	// polls. A full buffer applies backpressure (draining pauses until the
	// console catches up) — the in-tree equivalent of the BMC deasserting
	// CTS (§15.6) — so buffered data is never silently dropped.
	SOLRXBufferCap = 4096

	// SOLPayloadUDPPortDefault is the primary RMCP port reported when the
	// server transport does not expose its bound address.
	SOLPayloadUDPPortDefault = 623
)
View Source
const (
	SOLEncryptionOpSuspend = 0
	SOLEncryptionOpResume  = 1
	SOLEncryptionOpRegenIV = 2
)

Table 24-5 operations for Suspend/Resume Payload Encryption.

View Source
const (
	MaxUsers       = 63 // IPMI spec allows user IDs 1-63
	MaxUserNameLen = 16
	MaxPasswordLen = 20 // 20 bytes for IPMI 2.0 passwords
)
View Source
const DefaultInactivityTimeout = 60 * time.Second

Session inactivity timeout per IPMI spec:

  • v1.5 §6.11.13 Session Inactivity Timeout
  • v2.0 §6.12.15 Session Inactivity Timeout
View Source
const DefaultInactivityTimeoutTolerance = 3 * time.Second

DefaultInactivityTimeoutTolerance is the LAN inactivity tolerance per IPMI v1.5 Table 6-7 (+/- 3 seconds).

View Source
const DefaultSessionEvictInterval = 3 * time.Second

DefaultSessionEvictInterval is how often the server scans for idle sessions. The spec defines the 60-second inactivity limit, not the scan period.

View Source
const MaxSessions = 4

MaxSessions is the minimum number of concurrent sessions required by the spec.

Variables

View Source
var (
	// ErrSOLAlreadyActive → CodeActivatePayloadAlreadyActive (Table 24-2).
	ErrSOLAlreadyActive = errors.New("SOL payload already active")
	// ErrSOLDisabled → CodeActivatePayloadTypeDisabled (Table 24-2).
	ErrSOLDisabled = errors.New("SOL payload type is disabled")
	// ErrSOLPrivilege → CodeInsufficientPrivilege: session privilege below the
	// configured SOL level.
	ErrSOLPrivilege = errors.New("insufficient privilege to activate SOL")
	// ErrSOLEncryptionUnavailable → CodeActivatePayloadCannotActivateWithEncryption
	// (Table 24-2): the session negotiated no encryption algorithm.
	ErrSOLEncryptionUnavailable = errors.New("cannot activate SOL with encryption")
	// ErrSOLEncryptionRequired → CodeActivatePayloadCannotActivateWithoutEncryption
	// (Table 24-2): policy forces encryption the console declined.
	ErrSOLEncryptionRequired = errors.New("cannot activate SOL without encryption")
	// ErrSOLAuthenticationUnavailable → CodeRequestDataFieldInvalid: Table 24-2
	// defines no authentication-specific completion code, so an unsatisfiable
	// authentication request/policy falls back to the generic invalid-data-field
	// code.
	ErrSOLAuthenticationUnavailable = errors.New("cannot activate SOL with authentication")

	// ErrSOLInstanceNotActive → CodeSuspendResumePayloadEncryptionNotActive
	// (Table 24-5): the session owns no active SOL payload instance.
	ErrSOLInstanceNotActive = errors.New("SOL payload instance not active")
	// ErrSOLEncryptionForced → CodeSuspendResumePayloadEncryptionNotAllowed
	// (Table 24-5): SOL configuration parameter #2 forces encryption, so
	// suspending it is not allowed.
	ErrSOLEncryptionForced = errors.New("SOL encryption forced by configuration")
	// ErrSOLEncryptionUnavailableForSession →
	// CodeSuspendResumePayloadEncryptionNotAvailable (Table 24-5): the session
	// negotiated no encryption algorithm at open time.
	ErrSOLEncryptionUnavailableForSession = errors.New("encryption not available for session")
	// ErrSOLOperationUnsupported → CodeSuspendResumePayloadEncryptionNotSupported
	// (Table 24-5): IV regeneration is xRC4-specific; AES-CBC payloads draw a
	// fresh IV per packet already.
	ErrSOLOperationUnsupported = errors.New("operation not supported for SOL payload")
	// ErrSOLNotActive → CodeDeactivatePayloadAlreadyDeactivated (Table 24-3).
	ErrSOLNotActive = errors.New("SOL payload not active")
	// ErrSOLNotOwner → CodeInsufficientPrivilege (Deactivate): session owns no
	// instance and lacks the privilege to force-deactivate another session's
	// payload.
	ErrSOLNotOwner = errors.New("SOL payload owned by another session")
)

SOL activation failure reasons, mapped by the Activate Payload handler to the command-specific completion codes named in pkg/types (spec v2.0 Table 24-2 / Table 24-3 / Table 24-5).

DefaultCipherSuites is the cipher suite set advertised when no explicit configuration is provided. It contains the spec-mandatory suite 3 plus the recommended SHA256 suite 17.

View Source
var DefaultReconnectPolicy = ReconnectPolicy{Initial: time.Second, Factor: 2, Cap: 30 * time.Second}

DefaultReconnectPolicy is the built-in strategy: retry after 1s, then double to a 30s cap, forever. VM-migration-scale outages (minutes) are covered by the cap holding: attempts keep coming, just no more often than every 30s.

View Source
var DefaultV15AuthTypes = []V15AuthType{V15AuthTypeMD5}

DefaultV15AuthTypes is the default set of v1.5 auth types the reference BMC advertises and accepts.

View Source
var ErrChannelNotFound = errors.New("channel not found")

ErrChannelNotFound is returned when the requested channel number is not configured.

View Source
var ErrInvalidUserID = errors.New("user ID must be between 1 and 63")

ErrInvalidUserID is returned for user IDs outside the valid range 1-63.

View Source
var ErrNoSession = errors.New("session not found")

ErrNoSession is returned when the session ID is not in the store.

View Source
var ErrSessionFull = errors.New("no session slots available")

ErrSessionFull is returned when the store has reached capacity.

View Source
var ErrUserNotFound = errors.New("user not found")

ErrUserNotFound is returned when a user ID or name does not exist.

View Source
var ErrUsernameTaken = errors.New("username already taken")

ErrUsernameTaken is returned when trying to create a user with an already-used name.

Functions

func FormatV15AuthTypes

func FormatV15AuthTypes(types []V15AuthType) string

FormatV15AuthTypes formats auth types for logging (e.g. "md5,md2").

func GenerateChallenge

func GenerateChallenge(dst *[16]byte) error

GenerateChallenge fills dst with random bytes for Get Session Challenge.

func GenerateInboundSeq

func GenerateInboundSeq() (uint32, error)

GenerateInboundSeq returns a non-zero initial inbound sequence number.

func InboundSeqValid

func InboundSeqValid(last, seq uint32) bool

InboundSeqValid checks whether seq is within the acceptable sliding window defined by the IPMI spec (section 6.12.13): +15 / -16 of the last accepted value. Session sequence numbers start at 1; 0 is reserved for pre-session packets.

func PackSessionIDLE

func PackSessionIDLE(id uint32) []byte

PackSessionIDLE is a helper for auth code input construction.

func StorageMissing

func StorageMissing(err error) bool

StorageMissing reports whether err indicates a missing FRU device or SDR record (mapped to completion code CBh by storage handlers).

func SupportedCipherSuite

func SupportedCipherSuite(id types.CipherSuiteID) bool

SupportedCipherSuite reports whether the reference server implements every algorithm in the named cipher suite. Configuring an unsupported suite would cause a runtime handshake failure, so callers validate with this before installing a cipher suite list.

func V15AuthTypeName

func V15AuthTypeName(t V15AuthType) string

V15AuthTypeName returns a human-readable name for t.

func V15AuthTypeToCapsBit

func V15AuthTypeToCapsBit(t V15AuthType) uint8

V15AuthTypeToCapsBit maps an auth type to the corresponding bit in Get Channel Authentication Capabilities response byte 3 (bits [5:0]).

func V15InboundSeqValid

func V15InboundSeqValid(sess *V15Session, seq uint32) bool

V15InboundSeqValid reports whether seq is acceptable under Option 1 without mutating session state (for tests).

Types

type BMC

type BMC struct {
	Info DeviceInfo
	GUID [16]byte

	Users    *UserStore
	Channels *ChannelStore
	Sessions *SessionStore

	// V15Sessions tracks IPMI v1.5 LAN sessions (separate from RMCP+ sessions).
	V15Sessions *V15SessionStore

	// SDRRepo tracks SDR repository reservation state (v2.0§33.11).
	SDRRepo *SDRRepoStore
	// SOL holds the SOL payload configuration (v2.0 Table 26-5) and the
	// active SOL instance state machine (v2.0 §15).
	SOL *SOLStore
	// contains filtered or unexported fields
}

BMC is the central state object for an IPMI server.

Callers create a BMC via New and pass it to the server together with a transport and HAL. The BMC does not own any goroutines; lifecycle management belongs to the server.

func New

func New(info DeviceInfo, guid [16]byte, h hal.HAL, opts ...Option) *BMC

New creates a BMC with sane defaults.

h is required; it provides hardware access. opts are applied in order.

func (*BMC) Clock

func (b *BMC) Clock() clock.Clock

Clock returns the time source used by this BMC.

func (*BMC) FRUInventory

func (b *BMC) FRUInventory() *FRUInventory

FRUInventory returns the FRU inventory , or nil when the backing HAL provides no FRU storage.

func (*BMC) HAL

func (b *BMC) HAL() hal.HAL

HAL returns the underlying hardware abstraction.

func (*BMC) HasKG added in v0.9.1

func (b *BMC) HasKG() bool

HasKG reports whether a BMC key (Kg) is configured, without copying it.

func (*BMC) ResolvedCipherSuites

func (b *BMC) ResolvedCipherSuites() []types.CipherSuiteID

ResolvedCipherSuites returns a copy of the cipher suite list to use for advertisement, falling back to DefaultCipherSuites when none was configured.

func (*BMC) ResolvedKG added in v0.9.1

func (b *BMC) ResolvedKG() []byte

ResolvedKG returns a copy of the BMC key (Kg), or nil in one-key mode.

func (*BMC) ResolvedV15AuthTypes

func (b *BMC) ResolvedV15AuthTypes() []V15AuthType

ResolvedV15AuthTypes returns a copy of the v1.5 auth type list, defaulting to MD5.

func (*BMC) SDRRepository

func (b *BMC) SDRRepository() *SDRRepository

SDRRepository returns the cached SDR record repository, or nil when the backing HAL provides no SDR storage.

func (*BMC) SetCipherSuites

func (b *BMC) SetCipherSuites(ids []types.CipherSuiteID)

SetCipherSuites replaces the configured cipher suite list. Each ID must be supported by the reference server (SupportedCipherSuite); an unsupported ID panics, failing at configuration time rather than at handshake time.

func (*BMC) V15AuthTypeEnabled

func (b *BMC) V15AuthTypeEnabled(authType V15AuthType) bool

V15AuthTypeEnabled reports whether authType is configured on this BMC.

func (*BMC) V15LANEnabled

func (b *BMC) V15LANEnabled() bool

V15LANEnabled reports whether the BMC advertises and accepts IPMI v1.5 sessions.

type Channel

type Channel struct {
	Number uint8
	// Medium is the channel's physical medium. It is security-relevant: the
	// handler privilege check treats a session-less request on a
	// [ChannelMediumSystemIF] channel as locally authorized (the system
	// interface is inherently local), so labeling a network-reachable channel
	// as the system interface, or attaching a session to the system-interface
	// channel, would grant unauthenticated callers full privilege.
	Medium     ChannelMedium
	AccessMode ChannelAccessMode
	// MaxPrivilege is the maximum privilege level allowed on this channel.
	MaxPrivilege PrivilegeLevel
	// PerMessageAuth and UserLevelAuth reflect the channel security settings.
	PerMessageAuth bool
	UserLevelAuth  bool
	// PEFAlerts controls whether PEF alerting is enabled on this channel.
	PEFAlerts bool
}

Channel holds the configuration for a single IPMI channel.

type ChannelAccessMode

type ChannelAccessMode uint8

ChannelAccessMode controls whether a channel accepts connections.

const (
	ChannelAccessDisabled    ChannelAccessMode = 0x00
	ChannelAccessPreBootOnly ChannelAccessMode = 0x01
	ChannelAccessAlways      ChannelAccessMode = 0x02
	ChannelAccessShared      ChannelAccessMode = 0x03
)

type ChannelMedium

type ChannelMedium uint8

ChannelMedium identifies the physical medium of a channel (LAN, serial, etc.).

const (
	ChannelMediumIPMBv10  ChannelMedium = 0x01
	ChannelMediumICMB     ChannelMedium = 0x02
	ChannelMediumLAN      ChannelMedium = 0x04
	ChannelMediumSerial   ChannelMedium = 0x05
	ChannelMediumSMBus    ChannelMedium = 0x06
	ChannelMediumSMBusv20 ChannelMedium = 0x07
	ChannelMediumUSBv1    ChannelMedium = 0x08
	ChannelMediumUSBv2    ChannelMedium = 0x09
	ChannelMediumSystemIF ChannelMedium = 0x0C
)

type ChannelStore

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

ChannelStore holds the configuration for all BMC channels.

Channel numbers follow the IPMI spec:

  • 0x00 – primary IPMB
  • 0x01-0x0B – implementation-specific
  • 0x0E – current channel (self-reference, resolved by caller)
  • 0x0F – system interface

func NewChannelStore

func NewChannelStore() *ChannelStore

NewChannelStore returns a ChannelStore pre-populated with a default LAN channel (1) and the system interface (15 / 0x0F).

func (*ChannelStore) All

func (s *ChannelStore) All() []*Channel

All returns snapshot copies of all configured channels.

func (*ChannelStore) Get

func (s *ChannelStore) Get(n uint8) (*Channel, error)

Get returns a snapshot copy of the channel at number n, or ErrChannelNotFound. Channel is all scalar fields, so the copy is a complete, independent snapshot; mutate the store via ChannelStore.Set.

func (*ChannelStore) Set

func (s *ChannelStore) Set(ch *Channel)

Set adds or replaces the channel at number n, storing a private copy so the caller cannot mutate stored state afterwards.

type DeviceInfo

type DeviceInfo struct {
	DeviceID       uint8
	DeviceRevision uint8
	FirmwareMajor  uint8 // major revision (bits 6:0)
	FirmwareMinor  uint8 // minor revision, BCD
	IPMIVersion    uint8 // 0x20 for IPMI 2.0
	ManufacturerID uint32
	ProductID      uint16
	AuxFirmwareRev [4]byte
	// AdditionalDeviceSupport bitfield per Table 20-2.
	AdditionalDeviceSupport uint8
}

DeviceInfo contains the identification data returned by Get Device ID.

type FRUInventory

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

FRUInventory reads FRU inventory blobs from hal.FRUStore and implements FRU Device semantics for Storage NetFn handlers (v2.0§34).

func NewFRUInventory

func NewFRUInventory(store hal.FRUStore) *FRUInventory

NewFRUInventory returns an inventory backed by store.

func (*FRUInventory) AreaSize

func (f *FRUInventory) AreaSize(ctx context.Context, deviceID uint8) (uint16, error)

AreaSize returns the FRU inventory area size in bytes (v2.0§34.1).

func (*FRUInventory) Read

func (f *FRUInventory) Read(ctx context.Context, deviceID uint8) ([]byte, error)

Read returns the full FRU inventory blob for deviceID (v2.0§34.2).

type Option

type Option func(*BMC)

Option configures a BMC.

func WithCipherSuites

func WithCipherSuites(ids []types.CipherSuiteID) Option

WithCipherSuites sets the RMCP+ cipher suites the server advertises and accepts. Each ID must be a suite the reference server implements (SupportedCipherSuite); otherwise an error is returned by New and the default suite list is kept. Pass nil/empty to restore DefaultCipherSuites.

func WithClock

func WithClock(c clock.Clock) Option

WithClock injects a custom clock.Clock. Defaults to clock.Real.

func WithKG

func WithKG(kg []byte) Option

WithKG sets the BMC-level key (Kg) used for two-key RAKP authentication. Leave unset (or pass nil) to use one-key mode (Kuid only).

func WithV15AuthTypes

func WithV15AuthTypes(types []V15AuthType) Option

WithV15AuthTypes sets the IPMI v1.5 authentication types the BMC advertises and accepts. Pass nil/empty to restore DefaultV15AuthTypes.

func WithV15Disabled

func WithV15Disabled() Option

WithV15Disabled turns off IPMI v1.5 LAN session support. RMCP+ (v2.0) is unaffected.

type PrivilegeLevel

type PrivilegeLevel uint8

PrivilegeLevel mirrors types.PrivilegeLevel so bmc stays free of wire-type conversions in session state; handlers map to types before sending responses.

const (
	PrivilegeLevelCallback      PrivilegeLevel = 0x01
	PrivilegeLevelUser          PrivilegeLevel = 0x02
	PrivilegeLevelOperator      PrivilegeLevel = 0x03
	PrivilegeLevelAdministrator PrivilegeLevel = 0x04
	PrivilegeLevelOEM           PrivilegeLevel = 0x05
	PrivilegeLevelNoAccess      PrivilegeLevel = 0x0F
)

type ReconnectPolicy added in v0.9.1

type ReconnectPolicy struct {
	// Initial is the delay after the first failure; 0 retries immediately.
	Initial time.Duration
	// Factor multiplies the delay on each failure (>= 1).
	Factor float64
	// Jitter adds a random delay of up to Jitter × the computed delay
	// (0..1) to desynchronize concurrent reconnection attempts; 0 = none.
	Jitter float64
	// Cap bounds the delay; <= 0 means unbounded.
	Cap time.Duration
	// Steps bounds the number of reconnect attempts (1-based); once
	// exhausted the instance gives up and behaves as if reconnection were
	// disabled. 0 means retry forever.
	Steps int
}

ReconnectPolicy controls when the pump retries attaching to a failed console, after reconnection has been enabled via SOLStore.SetReconnectPolicy. By default reconnection is disabled: the payload stays active, reports status bit [5] (Table 15-2), and recovers only via deactivate/reactivate — the spec's own behavior.

Field semantics follow k8s.io/apimachinery/pkg/util/wait.Backoff. The delay for attempt n (n ≥ 1, counting from the first failure) is

Initial * Factor^(n-1), capped at Cap, plus a jitter of up to
Jitter × the capped value.

Console failures and reconnection are invisible to the remote console: ipmitool keeps its SOL session alive with a Get Device ID keepalive every 15s (ipmi_sol.c, SOL_KEEPALIVE_TIMEOUT) and only exits after 6 missed keepalives (~90s), so the server may back off as long as its RMCP+ path stays up. Keystrokes typed during the outage are lost either way: ipmitool treats a NACK as delivered and never retransmits.

func (*ReconnectPolicy) Delay added in v0.9.1

func (p *ReconnectPolicy) Delay(failures int) (wait time.Duration, giveUp bool)

Delay returns the wait before attempt failures+1 after failures consecutive failed attempts (failures ≥ 1), and whether reconnection should be given up (Steps exhausted).

type SDRCapabilities

type SDRCapabilities struct {
	ModalUpdate    bool
	NonModalUpdate bool
	DeleteSDR      bool
	PartialAddSDR  bool
	ReserveRepo    bool
	GetAllocInfo   bool
}

SDRCapabilities describes which SDR repository operations this BMC supports. Handlers map these flags onto storage.SDROperationSupport (v2.0§33.9).

type SDRRepoAllocInfo

type SDRRepoAllocInfo struct {
	PossibleAllocUnits uint16
	AllocUnitSize      uint16
	FreeAllocUnits     uint16
	LargestFreeBlock   uint16
	MaximumRecordSize  uint8
}

SDRRepoAllocInfo is BMC-side allocation accounting (not a wire response). Handlers map this to storage.GetSDRRepoAllocInfoResponse (v2.0§33.10).

type SDRRepoInfo

type SDRRepoInfo struct {
	SDRVersion      uint8
	RecordCount     uint16
	FreeBytes       int // raw free capacity; §33.9 wire encoding is the handler's job
	MostRecentAdd   time.Time
	MostRecentErase time.Time
	Overflow        bool
	Capabilities    SDRCapabilities
}

SDRRepoInfo is BMC-side repository status (not a wire response). Handlers map this to storage.GetSDRRepoInfoResponse (v2.0§33.9).

type SDRRepoStore

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

SDRRepoStore tracks the active SDR repository reservation (v2.0§33.11).

func NewSDRRepoStore

func NewSDRRepoStore() *SDRRepoStore

NewSDRRepoStore returns an empty SDR reservation tracker.

func (*SDRRepoStore) Reserve

func (s *SDRRepoStore) Reserve() uint16

Reserve invalidates any prior reservation and returns a new non-zero ID.

func (*SDRRepoStore) Validate

func (s *SDRRepoStore) Validate(id uint16) bool

Validate reports whether id matches the active reservation.

type SDRRepository

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

SDRRepository reads SDR records from hal.SDRStore and implements repository semantics for Storage NetFn handlers (v2.0§33).

func NewSDRRepository

func NewSDRRepository(store hal.SDRStore, clk clock.Clock) *SDRRepository

NewSDRRepository returns a repository backed by store.

func (*SDRRepository) AllocInfo

func (r *SDRRepository) AllocInfo(ctx context.Context) (*SDRRepoAllocInfo, error)

AllocInfo returns BMC-side allocation accounting per v2.0§33.10 semantics.

func (*SDRRepository) GetRecord

func (r *SDRRepository) GetRecord(ctx context.Context, recordID uint16) (record []byte, nextID uint16, err error)

GetRecord returns the wire record and next Record ID for repository traversal. Per v2.0§33.12: recordID 0000h maps to the first SDR; FFFFh maps to the last.

func (*SDRRepository) Info

func (r *SDRRepository) Info(ctx context.Context) (*SDRRepoInfo, error)

Info returns BMC-side SDR repository status per v2.0§33.9 semantics.

func (*SDRRepository) RecordIDs

func (r *SDRRepository) RecordIDs(ctx context.Context) ([]uint16, error)

RecordIDs returns the sorted list of stored record IDs.

type SOLConfig added in v0.9.1

type SOLConfig struct {

	// PayloadPort is the RMCP port carrying the SOL payload (Table 26-5 #8).
	// Server-internal, written once at construction and never settable via
	// IPMI (#7/#8 are read-only per SetParam), so a plain field suffices.
	PayloadPort uint16
	// contains filtered or unexported fields
}

SOLConfig holds the SOL configuration parameters of spec v2.0 Table 26-5. Only Set In Progress (#0) and the volatile bit rate (#6) are volatile; the volatile bit rate is reloaded from the non-volatile one on every payload activation (§15.8). Defaults are manufacturer choices (the spec leaves them open): SOL enabled, ADMINISTRATOR privilege, 115.2 kbps.

func NewSOLConfig added in v0.9.1

func NewSOLConfig() *SOLConfig

NewSOLConfig returns a SOLConfig with manufacturer defaults.

func (*SOLConfig) GetParam added in v0.9.1

func (c *SOLConfig) GetParam(selector uint8) ([]byte, bool)

GetParam returns the parameter data bytes for selector (Table 26-5), or false when the selector is not a supported parameter.

func (*SOLConfig) ResetVolatile added in v0.9.1

func (c *SOLConfig) ResetVolatile()

ResetVolatile clears the volatile SOL configuration — only #0 set in progress and #6 volatile bit rate are volatile (Table 26-5) — as after a BMC cold reset / power cycle, which aborts any parameter set in progress (Table 26-3 "set complete" rule).

func (*SOLConfig) SetParam added in v0.9.1

func (c *SOLConfig) SetParam(selector uint8, data []byte) types.CompletionCode

SetParam validates and applies one parameter write (Table 26-3/26-5), returning the command-specific completion code on failure.

type SOLInstance added in v0.9.1

type SOLInstance struct {
	SessionID uint32 // BMC session ID owning the activation
	// contains filtered or unexported fields
}

SOLInstance is one activated SOL payload: the binding between an RMCP+ session and the system console, plus the payload-level sequence state of Table 15-2. All methods are safe for concurrent use.

func (*SOLInstance) OutboundEncrypted added in v0.9.1

func (inst *SOLInstance) OutboundEncrypted() bool

OutboundEncrypted reports whether BMC→console SOL data is currently encrypted: the activation-negotiated setting unless the console toggled it via Suspend/Resume Payload Encryption (Table 24-5). The command only governs data from the BMC ("encryption on all transfers of specified payload data from the BMC"); inbound packets keep their own packet flags.

func (*SOLInstance) ProcessPacket added in v0.9.1

func (inst *SOLInstance) ProcessPacket(ctx context.Context, in *types.SOLPayloadPacket) *types.SOLPayloadPacket

ProcessPacket handles one inbound SOL payload packet (spec v2.0 §15.9/§15.11, Table 15-3) and produces the response packet.

func (*SOLInstance) SetTracef added in v0.9.1

func (inst *SOLInstance) SetTracef(f func(format string, args ...any))

SetTracef installs the diagnostic sink for console lifecycle events. The server wires it to its solDebug-gated SOL trace; a nil sink (the default) keeps the library silent. Callable from any goroutine; events are emitted from the pump.

The sink is printf-shaped, so printf-style loggers plug in directly — logrus.Printf and zap's SugaredLogger.Infof match this signature as-is.

type SOLSendFunc added in v0.9.1

type SOLSendFunc func(pkt *types.SOLPayloadPacket) error

SOLSendFunc transmits one BMC→console SOL payload packet. The server supplies it; it owns session-level encryption, sequencing, and transport.

type SOLSenderFactory added in v0.9.1

type SOLSenderFactory func(sess *Session, inst *SOLInstance) SOLSendFunc

SOLSenderFactory builds the send function for an activation. sess.Addr must already hold the console's transport address.

type SOLStore added in v0.9.1

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

SOLStore owns the SOL configuration and active instances. Packet sequencing lives in SOLInstance; asynchronous output is pushed by the instance pump (spec v2.0 §15.3: the BMC sends SOL data unrequested).

func NewSOLStore added in v0.9.1

func NewSOLStore(h hal.HAL, clk clock.Clock) *SOLStore

NewSOLStore creates a SOLStore. h may be nil or return a nil ConsoleHAL, in which case SOL stays inactive and unadvertised. Console reconnection is disabled by default; enable it with SOLStore.SetReconnectPolicy.

func (*SOLStore) Activate added in v0.9.1

func (s *SOLStore) Activate(ctx context.Context, sess *Session, wantEnc, wantAuth bool) (*SOLInstance, error)

Activate attaches the system console to sess (spec v2.0 §24.1). wantEnc / wantAuth are the remote console's Encryption/Authentication Activation bits from the Activate Payload auxiliary request data. The returned error maps to a Table 24-2 completion code in the handler.

func (*SOLStore) ActivationStatus added in v0.9.1

func (s *SOLStore) ActivationStatus() (capacity uint8, active1to8, active9to16 uint8)

ActivationStatus reports the Table 24-6 instance bitmask: bit (n-1) set when instance n is active.

func (*SOLStore) ActiveSessionID added in v0.9.1

func (s *SOLStore) ActiveSessionID(instance uint8) uint32

ActiveSessionID returns the owning session ID for instance (1-based), or 0 when not activated (Table 24-7).

func (*SOLStore) CloseAll added in v0.9.1

func (s *SOLStore) CloseAll()

CloseAll deactivates every instance; called when the server shuts down.

func (*SOLStore) Config added in v0.9.1

func (s *SOLStore) Config() *SOLConfig

Config returns the SOL configuration parameter store.

func (*SOLStore) Deactivate added in v0.9.1

func (s *SOLStore) Deactivate(sess *Session) error

Deactivate detaches the console (spec v2.0 §24.2). The owning session may always deactivate; another session may force-deactivate when its privilege meets the configured SOL level (Table 26-5 #2) — this is the recovery path for payloads orphaned by a crashed console.

func (*SOLStore) DeactivateBySession added in v0.9.1

func (s *SOLStore) DeactivateBySession(bmcID uint32)

DeactivateBySession drops the instance owned by bmcID, if any. Wired to SessionStore removals: session termination automatically deactivates its payloads (spec v2.0 §24.2 note).

func (*SOLStore) InstanceBySession added in v0.9.1

func (s *SOLStore) InstanceBySession(bmcID uint32) *SOLInstance

InstanceBySession returns the instance owned by bmcID, or nil. The server reads the negotiated protection flags from it before processing packets.

func (*SOLStore) ProcessPacket added in v0.9.1

func (s *SOLStore) ProcessPacket(ctx context.Context, sessionID uint32, in *types.SOLPayloadPacket) *types.SOLPayloadPacket

ProcessPacket handles one inbound SOL payload packet from the owning session and produces the response packet (spec v2.0 §15.9/§15.11, Table 15-3). It returns nil when the session owns no active instance.

func (*SOLStore) SetReconnectPolicy added in v0.9.1

func (s *SOLStore) SetReconnectPolicy(p *ReconnectPolicy)

SetReconnectPolicy enables and configures console reconnection: after a console failure the pump retries the HAL Open on the policy's schedule (see ReconnectPolicy). A nil policy (the default) disables reconnection: the payload reports status bit [5] and recovers only via deactivate/reactivate. Takes effect on the next activation.

func (*SOLStore) SetSenderFactory added in v0.9.1

func (s *SOLStore) SetSenderFactory(f SOLSenderFactory)

SetSenderFactory installs the factory used to build per-activation senders. Called by the server once the transport exists.

func (*SOLStore) Supported added in v0.9.1

func (s *SOLStore) Supported() bool

Supported reports whether the SOL payload type can be activated at all, i.e. a console exists and the type is enabled (Table 26-5 #1).

func (*SOLStore) SuspendResumeEncryption added in v0.9.1

func (s *SOLStore) SuspendResumeEncryption(sess *Session, op uint8) error

SuspendResumeEncryption applies a Table 24-5 operation to the instance owned by sess. Only BMC→console encryption is affected; authentication is untouched and inbound packets keep their activation-negotiated protection.

type Session

type Session struct {
	// BMCID is the session ID assigned by the BMC (sent in Open Session Response).
	BMCID uint32
	// ConsoleID is the session ID chosen by the remote console.
	ConsoleID uint32
	// Handle is the one-byte session handle Get Session Info reports
	// (spec v2.0§22.20). Assigned at allocation, unique among the store's live
	// sessions, never 0x00 ("no session") or the reserved 0xFF.
	Handle uint8

	State SessionState

	// Negotiated algorithms
	AuthAlg      types.AuthAlg
	IntegrityAlg types.IntegrityAlg
	CryptAlg     types.CryptAlg

	// Sequence tracking.
	// InboundSeq is the last accepted sequence number from the console.
	// OutboundSeq is the next sequence number the BMC will use.
	InboundSeq  uint32
	OutboundSeq uint32

	Addr net.Addr

	// ProcMu serializes per-session packet processing. The server spawns a
	// goroutine per inbound packet; without this lock, a burst of packets
	// from one session is processed in scheduler order — scrambling SOL
	// keystroke bytes and racing the inbound-seq check-then-set. The RAKP
	// handlers take it too: duplicate handshake messages for one pending
	// session otherwise race on the nonces, derived keys, and state below.
	// ProcMu may be held while taking the store lock, never the reverse.
	ProcMu sync.Mutex

	// Session keys derived during RAKP.
	SIK []byte
	K1  []byte
	K2  []byte

	// RAKP exchange state (zeroed once session is active).
	ConsoleRand [16]byte
	BMCRand     [16]byte
	Role        uint8 // whole byte from RAKP1, used in HMAC input

	// User and privilege
	User           *User
	PrivilegeLevel PrivilegeLevel
	MaxPrivilege   PrivilegeLevel

	// Channel this session arrived on.
	Channel uint8

	// Timing. LastActivity is guarded by the store lock: it is refreshed via
	// [SessionStore.Touch] when a validated packet is processed and read by
	// eviction, both under that lock. CreatedAt is set before the session is
	// published into the store and never written again.
	CreatedAt    time.Time
	LastActivity time.Time
	// contains filtered or unexported fields
}

Session holds all state for one active or pending IPMI session.

func (*Session) GetAddr added in v0.9.1

func (sess *Session) GetAddr() net.Addr

GetAddr returns the console's transport address, safe to call from any goroutine (the SOL pump targets asynchronous data at it).

func (*Session) NextOutboundSeq added in v0.9.1

func (sess *Session) NextOutboundSeq() uint32

NextOutboundSeq returns the session sequence number for the next outbound packet, advancing the counter shared by command responses and async SOL packets (§15.5).

func (*Session) SetAddr added in v0.9.1

func (sess *Session) SetAddr(addr net.Addr)

SetAddr records the console's transport address under the guard. The server calls it on every accepted inbound packet.

type SessionState

type SessionState uint8

SessionState tracks which phase of session negotiation has been reached.

const (
	// SessionStatePending means Open Session was received but RAKP is incomplete.
	SessionStatePending SessionState = iota
	// SessionStateActive means RAKP completed and commands may flow.
	SessionStateActive
	// SessionStateClosed means the session was explicitly closed or timed out.
	SessionStateClosed
)

type SessionStore

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

SessionStore is a thread-safe registry of active and pending sessions.

func NewSessionStore

func NewSessionStore(clk clock.Clock) *SessionStore

NewSessionStore creates a SessionStore limited to MaxSessions concurrent sessions with the default inactivity timeout.

func NewSessionStoreWithOptions

func NewSessionStoreWithOptions(clk clock.Clock, opts ...SessionStoreOption) *SessionStore

NewSessionStoreWithOptions creates a SessionStore with custom options.

func (*SessionStore) Activate added in v0.9.1

func (s *SessionStore) Activate(bmcID uint32) error

Activate marks the session active after RAKP completes, or returns ErrNoSession when the session was evicted in the meantime (capacity pressure evicts the oldest pending session, which can be one whose RAKP3 is in flight); activating the orphaned struct would hand the console a successful RAKP4 for a session that no longer exists. It takes the store lock because the pending-session eviction scan reads State under it, while the caller holds Session.ProcMu, so a reader under either lock observes a consistent value.

func (*SessionStore) Allocate

func (s *SessionStore) Allocate(consoleID uint32, authAlg types.AuthAlg, integrityAlg types.IntegrityAlg, cryptAlg types.CryptAlg, maxPriv PrivilegeLevel, channel uint8) (*Session, error)

Allocate creates a new pending session and returns it. If capacity is reached, it evicts the oldest pending session (LRU per spec). Returns ErrSessionFull only when all slots are occupied by active sessions.

maxPriv and channel are stored before the session is inserted into the map so the struct is fully initialized before it becomes reachable to other goroutines; callers must not write session fields after Allocate returns without holding Session.ProcMu.

func (*SessionStore) Cap added in v0.9.1

func (s *SessionStore) Cap() int

Cap returns the maximum number of concurrent sessions the store can hold, i.e. the number of slots in the session table.

func (*SessionStore) Close

func (s *SessionStore) Close(bmcID uint32) error

Close marks a session as closed and removes it from the store.

func (*SessionStore) Count

func (s *SessionStore) Count() int

Count returns the number of sessions currently in the store.

func (*SessionStore) EvictExpired

func (s *SessionStore) EvictExpired() int

EvictExpired removes all sessions that have been inactive beyond the timeout. Called periodically by the server.

func (*SessionStore) Get

func (s *SessionStore) Get(bmcID uint32) (*Session, error)

Get returns the session for bmcID, or ErrNoSession. It is a pure lookup: activity is refreshed separately via SessionStore.Touch, only once a packet has passed integrity and sequence validation. Refreshing on lookup would let packets that fail validation, or that merely name a session ID, keep the session alive forever.

func (*SessionStore) SetOnRemove added in v0.9.1

func (s *SessionStore) SetOnRemove(fn func(bmcID uint32))

SetOnRemove registers the hook fired when a session leaves the store.

func (*SessionStore) Touch added in v0.9.1

func (s *SessionStore) Touch(bmcID uint32)

Touch refreshes the session's inactivity clock. The server calls it for every packet that passed integrity and sequence validation. Handshake packets never touch: RAKP messages carry no authenticator, so the inactivity budget stamped at allocation bounds the whole handshake instead.

type SessionStoreOption

type SessionStoreOption func(*SessionStore)

Option configures a SessionStore.

func WithInactivityTimeout

func WithInactivityTimeout(d time.Duration) SessionStoreOption

WithInactivityTimeout overrides the default 60-second inactivity timeout.

func WithMaxSessions

func WithMaxSessions(n int) SessionStoreOption

WithMaxSessions overrides the default session limit.

type User

type User struct {
	// ID is the IPMI user slot (1-63).  Slot 1 is the anonymous/null user.
	ID   uint8
	Name string
	// Password is stored as a 20-byte padded value per the IPMI 2.0 spec.
	// Index 0 is valid; a zero-length slice means no password is set.
	Password [MaxPasswordLen]byte
	// Password20 records whether the password was stored with the 20-byte size
	// tag (spec v2.0§22.30). The size is part of the credential: a test with the
	// other size must fail, and a 20-byte password exists only in IPMI 2.0, so
	// it must not authenticate a v1.5 session.
	Password20 bool
	Enabled    bool

	// ChannelAccess holds per-channel access settings keyed by channel number.
	ChannelAccess map[uint8]UserChannelAccess

	// PayloadAccess holds per-channel payload activation rights keyed by
	// channel number (spec v2.0 §24.6/§24.7). Like every other mutable User
	// field it follows the store's snapshot discipline: lookups hand out
	// deep copies, and a runtime change goes through [UserStore.Update].
	PayloadAccess map[uint8]UserPayloadAccess
}

User represents a single BMC user account.

func (*User) PasswordV15Padded

func (u *User) PasswordV15Padded() []byte

PasswordV15Padded returns the user's password zero-padded to 16 bytes per IPMI v1.5 AuthCode algorithms (spec v1.5§18.15.1 / v2.0§22.17.1).

func (*User) PayloadAccessFor added in v0.9.1

func (u *User) PayloadAccessFor(channel uint8) UserPayloadAccess

PayloadAccessFor returns the user's payload access entry for channel, or the default rights (SOL enabled) when none was ever set.

func (*User) SetPassword

func (u *User) SetPassword(raw []byte)

SetPassword copies up to MaxPasswordLen bytes from raw into the User's password field. The size class is inferred from the input length: anything longer than 16 bytes is a 20-byte (IPMI 2.0 only) password. Pass the wire-tagged length unmodified so the class survives trailing zero bytes.

func (*User) SetPayloadAccess added in v0.9.1

func (u *User) SetPayloadAccess(channel uint8, enable bool, standard1, oem1 uint8)

SetPayloadAccess applies an enable/disable update to the user's payload access entry for channel (spec v2.0 Table 24-8: on enable, 1-bits set and 0-bits leave unchanged; on disable, 1-bits clear). Like any other User mutation, call it at construction time or on the live user inside a UserStore.Update callback, never on a store-returned snapshot.

func (*User) VerifyPassword

func (u *User) VerifyPassword(raw []byte) bool

VerifyPassword returns true when the supplied raw bytes match the stored password. Uses constant-time comparison to avoid timing attacks.

type UserChannelAccess

type UserChannelAccess struct {
	// MaxPrivilege is the highest privilege the user may request on this channel.
	MaxPrivilege PrivilegeLevel
	// CallbackOnly restricts the user to callback sessions only.
	CallbackOnly bool
	// Enabled controls whether the user is allowed on this channel at all.
	Enabled bool
	// LinkAuth records the link-authentication enable bit (spec v2.0§22.26).
	// Nothing enforces it on a LAN channel; it is stored so Get User Access
	// round-trips what Set User Access accepted.
	LinkAuth bool
}

UserChannelAccess records per-channel privilege settings for a user.

type UserPayloadAccess added in v0.9.1

type UserPayloadAccess struct {
	// Standard1 mirrors "Standard Payload enables 1": bit [1] = SOL.
	Standard1 uint8
	// OEM1 mirrors "OEM Payload Enables 1".
	OEM1 uint8
}

UserPayloadAccess records a user's payload activation rights on one channel, mirroring the bitfields of spec v2.0 Table 24-8/24-9.

func (UserPayloadAccess) SOLEnabled added in v0.9.1

func (a UserPayloadAccess) SOLEnabled() bool

SOLEnabled reports whether the user may activate the SOL payload.

type UserStore

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

UserStore is a thread-safe registry of BMC users.

func NewUserStore

func NewUserStore(opts ...UserStoreOption) *UserStore

NewUserStore creates a UserStore with the mandatory anonymous user (ID 1).

func (*UserStore) Add

func (s *UserStore) Add(id uint8, name string) (*User, error)

Add creates a new user at the given ID and returns the live *User for construction-time seeding. Mutating the returned pointer is only safe before the server starts serving; use UserStore.Update for runtime changes. Returns ErrInvalidUserID for IDs outside the store's advertised range, or ErrUsernameTaken if name is non-empty and already in use. Bounding by the advertised maximum keeps the whole store consistent: no slot can exist that enumerators cannot see but authentication still finds.

func (*UserStore) Count

func (s *UserStore) Count() int

Count returns the number of configured users.

func (*UserStore) CountEnabled added in v0.9.1

func (s *UserStore) CountEnabled() int

CountEnabled returns the number of enabled users, in one pass under one read lock. Get User Access reports this on every query, and counting through per-slot snapshot lookups would deep-copy the whole table each time.

func (*UserStore) Delete

func (s *UserStore) Delete(id uint8) error

Delete removes a user by ID. User 1 (anonymous) cannot be deleted.

func (*UserStore) FindEnabledByNameOnChannel

func (s *UserStore) FindEnabledByNameOnChannel(name string, channel uint8) (*User, error)

FindEnabledByNameOnChannel scans user IDs in order up to the store maximum and returns a snapshot copy of the first enabled user with a matching name and channel access (spec v1.5§18.24 / v2.0§22.27).

func (*UserStore) Get

func (s *UserStore) Get(id uint8) (*User, error)

Get returns a snapshot copy of the user at the given ID, or ErrUserNotFound. The returned *User is a private copy; mutate the store via UserStore.Update.

func (*UserStore) GetByName

func (s *UserStore) GetByName(name string) (*User, error)

GetByName returns a snapshot copy of the user with the given name, or ErrUserNotFound. An empty name matches the anonymous user (ID 1).

Slots are scanned in ID order, so the lookup is deterministic even when several slots share a name: user-management commands can create additional empty-named slots at runtime, and iterating the map directly would make the RAKP null-user lookup resolve to a random one of them.

func (*UserStore) MaxUserCount added in v0.9.1

func (s *UserStore) MaxUserCount() uint8

MaxUserCount returns the highest user ID the store advertises (default MaxUsers). Get User Access reports this so in-band enumerators know how many slots to walk.

func (*UserStore) Update added in v0.9.1

func (s *UserStore) Update(id uint8, fn func(*User) error) error

Update runs fn against the live *User for id under the store write lock, the race-free way to mutate a user at runtime (e.g. from a Set User Password handler). Returns ErrUserNotFound if id is not present.

fn runs with the store lock held: it must not call back into the store (that self-deadlocks on the non-reentrant lock) and must not retain the *User past its return, since using the live pointer outside the lock recreates the race the snapshot lookups exist to prevent.

func (*UserStore) Upsert added in v0.9.1

func (s *UserStore) Upsert(id uint8, fn func(*User) error) error

Upsert applies fn to the user in slot id under a single write-lock hold, creating the slot first (respecting the store max) when it does not exist. This is the atomic create-or-mutate path handlers use so two concurrent creates on the same empty slot cannot interleave and lose a field, which a separate Add-then-Update sequence would allow.

fn runs against a working copy, so a rejected mutation leaves stored state untouched: the slot is committed only when fn returns nil and the resulting non-empty name does not collide with a different slot. A colliding name is rejected with ErrUsernameTaken to keep name-based session lookup deterministic. Returns ErrInvalidUserID for an id outside 1..max.

type UserStoreOption added in v0.9.1

type UserStoreOption func(*UserStore)

UserStoreOption configures a UserStore at construction time.

func WithMaxUsers added in v0.9.1

func WithMaxUsers(n uint8) UserStoreOption

WithMaxUsers sets the highest user ID the store advertises via Get User Access. n is clamped to the spec range 1..MaxUsers; the default is MaxUsers (63).

type V15AuthType

type V15AuthType uint8

V15AuthType mirrors IPMI v1.5 authentication type codes.

const (
	V15AuthTypeNone     V15AuthType = 0x00
	V15AuthTypeMD2      V15AuthType = 0x01
	V15AuthTypeMD5      V15AuthType = 0x02
	V15AuthTypePassword V15AuthType = 0x04
	V15AuthTypeOEM      V15AuthType = 0x05
)

func ParseV15AuthType

func ParseV15AuthType(name string) (V15AuthType, error)

ParseV15AuthType parses a single auth type name (case-insensitive).

func ParseV15AuthTypes

func ParseV15AuthTypes(raw string) ([]V15AuthType, error)

ParseV15AuthTypes parses a comma-separated list of v1.5 auth type names.

type V15Session

type V15Session struct {
	// ProcMu serializes per-session packet processing. The server spawns a
	// goroutine per inbound packet; without this lock, concurrent packets of
	// one session race on the sequence window check-then-set and the counters.
	ProcMu sync.Mutex

	TempSessionID uint32
	SessionID     uint32
	// Handle is the one-byte session handle Get Session Info reports
	// (spec v2.0§22.20). Assigned at creation, unique among the store's live
	// sessions, never 0x00 ("no session") or the reserved 0xFF.
	Handle uint8
	State  V15SessionState

	AuthType  V15AuthType
	Challenge [16]byte

	InboundSeq  uint32
	InboundRcvd uint8 // bitmap: bit i => (InboundSeq - i) received
	OutboundSeq uint32

	User           *User
	PrivilegeLevel PrivilegeLevel
	MaxPrivilege   PrivilegeLevel

	Channel uint8

	CreatedAt    time.Time
	LastActivity time.Time
}

V15Session holds IPMI v1.5 session state.

Concurrency: it follows the same pattern as the v2.0 Session. ProcMu serializes per-session packet processing and guards the per-packet fields written during dispatch: the sequence counters, InboundRcvd, and PrivilegeLevel. SessionID, State, and MaxPrivilege are written after publication only by V15SessionStore.Activate, which runs under the store lock while its caller holds ProcMu, so a reader under either lock observes a consistent value (the Count* helpers read them under the store lock). LastActivity is guarded by the store lock and refreshed via V15SessionStore.Touch. TempSessionID, Handle, AuthType, Challenge, Channel, User, and CreatedAt are set before the session is published and never written again. ProcMu may be held while taking the store lock, never the reverse.

func (*V15Session) NextOutboundSeq

func (sess *V15Session) NextOutboundSeq() uint32

NextOutboundSeq returns the sequence number for the current outbound message and advances the counter for the next one. Sequence 0 is reserved for pre-session packets and is skipped on wrap (v1.5§6.11.9 / v2.0§6.12.9).

func (*V15Session) TryAcceptInboundSeq

func (sess *V15Session) TryAcceptInboundSeq(seq uint32) bool

TryAcceptInboundSeq implements spec v1.5§6.11.11 Option 1 / v2.0§6.12.11 Option 1 (+/-8 window, no dupes).

type V15SessionState

type V15SessionState uint8

V15SessionState tracks IPMI v1.5 session negotiation progress.

const (
	V15SessionStatePending V15SessionState = iota
	V15SessionStateActive
	V15SessionStateClosed
)

type V15SessionStore

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

V15SessionStore is a thread-safe registry of IPMI v1.5 sessions.

func NewV15SessionStore

func NewV15SessionStore(clk clock.Clock) *V15SessionStore

NewV15SessionStore creates a V15SessionStore with the default limits.

func (*V15SessionStore) Activate

func (s *V15SessionStore) Activate(pending *V15Session, permanentID, inboundSeq, outboundSeq uint32, maxPrivilege PrivilegeLevel) error

Activate transitions a pending session to active with a new permanent ID. maxPrivilege is the requested ceiling; initial privilege is USER per v1.5§6.8 / v2.0§6.8 (Callback when max is Callback).

inboundSeq is the Activate Session response "Session inbound sequence number" (spec v1.5§18.15 / v2.0§6.12.9): the starting sequence the remote console must use on its first authenticated packet. InboundSeq on the session tracks the highest sequence already accepted, so it is seeded to inboundSeq-1 (wrapping) with an empty receive bitmap — otherwise the first packet (seq == inboundSeq) is rejected as a duplicate and clients such as ipmitool stall for a full LAN timeout before retrying with inboundSeq+1.

Precondition: the caller must hold pending's ProcMu. Activate mutates fields of an already-published session (SessionID, State, MaxPrivilege, PrivilegeLevel, the seq counters, and LastActivity) under the store lock; per-packet readers hold ProcMu and the store's Count* and eviction scans hold the store lock, so with the writer holding both, a reader under either lock observes a consistent session.

A session evicted between lookup and activation is reported as not pending rather than silently re-inserted.

func (*V15SessionStore) Cap added in v0.9.1

func (s *V15SessionStore) Cap() int

Cap returns the maximum number of concurrent v1.5 sessions the store can hold, i.e. the number of slots in the session table.

func (*V15SessionStore) Close

func (s *V15SessionStore) Close(id uint32) error

Close removes a session by permanent or temp ID.

func (*V15SessionStore) Count added in v0.9.1

func (s *V15SessionStore) Count() int

Count returns the number of sessions currently in the store, pending or active, mirroring SessionStore.Count.

func (*V15SessionStore) CountActiveSessions

func (s *V15SessionStore) CountActiveSessions() int

CountActiveSessions returns the number of active v1.5 sessions.

func (*V15SessionStore) CountActiveSessionsForUser

func (s *V15SessionStore) CountActiveSessionsForUser(userID uint8) int

CountActiveSessionsForUser returns active sessions owned by userID.

func (*V15SessionStore) CountActiveSessionsWithMaxPrivilegeAtLeast

func (s *V15SessionStore) CountActiveSessionsWithMaxPrivilegeAtLeast(min PrivilegeLevel) int

CountActiveSessionsWithMaxPrivilegeAtLeast counts active sessions whose negotiated maximum privilege is >= min (for Table 18-17 completion 0x83).

func (*V15SessionStore) CreatePending

func (s *V15SessionStore) CreatePending(authType V15AuthType, user *User, challenge [16]byte, channel uint8) (*V15Session, error)

CreatePending allocates a pending v1.5 session after Get Session Challenge. The session is fully initialized before it is inserted into the map, so no unguarded field write happens after it becomes reachable to other goroutines.

func (*V15SessionStore) EvictExpired

func (s *V15SessionStore) EvictExpired() int

EvictExpired removes inactive v1.5 sessions past the timeout. Eviction reads LastActivity and deletes under the store lock only; it never takes a session's ProcMu, so a handler holding one can trigger eviction safely.

func (*V15SessionStore) Get

func (s *V15SessionStore) Get(id uint32) (*V15Session, error)

Get returns a session by its current lookup ID. It is a pure lookup: activity is refreshed separately via V15SessionStore.Touch, only once a packet has passed authentication and sequence validation. Refreshing on lookup would let packets that fail validation, or that merely name a session ID, keep the session alive forever.

func (*V15SessionStore) Touch

func (s *V15SessionStore) Touch(id uint32)

Touch refreshes the session's inactivity clock. The server calls it for every packet that passed authentication and sequence validation.

Jump to

Keyboard shortcuts

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