run the basic process with iscsi driver

This commit is contained in:
Lei Xue
2016-05-02 22:11:33 +08:00
parent d770eb33ac
commit c5d68b38b2
17 changed files with 948 additions and 633 deletions

70
pkg/port/interfaces.go Normal file
View File

@@ -0,0 +1,70 @@
package port
import (
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/port/iscsit"
)
type SCSITargetDriver interface {
Init() error
Exit() error
CreateTarget(target *api.SCSITarget) error
DestroyTarget(target *api.SCSITarget) error
CreatePortal(name string) error
DestroyPortal(name string) error
CreateLu(lu *api.SCSILu) error
GetLu(lun uint8) (uint64, error)
ProcessCommand(buf []byte) ([]byte, error)
CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error
}
type fakeSCSITargetDriver struct {
api.SCSITargetDriverCommon
}
func (fake *fakeSCSITargetDriver) Init() error {
return nil
}
func (fake *fakeSCSITargetDriver) Exit() error {
return nil
}
func (fake *fakeSCSITargetDriver) CreateTarget(target *api.SCSITarget) error {
return nil
}
func (fake *fakeSCSITargetDriver) DestroyTarget(target *api.SCSITarget) error {
return nil
}
func (fake *fakeSCSITargetDriver) CreatePortal(name string) error {
return nil
}
func (fake *fakeSCSITargetDriver) DestroyPortal(name string) error {
return nil
}
func (fake *fakeSCSITargetDriver) CreateLu(lu *api.SCSILu) error {
return nil
}
func (fake *fakeSCSITargetDriver) GetLun(lun uint8) (uint64, error) {
return 0, nil
}
func (fake *fakeSCSITargetDriver) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
return nil
}
func (fake *fakeSCSITargetDriver) ProcessCommand(buf []byte) ([]byte, error) {
return []byte(""), nil
}
func NewTargetDriver(driver string, tgt *api.SCSITarget) SCSITargetDriver {
if driver == "iscsi" {
return iscsit.NewISCSITarget(tgt)
}
return nil
}

View File

@@ -1,26 +1,8 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package packet implements the iSCSI PDU packet format as specified in
// rfc7143 section 11.
package iscsit
import (
"bytes"
"fmt"
"io"
"strings"
)
@@ -70,15 +52,7 @@ var opCodeMap = map[OpCode]string{
OpReject: "Reject",
}
func (c OpCode) String() string {
s := opCodeMap[c]
if s == "" {
s = fmt.Sprintf("Unknown Code: %x", int(c))
}
return s
}
type Message struct {
type ISCSICommand struct {
OpCode OpCode
RawHeader []byte
DataLen int
@@ -109,8 +83,8 @@ type Message struct {
// SCSI commands
ExpectedDataLen uint32
CDB []byte
Status Status
SCSIResponse Response
Status byte
SCSIResponse byte
// Data-In
HasStatus bool
@@ -118,23 +92,23 @@ type Message struct {
BufferOffset uint32
}
func (m *Message) Bytes() []byte {
switch m.OpCode {
func (cmd *ISCSICommand) Bytes() []byte {
switch cmd.OpCode {
case OpLoginResp:
return m.loginRespBytes()
return cmd.loginRespBytes()
case OpLogoutResp:
return m.logoutRespBytes()
return cmd.logoutRespBytes()
case OpSCSIResp:
return m.scsiCmdRespBytes()
return cmd.scsiCmdRespBytes()
case OpSCSIIn:
return m.dataInBytes()
return cmd.dataInBytes()
}
return nil
}
func (m *Message) String() string {
func (m *ISCSICommand) String() string {
var s []string
s = append(s, fmt.Sprintf("Op: %v", m.OpCode))
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))
@@ -171,37 +145,6 @@ func (m *Message) String() string {
return strings.Join(s, "\n")
}
// Response composes a reply to the given message with the appropriate bits set.
func (m *Message) Response(r *Message) {
r.TaskTag = m.TaskTag
r.ConnID = m.ConnID
r.ISID = m.ISID
}
func Next(r io.Reader) (*Message, error) {
buf := make([]byte, 48) // TODO: sync.Pool
if _, err := io.ReadFull(r, buf); err != nil {
return nil, err
}
m, err := parseHeader(buf)
if err != nil {
return nil, err
}
m.RawHeader = buf
if m.DataLen > 0 {
dl := m.DataLen
for dl%4 > 0 {
dl++
}
data := make([]byte, dl)
if _, err := io.ReadFull(r, data); err != nil {
return nil, err
}
m.RawData = data[:m.DataLen]
}
return m, nil
}
// 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 {
@@ -220,13 +163,12 @@ func MarshalUint64(i uint64) []byte {
}
return data
}
func parseHeader(data []byte) (*Message, error) {
func parseHeader(data []byte) (*ISCSICommand, error) {
if len(data) != 48 {
return nil, fmt.Errorf("garbled header")
}
// TODO: sync.Pool
m := &Message{}
m := &ISCSICommand{}
m.Immediate = 0x40&data[0] == 0x40
m.OpCode = OpCode(data[0] & 0x3f)
m.Final = 0x80&data[1] == 0x80
@@ -274,3 +216,197 @@ func parseHeader(data []byte) (*Message, error) {
}
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()
}

View File

@@ -17,6 +17,11 @@ limitations under the License.
// iSCSI Target Driver
package iscsit
import (
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
type ISCSIDiscoveryMethod string
var (
@@ -33,9 +38,11 @@ type ISCSIRedirectInfo struct {
}
type ISCSITarget struct {
*api.SCSITarget
api.SCSITargetDriverCommon
Sessions []*ISCSISession
SessionParam []ISCSISessionParam
TID int
Alias string
MaxSessions int
RedirectInfo ISCSIRedirectInfo
@@ -44,41 +51,128 @@ type ISCSITarget struct {
NopCount int
}
type ISCSITargetDriver struct {
SCSITargetDriver
func NewISCSITarget(target *api.SCSITarget) *ISCSITarget {
return &ISCSITarget{
SCSITarget: target,
}
}
func (tgt *ISCSITargetDriver) Init() error {
func (tgt *ISCSITarget) Init() error {
return nil
}
func (tgt *ISCSITargetDriver) Exit() error {
func (tgt *ISCSITarget) Exit() error {
return nil
}
func (tgt *ISCSITargetDriver) CreateTarget(target *SCSITarget) error {
func (tgt *ISCSITarget) CreateTarget(target *api.SCSITarget) error {
return nil
}
func (tgt *ISCSITargetDriver) DestroyTarget(target *SCSITarget) error {
func (tgt *ISCSITarget) DestroyTarget(target *api.SCSITarget) error {
return nil
}
func (tgt *ISCSITargetDriver) CreatePortal(name string) error {
func (tgt *ISCSITarget) CreatePortal(name string) error {
return nil
}
func (tgt *ISCSITargetDriver) DestroyPortal(name string) error {
func (tgt *ISCSITarget) DestroyPortal(name string) error {
return nil
}
func (tgt *ISCSITargetDriver) CreateLu(lu *SCSILu) error {
func (tgt *ISCSITarget) CreateLu(lu *api.SCSILu) error {
return nil
}
func (tgt *ISCSITargetDriver) GetLun(lun uint8) (uint64, error) {
func (tgt *ISCSITarget) GetLu(lun uint8) (uint64, error) {
return 0, nil
}
func (tgt *ISCSITargetDriver) CommandNotify(nid uint64, result int, cmd *SCSICommand) error {
func (tgt *ISCSITarget) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
return nil
}
func (tgt *ISCSITarget) ProcessCommand(buf []byte) ([]byte, error) {
b := make([]byte, 48) // TODO: sync.Pool
b = buf[0:48]
m, err := parseHeader(b)
if err != nil {
return nil, err
}
m.RawHeader = b
if m.DataLen > 0 {
m.RawData = buf[48:m.DataLen]
}
resp := &ISCSICommand{}
switch m.OpCode {
case OpLoginReq:
resp = &ISCSICommand{
OpCode: OpLoginResp,
Transit: true,
NSG: FullFeaturePhase,
StatSN: m.ExpStatSN,
TaskTag: m.TaskTag,
ExpCmdSN: m.CmdSN,
MaxCmdSN: m.CmdSN,
RawData: util.MarshalKVText(map[string]string{
"HeaderDigest": "None",
"DataDigest": "None",
}),
}
break
case OpSCSICmd:
resp = &ISCSICommand{
OpCode: OpSCSIResp,
Final: true,
StatSN: m.ExpStatSN,
TaskTag: m.TaskTag,
ExpCmdSN: m.CmdSN + 1,
MaxCmdSN: m.CmdSN + 10,
}
switch api.SCSICommandType(m.CDB[0]) {
case api.TEST_UNIT_READY:
// test unit ready
resp.Status = api.SAM_STAT_GOOD
resp.SCSIResponse = 0x01
break
case api.READ_CAPACITY:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
var data []byte
data = append(data, MarshalUint64(uint64(0))[4:]...)
data = append(data, MarshalUint64(uint64(0))[4:]...)
resp.RawData = data
break
case api.SERVICE_ACTION_IN:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
sa := m.CDB[1] & 0x1f
switch sa {
case 0x10:
c := &Capacity{}
resp.RawData = c.bytes()
}
break
case api.INQUIRY:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
alloc := int(ParseUint(m.CDB[3:5]))
inq := &InquiryData{
Vendor: [8]byte{'1', '1', 'c', 'a', 'n', 's'},
Product: [16]byte{'c', 'o', 'f', 'f', 'e', 'e'},
RevisionLevel: [4]byte{'1', '.', '0'},
SerialNumber: 52,
}
if len(inq.bytes()) >= alloc {
resp.RawData = inq.bytes()[:alloc]
} else {
resp.RawData = inq.bytes()
}
break
default:
break
}
}
b1 := resp.Bytes()
return b1, nil
}

View File

@@ -1,24 +1,8 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iscsit
import "bytes"
func (m *Message) loginRespBytes() []byte {
func (m *ISCSICommand) loginRespBytes() []byte {
// rfc7143 11.13
buf := &bytes.Buffer{}
// byte 0

View File

@@ -1,24 +1,8 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iscsit
import "bytes"
func (m *Message) logoutRespBytes() []byte {
func (m *ISCSICommand) logoutRespBytes() []byte {
buf := &bytes.Buffer{}
buf.WriteByte(byte(OpLogoutResp))
buf.WriteByte(0x80)

View File

@@ -42,6 +42,9 @@ type ISCSISession struct {
Rdma int
}
type ISCSIHeader struct {
}
type ISCSIPdu struct {
Bhs ISCSIHeader
AhsSize uint
@@ -78,7 +81,7 @@ func NewISCSISession() (*ISCSISession, error) {
tsih += uint16(b[0]) << 8
tsih += uint16(b[1])
return &Session{
return &ISCSISession{
Tsih: tsih,
}, nil
}