forked from google/gopacket
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathasf.go
166 lines (140 loc) · 5.3 KB
/
asf.go
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
// 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/kubeshark/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)
}