1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
// 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 header specified in section 3.2.2.2 of
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
import (
"fmt"
"github.com/gopacket/gopacket"
)
// RMCPClass is the class of a RMCP layer's payload, e.g. ASF or IPMI. This is a
// 4-bit unsigned int on the wire; all but 6 (ASF), 7 (IPMI) and 8 (OEM-defined)
// are currently reserved.
type RMCPClass uint8
// LayerType returns the payload layer type corresponding to a RMCP class.
func (c RMCPClass) LayerType() gopacket.LayerType {
if lt := rmcpClassLayerTypes[uint8(c)]; lt != 0 {
return lt
}
return gopacket.LayerTypePayload
}
func (c RMCPClass) String() string {
return fmt.Sprintf("%v(%v)", uint8(c), c.LayerType())
}
const (
// RMCPVersion1 identifies RMCP v1.0 in the Version header field. Lower
// values are considered legacy, while higher values are reserved by the
// specification.
RMCPVersion1 uint8 = 0x06
// RMCPNormal indicates a "normal" message, i.e. not an acknowledgement.
RMCPNormal uint8 = 0
// RMCPAck indicates a message is acknowledging a received normal message.
RMCPAck uint8 = 1 << 7
// RMCPClassASF identifies an RMCP message as containing an ASF-RMCP
// payload.
RMCPClassASF RMCPClass = 0x06
// RMCPClassIPMI identifies an RMCP message as containing an IPMI payload.
RMCPClassIPMI RMCPClass = 0x07
// RMCPClassOEM identifies an RMCP message as containing an OEM-defined
// payload.
RMCPClassOEM RMCPClass = 0x08
)
var (
rmcpClassLayerTypes = [16]gopacket.LayerType{
RMCPClassASF: LayerTypeASF,
// RMCPClassIPMI is to implement; RMCPClassOEM is deliberately not
// implemented, so we return LayerTypePayload
}
)
// RegisterRMCPLayerType allows specifying that the payload of a RMCP packet of
// a certain class should processed by the provided layer type. This overrides
// any existing registrations, including defaults.
func RegisterRMCPLayerType(c RMCPClass, l gopacket.LayerType) {
rmcpClassLayerTypes[c] = l
}
// RMCP describes the format of an RMCP header, which forms a UDP payload. See
// section 3.2.2.2.
type RMCP struct {
BaseLayer
// Version identifies the version of the RMCP header. 0x06 indicates RMCP
// v1.0; lower values are legacy, higher values are reserved.
Version uint8
// Sequence is the sequence number assicated with the message. Note that
// this rolls over to 0 after 254, not 255. Seq num 255 indicates the
// receiver must not send an ACK.
Sequence uint8
// Ack indicates whether this packet is an acknowledgement. If it is, the
// payload will be empty.
Ack bool
// Class idicates the structure of the payload. There are only 2^4 valid
// values, however there is no uint4 data type. N.B. the Ack bit has been
// split off into another field. The most significant 4 bits of this field
// will always be 0.
Class RMCPClass
}
// LayerType returns LayerTypeRMCP. It partially satisfies Layer and
// SerializableLayer.
func (*RMCP) LayerType() gopacket.LayerType {
return LayerTypeRMCP
}
// CanDecode returns LayerTypeRMCP. It partially satisfies DecodingLayer.
func (r *RMCP) CanDecode() gopacket.LayerClass {
return r.LayerType()
}
// DecodeFromBytes makes the layer represent the provided bytes. It partially
// satisfies DecodingLayer.
func (r *RMCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("invalid RMCP header, length %v less than 4",
len(data))
}
r.BaseLayer.Contents = data[:4]
r.BaseLayer.Payload = data[4:]
r.Version = uint8(data[0])
// 1 byte reserved
r.Sequence = uint8(data[2])
r.Ack = data[3]&RMCPAck != 0
r.Class = RMCPClass(data[3] & 0xF)
return nil
}
// NextLayerType returns the data layer of this RMCP layer. This partially
// satisfies DecodingLayer.
func (r *RMCP) NextLayerType() gopacket.LayerType {
return r.Class.LayerType()
}
// Payload returns the data layer. It partially satisfies ApplicationLayer.
func (r *RMCP) Payload() []byte {
return r.BaseLayer.Payload
}
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
// partially satisfying SerializableLayer.
func (r *RMCP) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
// The IPMI v1.5 spec contains a pad byte for frame sizes of certain lengths
// to work around issues in LAN chips. This is no longer necessary as of
// IPMI v2.0 (renamed to "legacy pad") so we do not attempt to add it. The
// same approach is taken by FreeIPMI:
// http://git.savannah.gnu.org/cgit/freeipmi.git/tree/libfreeipmi/interface/ipmi-lan-interface.c?id=b5ffcd38317daf42074458879f4c55ba6804a595#n836
bytes, err := b.PrependBytes(4)
if err != nil {
return err
}
bytes[0] = r.Version
bytes[1] = 0x00
bytes[2] = r.Sequence
bytes[3] = bool2uint8(r.Ack)<<7 | uint8(r.Class) // thanks, BFD layer
return nil
}
// decodeRMCP decodes the byte slice into an RMCP type, and sets the application
// layer to it.
func decodeRMCP(data []byte, p gopacket.PacketBuilder) error {
rmcp := &RMCP{}
err := rmcp.DecodeFromBytes(data, p)
p.AddLayer(rmcp)
p.SetApplicationLayer(rmcp)
if err != nil {
return err
}
return p.NextDecoder(rmcp.NextLayerType())
}
|