// Copyright 2019 The GoPacket Authors. All rights reserved. // // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file in the root of the source tree. package layers // This file implements the ASF RMCP payload specified in section 3.2.2.3 of // https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf import ( "encoding/binary" "fmt" "github.com/google/gopacket" ) const ( // ASFRMCPEnterprise is the IANA-assigned Enterprise Number of the ASF-RMCP. ASFRMCPEnterprise uint32 = 4542 ) // ASFDataIdentifier encapsulates fields used to uniquely identify the format of // the data block. // // While the enterprise number is almost always 4542 (ASF-RMCP), we support // registering layers using structs of this type as a key in case any users are // using OEM-extensions. type ASFDataIdentifier struct { // Enterprise is the IANA Enterprise Number associated with the entity that // defines the message type. A list can be found at // https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers. // This can be thought of as the namespace for the message type. Enterprise uint32 // Type is the message type, defined by the entity associated with the // enterprise above. No pressure, but in the context of EN 4542, 1 byte is // the difference between sending a ping and telling a machine to do an // unconditional power down (0x80 and 0x12 respectively). Type uint8 } // LayerType returns the payload layer type corresponding to an ASF message // type. func (a ASFDataIdentifier) LayerType() gopacket.LayerType { if lt := asfDataLayerTypes[a]; lt != 0 { return lt } // some layer types don't have a payload, e.g. ASF-RMCP Presence Ping. return gopacket.LayerTypePayload } // RegisterASFLayerType allows specifying that the data block of ASF packets // with a given enterprise number and type should be processed by a given layer // type. This overrides any existing registrations, including defaults. func RegisterASFLayerType(a ASFDataIdentifier, l gopacket.LayerType) { asfDataLayerTypes[a] = l } var ( // ASFDataIdentifierPresencePong is the message type of the response to a // Presence Ping message. It indicates the sender is ASF-RMCP-aware. ASFDataIdentifierPresencePong = ASFDataIdentifier{ Enterprise: ASFRMCPEnterprise, Type: 0x40, } // ASFDataIdentifierPresencePing is a message type sent to a managed client // to solicit a Presence Pong response. Clients may ignore this if the RMCP // version is unsupported. Sending this message with a sequence number <255 // is the recommended way of finding out whether an implementation sends // RMCP ACKs (e.g. iDRAC does, Super Micro does not). // // Systems implementing IPMI must respond to this ping to conform to the // spec, so it is a good substitute for an ICMP ping. ASFDataIdentifierPresencePing = ASFDataIdentifier{ Enterprise: ASFRMCPEnterprise, Type: 0x80, } // asfDataLayerTypes is used to find the next layer for a given ASF header. asfDataLayerTypes = map[ASFDataIdentifier]gopacket.LayerType{ ASFDataIdentifierPresencePong: LayerTypeASFPresencePong, } ) // ASF defines ASF's generic RMCP message Data block format. See section // 3.2.2.3. type ASF struct { BaseLayer ASFDataIdentifier // Tag is used to match request/response pairs. The tag of a response is set // to that of the message it is responding to. If a message is // unidirectional, i.e. not part of a request/response pair, this is set to // 255. Tag uint8 // 1 byte reserved, set to 0x00. // Length is the length of this layer's payload in bytes. Length uint8 } // LayerType returns LayerTypeASF. It partially satisfies Layer and // SerializableLayer. func (*ASF) LayerType() gopacket.LayerType { return LayerTypeASF } // CanDecode returns LayerTypeASF. It partially satisfies DecodingLayer. func (a *ASF) CanDecode() gopacket.LayerClass { return a.LayerType() } // DecodeFromBytes makes the layer represent the provided bytes. It partially // satisfies DecodingLayer. func (a *ASF) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { if len(data) < 8 { df.SetTruncated() return fmt.Errorf("invalid ASF data header, length %v less than 8", len(data)) } a.BaseLayer.Contents = data[:8] a.BaseLayer.Payload = data[8:] a.Enterprise = binary.BigEndian.Uint32(data[:4]) a.Type = uint8(data[4]) a.Tag = uint8(data[5]) // 1 byte reserved a.Length = uint8(data[7]) return nil } // NextLayerType returns the layer type corresponding to the message type of // this ASF data layer. This partially satisfies DecodingLayer. func (a *ASF) NextLayerType() gopacket.LayerType { return a.ASFDataIdentifier.LayerType() } // SerializeTo writes the serialized fom of this layer into the SerializeBuffer, // partially satisfying SerializableLayer. func (a *ASF) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { payload := b.Bytes() bytes, err := b.PrependBytes(8) if err != nil { return err } binary.BigEndian.PutUint32(bytes[:4], a.Enterprise) bytes[4] = uint8(a.Type) bytes[5] = a.Tag bytes[6] = 0x00 if opts.FixLengths { a.Length = uint8(len(payload)) } bytes[7] = a.Length return nil } // decodeASF decodes the byte slice into an RMCP-ASF data struct. func decodeASF(data []byte, p gopacket.PacketBuilder) error { return decodingLayerDecoder(&ASF{}, data, p) }