bmc

package module
v0.0.0-...-9d76940 Latest Latest
Warning

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

Go to latest
Published: Jan 30, 2023 License: LGPL-3.0 Imports: 22 Imported by: 0

README

Baseboard Management Controller Remote Console

Build Status GoDoc Go Report Card

This project implements an IPMI v2.0 remote console in pure Go, to interact with BMCs.

Specifications

All section references in the code use the following documents:

Contributing

Contributions in the form of bug reports and PRs are greatly appreciated. Please see CONTRIBUTING.md for a few guidelines.

Documentation

Overview

Package bmc implements an IPMI v1.5/2.0 remote console. pkg/ipmi provides the layers; this package makes IPMI work in Go.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrSensorReadingUnavailable is returned when the BMC sets the
	// "reading/state unavailable" flag in the Get Sensor Reading response. This
	// indicates the reading should be ignored, so is returned as an error.
	ErrSensorReadingUnavailable = errors.New("sensor reading is not available")

	// ErrSensorScanningDisabled is returned when the BMC sets the "sensor
	// scanning disabled" flag in the Get Sensor Reading response. This suggests
	// the machine is powered down and the reading should be ignored, so is
	// returned as an error.
	ErrSensorScanningDisabled = errors.New("sensor is disabled")
)
View Source
var (
	ErrIncorrectPassword = errors.New("RAKP2 HMAC fail (this indicates the " +
		"BMC is using a different password)")
)

Functions

func FirmwareVersion

func FirmwareVersion(r *ipmi.GetDeviceIDRsp) string

FirmwareVersion builds a firmware version string using all information available in the Get Device ID response. Many manufacturers use the 4 auxiliary firmware revision bytes to add additional components to the version, e.g. "3.15.17.15" in the case of Dell. Using the major and minor firmware version fields alone would only yield 3.15. This function aims to capture as much relevant data as possible, falling back on "major.minor". Where possible, it returns data in the same format (e.g. 0 padding) as in the BMC web interface.

func ValidateResponse

func ValidateResponse(c ipmi.CompletionCode, err error) error

ValidateResponse is a helper to remove some boilerplate error handling from SendCommand() calls. It ensures a non-nil error and normal completion code. If the error is non-nil, it is returned. If the completion code is non-normal, an error is returned containing the actual value.

Types

type AdditionalKeyMaterialGenerator

type AdditionalKeyMaterialGenerator interface {

	// K computes the value of K_N for a given value of N, using the negotiated
	// authentication algorithm (used during session establishment) loaded with
	// the SIK. N is only defined for values 1 through 255. This method is not
	// used by the library itself, and is assumed to be only for
	// informational/debugging purposes, so we make no attempt to memoise
	// results. This function is not safe for concurrent use by multiple
	// goroutines.
	K(n int) []byte
}

AdditionalKeyMaterialGenerator is satisfied by types that can produce key material derived from the Session Integrity Key, as defined in section 13.32 of IPMI v2.0. This additional key material is referred to as K_N. In practice, only K_1 and K_2 are used, for packet authentication and confidentiality respectively

type Connection

type Connection interface {

	// SendCommand sends a command to the BMC, blocking until it receives a
	// response. This method will retry with the configured per-request timeout
	// until a valid response with a non-temporary error (e.g. resource
	// exhaustion) is received, or the context expires (whichever happens
	// first). If the final request fails with a transport error (including
	// timeout), a serialise/decode error occurs above the command response
	// layer, or the message layer is missing, the returned error will be
	// non-nil, and the completion code must be ignored. If the message layer of
	// the response was decoded successfully, the code will be set to that,
	// however the error can still be non-nil if the command expects a response
	// and that failed to decode correctly.
	//
	// This method uses the response layer (if any) included in the command
	// interface for decoding the response. The caller should first check the
	// error, then the completion code, then assuming both indicate no error,
	// read the response layer if required. The ValidateResponse() function can
	// be used for the sake of brevity.
	//
	// This method does not allocate any memory for layers, so is ideal in
	// situations where you intend to send the same command repeatedly, e.g. a
	// Prometheus exporter. If you don't need this performance, for the sake of
	// one more allocation per command, it is recommended to use the
	// higher-level API, e.g. GetSystemGUID(), which wraps this.
	SendCommand(ctx context.Context, cmd ipmi.Command) (ipmi.CompletionCode, error)

	// Version returns the underlying IPMI version of the connection, either
	// "1.5" or "2.0". Note that even session-less connections use a session
	// wrapper, which has either the v1.5 or v2.0 format. This is provided for
	// informational and debugging purposes - branching based on this value is a
	// code smell.
	Version() string
}

Connection is an IPMI v1.5 or v2.0 session-less, single-session or multi-session connection. The IPMI version and nature of the connection is fixed upon creation - if sending two messages, it will never be the case that one uses one wrapper format and the second another. It defines logical things that can be done once communication is established with a BMC. Note that this is *not* a transport in itself - hence why there is no Close() - but it abstracts over a transport to provide its functionality. This interface is always wrapped in something else that has a Close() to cleanly terminate the underlying connection.

type SDRRepository

type SDRRepository map[ipmi.RecordID]*ipmi.FullSensorRecord

SDRRepository is a retrieved SDR Repository. For the time being, this is a collection of Full Sensor Records, indexed by record ID. Note that because this is a map, iteration order is randomised and almost definitely not the same as retrieval order, which has no guarantees anyway.

func RetrieveSDRRepository

func RetrieveSDRRepository(ctx context.Context, s Session) (SDRRepository, error)

RetrieveSDRRepository enumerates all Full Sensor Records in the BMC's SDR Repository. This method will back-off if an error occurs, or it detects a change mid-way through iteration, which would invalidate records retrieved so far. The session-configured timeout is used for individual commands.

type SensorReader

type SensorReader interface {

	// Read returns the current value of the sensor with any conversion factors
	// and linearisation applied. It will return ErrSensorReadingUnavailable if
	// the BMC indicates the sensor is not yet ready.
	Read(context.Context, Session) (float64, error)
}

SensorReader is implemented by types that can read the current value of a sensor. This abstracts over linear, linearised and non-linear sensors, issuing the relevant commands behind the scenes. It is rather high-level, however it is difficult to, say, implement only conversion given a raw value, as this sometimes requires conversion factors - given we would need a session there, we may as well retrieve the current reading in the first place.

func NewSensorReader

func NewSensorReader(r *ipmi.FullSensorRecord) (SensorReader, error)

NewSensorReader returns an appropriate SensorReader implementation for a given SDR.

type Session

type Session interface {
	Connection
	SessionCommands

	// ID returns our identifier for this session. Note the managed system and
	// remote console share the same identifier in IPMI v1.5, however each
	// chooses its own identifier in v2.0, so they likely differ.
	ID() uint32

	// Close closes the session by sending a Close Session command to the BMC.
	// As the underlying transport/socket is used but not managed by
	// connections, it is left open in case the user wants to continue issuing
	// session-less commands or establish a new session. It is envisaged that
	// this call is deferred immediately after successful session establishment.
	// If an error is returned, the session can be assumed to be closed.
	Close(context.Context) error
}

Session is an established session-based IPMI v1.5 or 2.0 connection. More specifically, it is a multi-session connection, as the spec demands this of the LAN interface. Commands are sent in the context of the session, using the negotiated integrity and confidentiality algorithms.

type SessionCommands

type SessionCommands interface {

	// SessionlessCommands enables all session-less commands to also be sent
	// inside a session; indeed it is convention for Get Channel Authentication
	// Capabilities to be used as a keepalive.
	SessionlessCommands

	// GetSessionInfo sends a Get Session Info command to the BMC. This is
	// specified in 18.18 and 22.20 of IPMI v1.5 and v2.0 respectively.
	GetSessionInfo(context.Context, *ipmi.GetSessionInfoReq) (*ipmi.GetSessionInfoRsp, error)

	// GetDeviceID sends a Get Device ID command to the BMC. This is specified
	// in 17.1 and 20.1 of IPMI v1.5 and 2.0 respectively.
	GetDeviceID(context.Context) (*ipmi.GetDeviceIDRsp, error)

	// GetChassisStatus sends a Get Chassis Status command to the BMC. This is
	// specified in 22.2 and 28.2 of IPMI v1.5 and 2.0 respectively.
	GetChassisStatus(context.Context) (*ipmi.GetChassisStatusRsp, error)

	// ChassisControl provides power up, power down and reset control. It is
	// specified in 22.3 and 28.3 of IPMI v1.5 and 2.0 respectively.
	ChassisControl(context.Context, ipmi.ChassisControl) error

	// GetSDRRepositoryInfo obtains information about the BMC's Sensor Data
	// Record Repository. It is specified in 27.9 and 33.9 of IPMI v1.5 and 2.0
	// respectively.
	GetSDRRepositoryInfo(context.Context) (*ipmi.GetSDRRepositoryInfoRsp, error)

	// GetSensorReading retrieves the current value of a sensor, identified by
	// its number. It is specified in 29.14 and 35.14 of IPMI v1.5 and 2.0
	// respectively. Note, the raw value is in one of three formats, and is
	// converted into a "real" reading via one or more formulae - interpreting
	// it requires the SDR.
	GetSensorReading(context.Context, uint8) (*ipmi.GetSensorReadingRsp, error)
	// contains filtered or unexported methods
}

SessionCommands contains high-level wrappers for sending commands within an established session. These commands are common to all versions of IPMI.

type SessionOpts

type SessionOpts struct {

	// Username is the username of the user to connect as. Only ASCII characters
	// (excluding \0) are allowed, and it cannot be more than 16 characters
	// long.
	Username string

	// Password is the password of the above user, stored on the managed system
	// as either 16 bytes (for v1.5, or to preserve the ability to log in with a
	// v1.5 session in v2.0) or 20 bytes of uninterpreted data (hence why this
	// isn't a string, and "special characters" aren't usually allowed).
	// Passwords shorter than the maximum length are padded with 0x00. This is
	// called K_[UID] in the spec ("the key for the user with ID 'UID'"). Some
	// BMCs have tighter constraints, e.g. Super Micro supports up to 19 chars.
	Password []byte

	// MaxPrivilegeLevel is the upper privilege limit for the session. It
	// defaults to ipmi.PrivilegeLevelHighest for IPMI v2.0, and
	// ipmi.PrivilegeLevelUser for IPMI v1.5, which does not have that value,
	// however the channel or user privilege level limit may further constrain
	// allowed commands for either version.
	//
	// Due to the deliberate inconsistency between IPMI versions, it is always
	// recommended to explicitly set this. Unfortunately it was not possible to
	// default to User for v2.0 as, because Highest has value 0, we cannot
	// distinguish between it being implicitly and explicitly set, so we don't
	// know when to intervene.
	MaxPrivilegeLevel ipmi.PrivilegeLevel
}

SessionOpts contains session-establishment options common to IPMI v1.5 and 2.0. A value of this type is required to establish a version-agnostic session.

type Sessionless

type Sessionless interface {
	Connection
	SessionlessCommands
}

Sessionless is a session-less IPMI v1.5 or 2.0 connection. It enables sending commands to a BMC outside the context of a session (however note that all such commands can also be validly sent inside a session, for example Get Channel Authentication Capabilities is commonly used as a form of keepalive). Creating a concrete session-less connection will require a transport in order to send bytes.

type SessionlessCommands

type SessionlessCommands interface {

	// GetSystemGUID issues the Get System GUID command to the BMC, specified in
	// section 18.13 of IPMI v1.5 and 22.14 of v2.0. You may wish to use a
	// library like google/uuid to manipulate and display the GUID.
	GetSystemGUID(context.Context) ([16]byte, error)

	// GetChannelAuthenticationCapabilities issues the Get Channel
	// Authentication Capabilities command to the BMC, specified in 18.12 of
	// IPMI v1.5 and 22.13 of v2.0. This is mainly useful for this library when
	// deciding how to connect to a BMC (e.g. whether to use v1.5 or v2.0) and
	// as a keepalive, however could be useful to scan an estate for
	// compatibility.
	GetChannelAuthenticationCapabilities(context.Context, *ipmi.GetChannelAuthenticationCapabilitiesReq) (*ipmi.GetChannelAuthenticationCapabilitiesRsp, error)
}

SessionlessCommands contains high-level methods to issue commands that do not require a session, but can be sent using one. This is IPMI version agnostic, so RMCP+ session setup payload types do not appear, only commands that would come under the IPMI message standard payload type.

type SessionlessTransport

type SessionlessTransport interface {

	// Transport is the underlying socket, used to send and receive arbitrary
	// bytes, and get the address of the BMC. The Close() method of this
	// interface closes the transport, not the sessionless-connection (which
	// does not require closing).
	transport.Transport

	// Sessionless is the IPMI connection to the BMC, allowing the user to send
	// things at a higher level of abstraction than the transport alone
	// provides.
	Sessionless

	// NewSession opens a new session to the BMC using the underlying wrapper
	// format. NewSession uses the sessionless methods for establishment.
	NewSession(ctx context.Context, opts *SessionOpts) (Session, error)
}

SessionlessTransport represents a session-less IPMI v1.5 or v2.0 LAN connection, its underlying transport, and a means of creating a new session using that transport. The IPMI version is fixed at creation time by the session-less connection; to obtain the version, call Version(). This is the type returned by Dial().

func Dial

Dial is currently an alias for DialV2. When IPMI v1.5 is implemented, this will query the BMC for IPMI v2.0 capability. If it supports IPMI v2.0, a V2SessionlessTransport will be returned, otherwise a V1SessionlessTransport will be returned. If you know the BMC's capabilities, or need a specific feature (e.g. DCMI), use the DialV*() functions instead, which expose additional information and functionality.

type V2Session

type V2Session struct {

	// LocalID is the remote console's session ID, used by the BMC to send us
	// packets.
	LocalID uint32

	// RemoteID is the managed system's session ID, used by us to send the BMC
	// packets.
	RemoteID uint32

	// AuthenticatedSequenceNumbers is the pair of sequence numbers for
	// authenticated packets.
	AuthenticatedSequenceNumbers sequenceNumbers

	// UnauthenticatedSequenceNumbers is the pair of sequence numbers for
	// unauthenticated packets, i.e. those without an auth code. We only send
	// unauthenticated packets to the BMC if IntegrityAlgorithmNone was
	// negotiated.
	UnauthenticatedSequenceNumbers sequenceNumbers

	// SIK is the session integrity key, whose creation is described in section
	// 13.31 of the specs. It is the result of applying the negotiated
	// authentication algorithm (which is usually, but may not be, an HMAC) to
	// some random numbers, the remote console's requested maximum privilege
	// level, and username. The SIK is then used to derive K_1 and K_2 (and
	// possibly more, but not for any algorithms in the spec) which are the keys
	// for the integrity algorithm and confidentiality algorithms respectively.
	SIK []byte

	// AuthenticationAlgorithm is the algorithm used to authenticate the user
	// during establishment of the session. Given the session is already
	// established, this will not be used further.
	AuthenticationAlgorithm ipmi.AuthenticationAlgorithm

	// IntegrityAlgorithm is the algorithm used to sign, or authenticate packets
	// sent by the managed system and remote console. This library authenticates
	// all packets it sends inside a session, provided IntegrityAlgorithmNone
	// was not negotiated.
	IntegrityAlgorithm ipmi.IntegrityAlgorithm

	// ConfidentialityAlgorithm is the algorithm used to encrypt and decrypt
	// packets sent by the managed system and remote console. This library
	// encrypts all packets it sends inside a session, provided
	// ConfidentialityAlgorithmNone was not negotiated.
	ConfidentialityAlgorithm ipmi.ConfidentialityAlgorithm

	// AdditionalKeyMaterialGenerator is the instance of the authentication
	// algorithm used during session establishment, loaded with the session
	// integrity key. It has no further use as far as the BMC is concerned by
	// the time we have this struct, however we keep it around to allow
	// providing K_n for information purposes.
	AdditionalKeyMaterialGenerator
	// contains filtered or unexported fields
}

V2Session represents an established IPMI v2.0/RMCP+ session with a BMC.

func (*V2Session) ChassisControl

func (s *V2Session) ChassisControl(ctx context.Context, c ipmi.ChassisControl) error

func (*V2Session) Close

func (s *V2Session) Close(ctx context.Context) error

func (*V2Session) GetChassisStatus

func (s *V2Session) GetChassisStatus(ctx context.Context) (*ipmi.GetChassisStatusRsp, error)

func (*V2Session) GetDeviceID

func (s *V2Session) GetDeviceID(ctx context.Context) (*ipmi.GetDeviceIDRsp, error)

func (*V2Session) GetSDRRepositoryInfo

func (s *V2Session) GetSDRRepositoryInfo(ctx context.Context) (*ipmi.GetSDRRepositoryInfoRsp, error)

func (*V2Session) GetSensorReading

func (s *V2Session) GetSensorReading(ctx context.Context, sensor uint8) (*ipmi.GetSensorReadingRsp, error)

func (*V2Session) GetSessionInfo

func (s *V2Session) GetSessionInfo(ctx context.Context, r *ipmi.GetSessionInfoReq) (*ipmi.GetSessionInfoRsp, error)

func (*V2Session) GetSystemGUID

func (s *V2Session) GetSystemGUID(ctx context.Context) ([16]byte, error)

func (*V2Session) ID

func (s *V2Session) ID() uint32

func (*V2Session) SendCommand

func (s *V2Session) SendCommand(ctx context.Context, c ipmi.Command) (ipmi.CompletionCode, error)

func (*V2Session) String

func (s *V2Session) String() string

String returns a summary of the session's attributes on one line.

func (*V2Session) Version

func (s *V2Session) Version() string

type V2SessionOpts

type V2SessionOpts struct {
	SessionOpts

	// PrivilegeLevelLookup indicates whether to use both the MaxPrivilegeLevel
	// and Username to search for the relevant user entry. If this is true, as
	// both are used in the search, a user will a lower max privilege level than
	// MaxPrivilegeLevel would not be found. If this is true and the username is
	// empty, we effectively use role-based authentication.
	PrivilegeLevelLookup bool

	// KG is the key-generating key or "BMC key". It is almost always unset, as
	// it effectively adds a second password in addition to the user/role
	// password, which must be known a-priori to establish a session. It is a 20
	// byte value. If this field is unset, K_[UID], i.e. the user password, will
	// be used in its place (and it is recommended for all 20 bytes of that
	// password to be used to preserve the complexity).
	KG []byte

	// AuthenticationAlgorithms is a slice of authentication algorithms to
	// propose. If this is unspecified, all supported algorithms will be
	// proposed.
	AuthenticationAlgorithms []ipmi.AuthenticationAlgorithm

	// IntegrityAlgorithms is a slice of integrity algorithms to propose for
	// packet signing. If this is unspecified, all supported algorithms will be
	// proposed.
	IntegrityAlgorithms []ipmi.IntegrityAlgorithm

	// ConfidentialityAlgorithms is a slice of confidentiality algorithms to
	// propose for packet encryption. If this is unspecified, all supported
	// algorithms will be proposed.
	ConfidentialityAlgorithms []ipmi.ConfidentialityAlgorithm
}

V2SessionOpts contains configurable parameters for RMCP+ session establishment.

type V2Sessionless

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

V2Sessionless represents a session-less connection to a BMC using a "null" IPMI v2.0 session wrapper.

func (*V2Sessionless) GetSystemGUID

func (s *V2Sessionless) GetSystemGUID(ctx context.Context) ([16]byte, error)

func (*V2Sessionless) SendCommand

func (s *V2Sessionless) SendCommand(ctx context.Context, c ipmi.Command) (ipmi.CompletionCode, error)

func (*V2Sessionless) SetTimeout

func (s *V2Sessionless) SetTimeout(t time.Duration)

SetTimeout configures the per-request timeout for a given RMCP+ or IPMI command. Methods will retry temporary errors until the context expires; this configures how long we will wait for a response.

func (*V2Sessionless) Version

func (s *V2Sessionless) Version() string

type V2SessionlessTransport

type V2SessionlessTransport struct {
	transport.Transport
	*V2Sessionless
}

V2SessionlessTransport is a session-less connection to a BMC using an IPMI v2.0/RMCP+ session wrapper, along with its underlying transport. A pointer to this type is returned by DialV2().

func DialV2

func DialV2(addr string) (*V2SessionlessTransport, error)

DialV2 establishes a new IPMI v2.0 connection with the supplied BMC. The address is of the form IP[:port] (IPv6 must be enclosed in square brackets). Use this if you know the BMC supports IPMI v2.0 and/or require DCMI functionality. Note v4 is preferred to v6 if a hostname is passed returning both A and AAAA records.

func (*V2SessionlessTransport) Close

func (s *V2SessionlessTransport) Close() error

func (*V2SessionlessTransport) NewSession

func (s *V2SessionlessTransport) NewSession(
	ctx context.Context,
	opts *SessionOpts,
) (Session, error)

NewSession establishes a new RMCP+ session. Two-key login is assumed to be disabled (i.e. KG is null), and all algorithms supported by the library will be offered. This should cover the majority of use cases, and is recommended unless you know a-priori that a BMC key is set.

func (*V2SessionlessTransport) NewV2Session

func (s *V2SessionlessTransport) NewV2Session(ctx context.Context, opts *V2SessionOpts) (*V2Session, error)

NewV2Session establishes a new RMCP+ session with fine-grained parameters. This function does not modify the input options. The caller is responsible for knowing that v2.0 is supported.

Directories

Path Synopsis
cmd
internal
pkg/bcd
Package bcd decodes bytes containing values in Binary-coded Decimal format to their native representation.
Package bcd decodes bytes containing values in Binary-coded Decimal format to their native representation.
pkg/complement
Package complement implements conversion from one and two's complement numbers to their native representation.
Package complement implements conversion from one and two's complement numbers to their native representation.
pkg/transport
Package transport provides a context wrapper and error handling around a UDP connection.
Package transport provides a context wrapper and error handling around a UDP connection.
pkg
dcmi
Package dcmi implements the Data Center Manageability Interface specification.
Package dcmi implements the Data Center Manageability Interface specification.
iana
Package iana contains types for IANA number resources.
Package iana contains types for IANA number resources.
ipmi
Package ipmi implements the message and layer formats of IPMI v1.5 and v2.0.
Package ipmi implements the message and layer formats of IPMI v1.5 and v2.0.
layerexts
Package layerexts contains useful extensions to gopacket.
Package layerexts contains useful extensions to gopacket.

Jump to

Keyboard shortcuts

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