run the basic process with iscsi driver
This commit is contained in:
412
pkg/port/iscsit/cmd.go
Normal file
412
pkg/port/iscsit/cmd.go
Normal file
@@ -0,0 +1,412 @@
|
||||
package iscsit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type OpCode int
|
||||
|
||||
const (
|
||||
// Defined on the initiator.
|
||||
OpNoopOut OpCode = 0x00
|
||||
OpSCSICmd = 0x01
|
||||
OpSCSITaskReq = 0x02
|
||||
OpLoginReq = 0x03
|
||||
OpTextReq = 0x04
|
||||
OpSCSIOut = 0x05
|
||||
OpLogoutReq = 0x06
|
||||
OpSNACKReq = 0x10
|
||||
// Defined on the target.
|
||||
OpNoopIn OpCode = 0x20
|
||||
OpSCSIResp = 0x21
|
||||
OpSCSITaskResp = 0x22
|
||||
OpLoginResp = 0x23
|
||||
OpTextResp = 0x24
|
||||
OpSCSIIn = 0x25
|
||||
OpLogoutResp = 0x26
|
||||
OpReady = 0x31
|
||||
OpAsync = 0x32
|
||||
OpReject = 0x3f
|
||||
)
|
||||
|
||||
var opCodeMap = map[OpCode]string{
|
||||
OpNoopOut: "NOP-Out",
|
||||
OpSCSICmd: "SCSI Command",
|
||||
OpSCSITaskReq: "SCSI Task Management FunctionRequest",
|
||||
OpLoginReq: "Login Request",
|
||||
OpTextReq: "Text Request",
|
||||
OpSCSIOut: "SCSI Data-Out (write)",
|
||||
OpLogoutReq: "Logout Request",
|
||||
OpSNACKReq: "SNACK Request",
|
||||
OpNoopIn: "NOP-In",
|
||||
OpSCSIResp: "SCSI Response",
|
||||
OpSCSITaskResp: "SCSI Task Management Function Response",
|
||||
OpLoginResp: "Login Response",
|
||||
OpTextResp: "Text Response",
|
||||
OpSCSIIn: "SCSI Data-In (read)",
|
||||
OpLogoutResp: "Logout Response",
|
||||
OpReady: "Ready To Transfer (R2T)",
|
||||
OpAsync: "Asynchronous Message",
|
||||
OpReject: "Reject",
|
||||
}
|
||||
|
||||
type ISCSICommand struct {
|
||||
OpCode OpCode
|
||||
RawHeader []byte
|
||||
DataLen int
|
||||
RawData []byte
|
||||
Final bool
|
||||
Immediate bool
|
||||
TaskTag uint32
|
||||
ExpCmdSN, MaxCmdSN uint32
|
||||
AHSLen int
|
||||
|
||||
ConnID uint16 // Connection ID.
|
||||
CmdSN uint32 // Command serial number.
|
||||
ExpStatSN uint32 // Expected status serial.
|
||||
|
||||
Read, Write bool
|
||||
LUN uint8
|
||||
Transit bool // Transit bit.
|
||||
Cont bool // Continue bit.
|
||||
CSG, NSG Stage // Current Stage, Next Stage.
|
||||
ISID uint64 // Initiator part of the SSID.
|
||||
TSIH uint16 // Target-assigned Session Identifying Handle.
|
||||
StatSN uint32 // Status serial number.
|
||||
|
||||
// For login response.
|
||||
StatusClass uint8
|
||||
StatusDetail uint8
|
||||
|
||||
// SCSI commands
|
||||
ExpectedDataLen uint32
|
||||
CDB []byte
|
||||
Status byte
|
||||
SCSIResponse byte
|
||||
|
||||
// Data-In
|
||||
HasStatus bool
|
||||
DataSN uint32
|
||||
BufferOffset uint32
|
||||
}
|
||||
|
||||
func (cmd *ISCSICommand) Bytes() []byte {
|
||||
switch cmd.OpCode {
|
||||
case OpLoginResp:
|
||||
return cmd.loginRespBytes()
|
||||
case OpLogoutResp:
|
||||
return cmd.logoutRespBytes()
|
||||
case OpSCSIResp:
|
||||
return cmd.scsiCmdRespBytes()
|
||||
case OpSCSIIn:
|
||||
return cmd.dataInBytes()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ISCSICommand) String() string {
|
||||
var s []string
|
||||
s = append(s, fmt.Sprintf("Op: %v", opCodeMap[m.OpCode]))
|
||||
s = append(s, fmt.Sprintf("Final = %v", m.Final))
|
||||
s = append(s, fmt.Sprintf("Immediate = %v", m.Immediate))
|
||||
s = append(s, fmt.Sprintf("Data Segment Length = %d", m.DataLen))
|
||||
s = append(s, fmt.Sprintf("Task Tag = %x", m.TaskTag))
|
||||
s = append(s, fmt.Sprintf("AHS Length = %d", m.AHSLen))
|
||||
switch m.OpCode {
|
||||
case OpLoginReq:
|
||||
s = append(s, fmt.Sprintf("ISID = %x", m.ISID))
|
||||
s = append(s, fmt.Sprintf("Transit = %v", m.Transit))
|
||||
s = append(s, fmt.Sprintf("Continue = %v", m.Cont))
|
||||
s = append(s, fmt.Sprintf("Current Stage = %v", m.CSG))
|
||||
s = append(s, fmt.Sprintf("Next Stage = %v", m.NSG))
|
||||
case OpLoginResp:
|
||||
s = append(s, fmt.Sprintf("ISID = %x", m.ISID))
|
||||
s = append(s, fmt.Sprintf("Transit = %v", m.Transit))
|
||||
s = append(s, fmt.Sprintf("Continue = %v", m.Cont))
|
||||
s = append(s, fmt.Sprintf("Current Stage = %v", m.CSG))
|
||||
s = append(s, fmt.Sprintf("Next Stage = %v", m.NSG))
|
||||
s = append(s, fmt.Sprintf("Status Class = %d", m.StatusClass))
|
||||
s = append(s, fmt.Sprintf("Status Detail = %d", m.StatusDetail))
|
||||
case OpSCSICmd:
|
||||
s = append(s, fmt.Sprintf("LUN = %d", m.LUN))
|
||||
s = append(s, fmt.Sprintf("ExpectedDataLen = %d", m.ExpectedDataLen))
|
||||
s = append(s, fmt.Sprintf("CmdSN = %d", m.CmdSN))
|
||||
s = append(s, fmt.Sprintf("ExpStatSN = %d", m.ExpStatSN))
|
||||
s = append(s, fmt.Sprintf("Read = %v", m.Read))
|
||||
s = append(s, fmt.Sprintf("Write = %v", m.Write))
|
||||
s = append(s, fmt.Sprintf("CDB = %x", m.CDB))
|
||||
case OpSCSIResp:
|
||||
s = append(s, fmt.Sprintf("StatSN = %d", m.StatSN))
|
||||
s = append(s, fmt.Sprintf("ExpCmdSN = %d", m.ExpCmdSN))
|
||||
s = append(s, fmt.Sprintf("MaxCmdSN = %d", m.MaxCmdSN))
|
||||
}
|
||||
return strings.Join(s, "\n")
|
||||
}
|
||||
|
||||
// parseUint parses the given slice as a network-byte-ordered integer. If
|
||||
// there are more than 8 bytes in data, it overflows.
|
||||
func ParseUint(data []byte) uint64 {
|
||||
var out uint64
|
||||
for i := 0; i < len(data); i++ {
|
||||
out += uint64(data[len(data)-i-1]) << uint(8*i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func MarshalUint64(i uint64) []byte {
|
||||
var data []byte
|
||||
for j := 0; j < 8; j++ {
|
||||
b := byte(i >> uint(8*(7-j)) & 0xff)
|
||||
data = append(data, b)
|
||||
}
|
||||
return data
|
||||
}
|
||||
func parseHeader(data []byte) (*ISCSICommand, error) {
|
||||
if len(data) != 48 {
|
||||
return nil, fmt.Errorf("garbled header")
|
||||
}
|
||||
// TODO: sync.Pool
|
||||
m := &ISCSICommand{}
|
||||
m.Immediate = 0x40&data[0] == 0x40
|
||||
m.OpCode = OpCode(data[0] & 0x3f)
|
||||
m.Final = 0x80&data[1] == 0x80
|
||||
m.AHSLen = int(data[4]) * 4
|
||||
m.DataLen = int(ParseUint(data[5:8]))
|
||||
m.TaskTag = uint32(ParseUint(data[16:20]))
|
||||
switch m.OpCode {
|
||||
case OpSCSICmd:
|
||||
m.LUN = uint8(data[9])
|
||||
m.ExpectedDataLen = uint32(ParseUint(data[20:24]))
|
||||
m.CmdSN = uint32(ParseUint(data[24:28]))
|
||||
m.Read = data[1]&0x40 == 0x40
|
||||
m.Write = data[1]&0x20 == 0x20
|
||||
m.CDB = data[32:48]
|
||||
m.ExpStatSN = uint32(ParseUint(data[28:32]))
|
||||
case OpSCSIResp:
|
||||
case OpLoginReq:
|
||||
m.Transit = m.Final
|
||||
m.Cont = data[1]&0x40 == 0x40
|
||||
if m.Cont && m.Transit {
|
||||
// rfc7143 11.12.2
|
||||
return nil, fmt.Errorf("transit and continue bits set in same login request")
|
||||
}
|
||||
m.CSG = Stage(data[1]&0xc) >> 2
|
||||
m.NSG = Stage(data[1] & 0x3)
|
||||
m.ISID = uint64(ParseUint(data[8:14]))
|
||||
m.TSIH = uint16(ParseUint(data[14:16]))
|
||||
m.ConnID = uint16(ParseUint(data[20:22]))
|
||||
m.CmdSN = uint32(ParseUint(data[24:28]))
|
||||
m.ExpStatSN = uint32(ParseUint(data[28:32]))
|
||||
case OpLoginResp:
|
||||
m.Transit = m.Final
|
||||
m.Cont = data[1]&0x40 == 0x40
|
||||
if m.Cont && m.Transit {
|
||||
// rfc7143 11.12.2
|
||||
return nil, fmt.Errorf("transit and continue bits set in same login request")
|
||||
}
|
||||
m.CSG = Stage(data[1]&0xc) >> 2
|
||||
m.NSG = Stage(data[1] & 0x3)
|
||||
m.StatSN = uint32(ParseUint(data[24:28]))
|
||||
m.ExpCmdSN = uint32(ParseUint(data[28:32]))
|
||||
m.MaxCmdSN = uint32(ParseUint(data[32:36]))
|
||||
m.StatusClass = uint8(data[36])
|
||||
m.StatusDetail = uint8(data[37])
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *ISCSICommand) scsiCmdRespBytes() []byte {
|
||||
// rfc7143 11.4
|
||||
buf := &bytes.Buffer{}
|
||||
buf.WriteByte(byte(OpSCSIResp))
|
||||
buf.WriteByte(0x80) // 11.4.1 = wtf
|
||||
buf.WriteByte(byte(m.SCSIResponse))
|
||||
buf.WriteByte(byte(m.Status))
|
||||
|
||||
// Skip through to byte 16
|
||||
for i := 0; i < 3*4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
for i := 0; i < 4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
for i := 0; i < 3*4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (m *ISCSICommand) dataInBytes() []byte {
|
||||
// rfc7143 11.7
|
||||
buf := &bytes.Buffer{}
|
||||
buf.WriteByte(byte(OpSCSIIn))
|
||||
var b byte
|
||||
b = 0x80
|
||||
if m.HasStatus {
|
||||
b |= 0x01
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(0x00)
|
||||
if m.HasStatus {
|
||||
b = byte(m.Status)
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
|
||||
buf.WriteByte(0x00) // 4
|
||||
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||
buf.WriteByte(0x00)
|
||||
buf.WriteByte(byte(m.LUN))
|
||||
// Skip through to byte 16
|
||||
for i := 0; i < 6; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
for i := 0; i < 4; i++ {
|
||||
// 11.7.4
|
||||
buf.WriteByte(0xff)
|
||||
}
|
||||
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
buf.Write(MarshalUint64(uint64(m.DataSN))[4:])
|
||||
buf.Write(MarshalUint64(uint64(m.BufferOffset))[4:])
|
||||
for i := 0; i < 4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
buf.Write(m.RawData)
|
||||
dl := len(m.RawData)
|
||||
for dl%4 > 0 {
|
||||
dl++
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
type InquiryData struct {
|
||||
PeripheralQualifier int
|
||||
PeripheralType int
|
||||
Removable bool
|
||||
Version int
|
||||
SupportsACA bool
|
||||
Hierarchical bool
|
||||
SupportsSCC bool
|
||||
HasACC bool
|
||||
TargetGroupSupport int
|
||||
ThirdPartyCopy bool
|
||||
Protect bool
|
||||
EnclosureServices bool
|
||||
Multiport bool
|
||||
MediaChanger bool
|
||||
Vendor [8]byte
|
||||
Product [16]byte
|
||||
RevisionLevel [4]byte
|
||||
SerialNumber uint64
|
||||
}
|
||||
|
||||
func (id *InquiryData) bytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
var b byte
|
||||
b = (uint8(id.PeripheralQualifier) << 5) & 0xe0
|
||||
b |= uint8(id.PeripheralType) & 0x1f
|
||||
buf.WriteByte(b)
|
||||
b = 0
|
||||
if id.Removable {
|
||||
b = 0x80
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(byte(id.Version))
|
||||
b = 0x02
|
||||
if id.SupportsACA {
|
||||
b |= 0x20
|
||||
}
|
||||
if id.Hierarchical {
|
||||
b |= 0x10
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(0x00)
|
||||
// byte 5
|
||||
b = 0
|
||||
if id.SupportsSCC {
|
||||
b |= 0x80
|
||||
}
|
||||
if id.HasACC {
|
||||
b |= 0x40
|
||||
}
|
||||
b |= byte(id.TargetGroupSupport) << 4 & 0x30
|
||||
if id.ThirdPartyCopy {
|
||||
b |= 0x08
|
||||
}
|
||||
if id.Protect {
|
||||
b |= 0x01
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
// byte 6
|
||||
b = 0
|
||||
if id.EnclosureServices {
|
||||
b |= 0x40
|
||||
}
|
||||
if id.Multiport {
|
||||
b |= 0x10
|
||||
}
|
||||
if id.MediaChanger {
|
||||
b |= 0x08
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(0x02)
|
||||
buf.Write(id.Vendor[:])
|
||||
buf.Write(id.Product[:])
|
||||
buf.Write(id.RevisionLevel[:])
|
||||
buf.Write(MarshalUint64(id.SerialNumber))
|
||||
for i := 0; i < 12; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
data := buf.Bytes()
|
||||
data[4] = byte(len(data) - 4)
|
||||
return data
|
||||
}
|
||||
|
||||
type Capacity struct {
|
||||
LBA uint64
|
||||
Blocksize uint32
|
||||
ProtectionType uint8
|
||||
PIExponent uint8
|
||||
LogicalExponent uint8
|
||||
ThinProvisioned bool
|
||||
ThinProvReturnsZeros bool
|
||||
LowestLBA uint16
|
||||
}
|
||||
|
||||
func (c *Capacity) bytes() []byte {
|
||||
// table 111
|
||||
// http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
|
||||
buf := &bytes.Buffer{}
|
||||
buf.Write(MarshalUint64(c.LBA))
|
||||
buf.Write(MarshalUint64(uint64(c.Blocksize))[4:])
|
||||
var b byte
|
||||
if c.ProtectionType > 0 {
|
||||
b |= 0x01
|
||||
b |= c.ProtectionType << 1
|
||||
b &= 0x0f
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
b = c.PIExponent << 4
|
||||
b |= c.LogicalExponent
|
||||
buf.WriteByte(b)
|
||||
lowLBA := MarshalUint64(uint64(c.LowestLBA))[6:]
|
||||
lowLBA[0] &= 0x3f
|
||||
if c.ThinProvisioned {
|
||||
lowLBA[0] &= 0x80
|
||||
}
|
||||
if c.ThinProvReturnsZeros {
|
||||
lowLBA[0] &= 0x40
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
Reference in New Issue
Block a user