Skip to content

Commit 7191022

Browse files
committed
Add files
1 parent cb02ecf commit 7191022

18 files changed

+2967
-1
lines changed

README.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,56 @@
1-
# ipmigo
1+
ipmigo
2+
======
3+
4+
**Work In Progress**
5+
6+
ipmigo is a golang implementation for IPMI client.
7+
8+
Supported Version
9+
-----------------
10+
11+
* IPMI v2.0(lanplus)
12+
13+
Examples
14+
--------
15+
16+
```go
17+
package main
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/k-sone/ipmigo"
23+
)
24+
25+
func main() {
26+
c, err := ipmigo.NewClient(ipmigo.Arguments{
27+
Version: ipmigo.V2_0,
28+
Address: "192.168.1.1:623",
29+
Username: "myuser",
30+
Password: "mypass",
31+
CipherSuiteID: 3,
32+
})
33+
if err != nil {
34+
fmt.Println(err)
35+
return
36+
}
37+
38+
if err := c.Open(); err != nil {
39+
fmt.Println(err)
40+
return
41+
}
42+
defer c.Close()
43+
44+
cmd := &ipmigo.GetPOHCounterCommand{}
45+
if err := c.Execute(cmd); err != nil {
46+
fmt.Println(err)
47+
return
48+
}
49+
fmt.Println("Power On Hours", cmd.PowerOnHours())
50+
}
51+
```
52+
53+
License
54+
-------
55+
56+
MIT

asf.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package ipmigo
2+
3+
import (
4+
"encoding/binary"
5+
"encoding/hex"
6+
"fmt"
7+
"net"
8+
"time"
9+
)
10+
11+
const (
12+
asfHeaderSize = 8
13+
pongBodySize = 16
14+
asfIANA = 0x000011be
15+
)
16+
17+
type asfType uint8
18+
19+
const (
20+
asfTypePing = 0x80
21+
asfTypePong = 0x40
22+
)
23+
24+
func (t asfType) String() string {
25+
switch t {
26+
case asfTypePing:
27+
return "Ping"
28+
case asfTypePong:
29+
return "Pong"
30+
default:
31+
return fmt.Sprintf("Unknown(%d)", t)
32+
}
33+
}
34+
35+
type asfHeader struct {
36+
IANA uint32
37+
Type asfType
38+
Tag uint8
39+
Reserved uint8
40+
Length uint8
41+
}
42+
43+
func (a *asfHeader) Marshal() ([]byte, error) {
44+
buf := make([]byte, asfHeaderSize)
45+
binary.BigEndian.PutUint32(buf, a.IANA)
46+
buf[4] = byte(a.Type)
47+
buf[5] = a.Tag
48+
buf[6] = a.Reserved
49+
buf[7] = a.Length
50+
return buf, nil
51+
}
52+
53+
func (a *asfHeader) Unmarshal(buf []byte) ([]byte, error) {
54+
if len(buf) < asfHeaderSize {
55+
return nil, &MessageError{
56+
Message: fmt.Sprintf("Invalid ASF header size : %d", len(buf)),
57+
Detail: hex.EncodeToString(buf),
58+
}
59+
}
60+
61+
a.IANA = binary.BigEndian.Uint32(buf)
62+
a.Type = asfType(buf[4])
63+
a.Tag = buf[5]
64+
a.Reserved = buf[6]
65+
a.Length = buf[7]
66+
67+
return buf[8:], nil
68+
}
69+
70+
func (a *asfHeader) String() string {
71+
return fmt.Sprintf(
72+
`{"IANA":%d,"Type":"%s","Tag":%d,"Reserved":%d,"Length":%d}`,
73+
a.IANA, a.Type, a.Tag, a.Reserved, a.Length)
74+
}
75+
76+
// RMCP/ASF Ping Message (Section 13.2.3)
77+
type pingMessage struct {
78+
RMCPHeader *rmcpHeader
79+
ASFHeader *asfHeader
80+
}
81+
82+
func (p *pingMessage) Marshal() ([]byte, error) {
83+
buf1, err := p.RMCPHeader.Marshal()
84+
if err != nil {
85+
return nil, err
86+
}
87+
buf2, err := p.ASFHeader.Marshal()
88+
if err != nil {
89+
return nil, err
90+
}
91+
return append(buf1, buf2...), nil
92+
}
93+
94+
func (p *pingMessage) String() string {
95+
return fmt.Sprintf(`{"RMCPHeader":%s,"ASFHeader":%s}`, p.RMCPHeader, p.ASFHeader)
96+
}
97+
98+
func newPingMessage() *pingMessage {
99+
return &pingMessage{
100+
RMCPHeader: &rmcpHeader{
101+
Version: rmcpVersion1,
102+
Sequence: rmcpNoAckSeq,
103+
Class: rmcpClassASF,
104+
},
105+
ASFHeader: &asfHeader{
106+
IANA: asfIANA,
107+
Type: asfTypePing,
108+
},
109+
}
110+
}
111+
112+
// RMCP/ASF Pong Message (Section 13.2.4)
113+
type pongMessage struct {
114+
rmcpHeader *rmcpHeader
115+
asfHeader *asfHeader
116+
IANA uint32
117+
OEM uint32
118+
SupEntities uint8
119+
SupInteract uint8
120+
Reserved [6]byte
121+
}
122+
123+
func (p *pongMessage) SupportedIPMI() bool {
124+
return p.SupEntities&0x80 != 0
125+
}
126+
127+
func (p *pongMessage) Unmarshal(buf []byte) ([]byte, error) {
128+
if len(buf) < pongBodySize {
129+
return nil, &MessageError{
130+
Message: fmt.Sprintf("Invalid Pong body size : %d", len(buf)),
131+
Detail: hex.EncodeToString(buf),
132+
}
133+
}
134+
135+
p.IANA = binary.BigEndian.Uint32(buf)
136+
p.OEM = binary.BigEndian.Uint32(buf[4:])
137+
p.SupEntities = buf[8]
138+
p.SupInteract = buf[9]
139+
copy(p.Reserved[:], buf[10:])
140+
141+
return buf[pongBodySize:], nil
142+
}
143+
144+
func (p *pongMessage) String() string {
145+
return fmt.Sprintf(
146+
`{"RMCPHeader":%s,"ASFHeader":%s,"IANA":%d,"OEM":%d,`+
147+
`"SupEntities":%d,"SupInteract":%d,"Reserved":"%s"}`,
148+
p.rmcpHeader, p.asfHeader, p.IANA, p.OEM, p.SupEntities, p.SupInteract,
149+
hex.EncodeToString(p.Reserved[:]))
150+
}
151+
152+
func ping(conn net.Conn, timeout time.Duration) error {
153+
res, _, err := sendMessage(conn, newPingMessage(), timeout)
154+
if err != nil {
155+
return err
156+
}
157+
158+
pong, ok := res.(*pongMessage)
159+
if !ok {
160+
return &MessageError{
161+
Message: "Received an unexpected message (Ping)",
162+
Detail: res.String(),
163+
}
164+
}
165+
if !pong.SupportedIPMI() {
166+
return ErrNotSupportedIPMI
167+
}
168+
169+
return nil
170+
}

client.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package ipmigo
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
type Version int
9+
10+
const (
11+
V1_5 Version = iota + 1
12+
V2_0
13+
)
14+
15+
// Channel Privilege Levels. (Section 6.8)
16+
type PrivilegeLevel uint8
17+
18+
const (
19+
PrivilegeCallback PrivilegeLevel = iota + 1
20+
PrivilegeUser
21+
PrivilegeOperator
22+
PrivilegeAdministrator
23+
)
24+
25+
func (p PrivilegeLevel) String() string {
26+
switch p {
27+
case PrivilegeCallback:
28+
return "CALLBACK"
29+
case PrivilegeUser:
30+
return "USER"
31+
case PrivilegeOperator:
32+
return "OPERATOR"
33+
case PrivilegeAdministrator:
34+
return "ADMINISTRATOR"
35+
default:
36+
return fmt.Sprintf("Unknown(%d)", p)
37+
}
38+
}
39+
40+
// An argument for creating an IPMI Client
41+
type Arguments struct {
42+
Version Version // IPMI version to use
43+
Network string // See net.Dial parameter (The default is `udp`)
44+
Address string // See net.Dial parameter
45+
Timeout time.Duration // Each connect/read-write timeout (The default is 5sec)
46+
Retries uint // Number of retries (The default is `0`)
47+
Username string // Remote server username
48+
Password string // Remote server password
49+
PrivilegeLevel PrivilegeLevel // Session privilege level (The default is `Administrator`)
50+
CipherSuiteID uint // ID of cipher suite, See Table 22-20 (The default is `0` which no auth and no encrypt)
51+
}
52+
53+
func (a *Arguments) setDefault() {
54+
if a.Version == 0 {
55+
a.Version = V2_0
56+
}
57+
if a.Network == "" {
58+
a.Network = "udp"
59+
}
60+
if a.Timeout == 0 {
61+
a.Timeout = 5 * time.Second
62+
}
63+
if a.PrivilegeLevel == 0 {
64+
a.PrivilegeLevel = PrivilegeAdministrator
65+
}
66+
}
67+
68+
func (a *Arguments) validate() error {
69+
switch a.Version {
70+
case V2_0:
71+
if len(a.Password) > passwordMaxLengthV2_0 {
72+
return &ArgumentError{
73+
Value: a.Password,
74+
Message: "Password is too long",
75+
}
76+
}
77+
if a.CipherSuiteID < 0 || a.CipherSuiteID > uint(len(cipherSuiteIDs)-1) {
78+
return &ArgumentError{
79+
Value: a.CipherSuiteID,
80+
Message: "Invalid Cipher Suite ID",
81+
}
82+
}
83+
if a.CipherSuiteID > 3 {
84+
return &ArgumentError{
85+
Value: a.CipherSuiteID,
86+
Message: "Unsupported Cipher Suite ID in ipmigo",
87+
}
88+
}
89+
case V1_5:
90+
// TODO Support v1.5 ?
91+
fallthrough
92+
default:
93+
return &ArgumentError{
94+
Value: a.Version,
95+
Message: "Unsupported IPMI version",
96+
}
97+
}
98+
99+
if a.PrivilegeLevel < 0 || a.PrivilegeLevel > PrivilegeAdministrator {
100+
return &ArgumentError{
101+
Value: a.PrivilegeLevel,
102+
Message: "Invalid Privilege Level",
103+
}
104+
}
105+
106+
if len(a.Username) > userNameMaxLength {
107+
return &ArgumentError{
108+
Value: a.Username,
109+
Message: "Username is too long",
110+
}
111+
}
112+
113+
return nil
114+
}
115+
116+
// IPMI Client
117+
type Client struct {
118+
session session
119+
}
120+
121+
func (c *Client) Ping() error { return c.session.Ping() }
122+
func (c *Client) Open() error { return c.session.Open() }
123+
func (c *Client) Close() error { return c.session.Close() }
124+
func (c *Client) Execute(cmd Command) error { return c.session.Execute(cmd) }
125+
126+
// Create an IPMI Client
127+
func NewClient(args Arguments) (*Client, error) {
128+
if err := args.validate(); err != nil {
129+
return nil, err
130+
}
131+
args.setDefault()
132+
133+
var s session
134+
switch args.Version {
135+
case V1_5:
136+
s = newSessionV1_5(&args)
137+
case V2_0:
138+
s = newSessionV2_0(&args)
139+
}
140+
return &Client{session: s}, nil
141+
}

0 commit comments

Comments
 (0)