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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
package ipmi
import "context"
const (
IPMIRequesterSequenceMax uint8 = 0x3f // RequesterSequence only occupy 6 bits
)
// 13.8 IPMI LAN Message Format
type IPMIRequest struct {
// SlaveAddress or SoftwareID
// Responder's Slave Address. 1 byte. LS bit is 0 for Slave Addresses and 1 for Software IDs. Upper 7-bits hold Slave Address or Software ID, respectively. This byte is always 20h when the BMC is the responder.
ResponderAddr uint8 // SlaveAddress or SoftwareID
// The lower 2-bits of the netFn byte identify the logical unit number, which provides further sub-addressing within the target node.
NetFn NetFn // (even) / rsLUN
ResponderLUN uint8 // lower 2 bits
// Checksum1 is filled by calling ComputeChecksum method
Checksum1 uint8
// SlaveAddress or SoftwareID
// Requester's Address. 1 byte. LS bit is 0 for Slave Addresses and 1 for Software IDs. Upper 7-bits hold Slave Address or Software ID, respectively. This byte is always 20h when the BMC is the requester.
RequesterAddr uint8 // rqSA
RequesterSequence uint8 // rqSeq, occupies the highest 6 bits, (so should left shit 2 bits)
RequesterLUN uint8 // rqLUN, occupies the lowest 2 bits
Command uint8 // Command ID
// Command Request Body Data defined by each command.
CommandData []byte // optional, 0 or more
// Checksum2 is filled by calling ComputeChecksum method
Checksum2 uint8
}
// IPMIResponse represent IPMI PayloadType msg response
type IPMIResponse struct {
// Requester's Address. 1 byte. LS bit is 0 for Slave Addresses and 1 for Software IDs. Upper 7-bits hold Slave Address or Software ID, respectively. This byte is always 20h when the BMC is the requester.
RequesterAddr uint8 // SlaveAddress or SoftwareID
// Network Function code
// The lower 2-bits of the netFn byte identify the logical unit number, which provides further sub-addressing within the target node.
NetFn NetFn // (odd) higher 6 bits
// Requester's LUN
RequestLUN uint8 // lower 2 bits
// 8-bit checksum algorithm: Initialize checksum to 0. For each byte, checksum = (checksum + byte) modulo 256. Then checksum = - checksum. When the checksum and the bytes are added together, modulo 256, the result should be 0.
Checksum1 uint8
// Responder's Slave Address. 1 byte. LS bit is 0 for Slave Addresses and 1 for Software IDs. Upper 7-bits hold Slave Address or Software ID, respectively. This byte is always 20h when the BMC is the responder.
ResponderAddr uint8 // SlaveAddress or SoftwareID
// Sequence number. This field is used to verify that a response is for a particular instance of a request. Refer to [IPMB] for additional information on use and operation of the Seq field.
RequesterSequence uint8 // higher 6 bits
ResponderLUN uint8 // lower 2 bits
Command uint8
// Completion code returned in the response to indicated success/failure status of the request.
CompletionCode uint8
// Response Data
Data []byte // optional
Checksum2 uint8
}
func (req *IPMIRequest) Pack() []byte {
msgLen := 6 + len(req.CommandData) + 1
msg := make([]byte, msgLen)
packUint8(req.ResponderAddr, msg, 0)
netFn := uint8(req.NetFn) << 2
resLun := req.ResponderLUN & 0x03
packUint8(netFn|resLun, msg, 1)
packUint8(req.Checksum1, msg, 2)
packUint8(req.RequesterAddr, msg, 3)
var seq uint8 = req.RequesterSequence << 2
reqLun := req.RequesterLUN & 0x03
packUint8(seq|reqLun, msg, 4)
packUint8(uint8(req.Command), msg, 5)
if req.CommandData != nil {
packBytes(req.CommandData, msg, 6)
}
packUint8(req.Checksum2, msg, msgLen-1)
return msg
}
func (req *IPMIRequest) ComputeChecksum() {
// 8-bit checksum algorithm: Initialize checksum to 0. For each byte, checksum = (checksum + byte) modulo 256. Then checksum = - checksum. When the checksum and the bytes are added together, modulo 256, the result should be 0.
//
// the position end is not included
var checksumFn = func(msg []byte, start int, end int) uint8 {
c := 0
for i := start; i < end; i++ {
c = (c + int(msg[i])) % 256
}
return -uint8(c)
}
tempData := req.Pack()
cs1Start, cs1End := 0, 2
req.Checksum1 = checksumFn(tempData, cs1Start, cs1End)
cs2Start, cs2End := 3, len(tempData)-1
req.Checksum2 = checksumFn(tempData, cs2Start, cs2End)
}
func (res *IPMIResponse) Unpack(msg []byte) error {
if len(msg) < 7 {
return ErrUnpackedDataTooShortWith(len(msg), 7)
}
res.RequesterAddr, _, _ = unpackUint8(msg, 0)
b, _, _ := unpackUint8(msg, 1)
res.NetFn = NetFn(b >> 2) // the most 6 bit
res.RequestLUN = b & 0x03 // the least 2 bit
res.Checksum1, _, _ = unpackUint8(msg, 2)
res.ResponderAddr, _, _ = unpackUint8(msg, 3)
b4, _, _ := unpackUint8(msg, 4)
res.RequesterSequence = b4 >> 2
res.ResponderLUN = b4 & 0x03
res.Command, _, _ = unpackUint8(msg, 5)
res.CompletionCode, _, _ = unpackUint8(msg, 6)
if len(msg) == 7 {
// Response with only Completion Code
return nil
}
dataLen := len(msg) - 7 - 1
res.Data, _, _ = unpackBytes(msg, 7, dataLen)
res.Checksum2, _, _ = unpackUint8(msg, len(msg)-1)
return nil
}
// BuildIPMIRequest creates IPMIRequest for a Command Request.
// It also fills the Checksum1 and Checksum2 fields of IPMIRequest.
func (c *Client) BuildIPMIRequest(ctx context.Context, reqCmd Request) (*IPMIRequest, error) {
c.lock()
defer c.unlock()
ipmiReq := &IPMIRequest{
ResponderAddr: c.responderAddr,
NetFn: reqCmd.Command().NetFn,
ResponderLUN: c.responderLUN,
RequesterAddr: c.requesterAddr,
RequesterSequence: c.session.ipmiSeq,
RequesterLUN: c.requesterLUN,
Command: reqCmd.Command().ID,
CommandData: reqCmd.Pack(),
}
commandContext := GetCommandContext(ctx)
if commandContext != nil {
c.Debug("Got CommandContext:", commandContext)
if commandContext.responderAddr != nil {
ipmiReq.ResponderAddr = *commandContext.responderAddr
}
if commandContext.responderLUN != nil {
ipmiReq.ResponderLUN = *commandContext.responderLUN
}
if commandContext.requesterAddr != nil {
ipmiReq.RequesterAddr = *commandContext.requesterAddr
}
if commandContext.requesterLUN != nil {
ipmiReq.RequesterLUN = *commandContext.requesterLUN
}
}
c.session.ipmiSeq += 1
if c.session.ipmiSeq > IPMIRequesterSequenceMax {
c.session.ipmiSeq = 1
}
ipmiReq.ComputeChecksum()
return ipmiReq, nil
}
// AllCC returns all possible completion codes for the specified response.
// i.e.:
//
// the generic completion codes for all ipmi cmd response
// +
// the specific completion codes for specified cmd response.
func AllCC(response Response) map[uint8]string {
out := map[uint8]string{}
for k, v := range CC {
out[k] = v
}
for k, v := range response.CompletionCodes() {
out[k] = v
}
return out
}
// StrCC return the description of ccode for the specified response.
// The available completion codes set consists of general completion codes (CC) for all
// commands response and specific completion codes for this response.
func StrCC(response Response, ccode uint8) string {
s, ok := AllCC(response)[ccode]
if ok {
return s
}
return "unknown completion code"
}
|