fix bugs and make it run basically
This commit is contained in:
@@ -1,9 +1,22 @@
|
||||
/*
|
||||
Copyright 2016 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 port
|
||||
|
||||
import (
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/port/iscsit"
|
||||
)
|
||||
import "github.com/gostor/gotgt/pkg/api"
|
||||
|
||||
type SCSITargetDriver interface {
|
||||
Init() error
|
||||
@@ -16,7 +29,6 @@ type SCSITargetDriver interface {
|
||||
CreateLu(lu *api.SCSILu) error
|
||||
GetLu(lun uint8) (uint64, error)
|
||||
|
||||
ProcessCommand(buf []byte) ([]byte, error)
|
||||
CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error
|
||||
}
|
||||
|
||||
@@ -58,13 +70,3 @@ func (fake *fakeSCSITargetDriver) GetLun(lun uint8) (uint64, error) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
type OpCode int
|
||||
@@ -52,6 +54,8 @@ var opCodeMap = map[OpCode]string{
|
||||
OpReject: "Reject",
|
||||
}
|
||||
|
||||
const DataPadding = 4
|
||||
|
||||
type ISCSICommand struct {
|
||||
OpCode OpCode
|
||||
RawHeader []byte
|
||||
@@ -68,7 +72,7 @@ type ISCSICommand struct {
|
||||
ExpStatSN uint32 // Expected status serial.
|
||||
|
||||
Read, Write bool
|
||||
LUN uint8
|
||||
LUN [8]uint8
|
||||
Transit bool // Transit bit.
|
||||
Cont bool // Continue bit.
|
||||
CSG, NSG Stage // Current Stage, Next Stage.
|
||||
@@ -102,6 +106,10 @@ func (cmd *ISCSICommand) Bytes() []byte {
|
||||
return cmd.scsiCmdRespBytes()
|
||||
case OpSCSIIn:
|
||||
return cmd.dataInBytes()
|
||||
case OpTextResp:
|
||||
return cmd.textRespBytes()
|
||||
case OpNoopIn:
|
||||
return cmd.noopInBytes()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -155,16 +163,8 @@ func ParseUint(data []byte) uint64 {
|
||||
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 {
|
||||
if len(data) != BHS_SIZE {
|
||||
return nil, fmt.Errorf("garbled header")
|
||||
}
|
||||
// TODO: sync.Pool
|
||||
@@ -177,7 +177,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
|
||||
m.TaskTag = uint32(ParseUint(data[16:20]))
|
||||
switch m.OpCode {
|
||||
case OpSCSICmd:
|
||||
m.LUN = uint8(data[9])
|
||||
m.LUN = [8]uint8{data[9]}
|
||||
m.ExpectedDataLen = uint32(ParseUint(data[20:24]))
|
||||
m.CmdSN = uint32(ParseUint(data[24:28]))
|
||||
m.Read = data[1]&0x40 == 0x40
|
||||
@@ -185,7 +185,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
|
||||
m.CDB = data[32:48]
|
||||
m.ExpStatSN = uint32(ParseUint(data[28:32]))
|
||||
case OpSCSIResp:
|
||||
case OpLoginReq:
|
||||
case OpLoginReq, OpTextReq, OpNoopOut, OpLogoutReq:
|
||||
m.Transit = m.Final
|
||||
m.Cont = data[1]&0x40 == 0x40
|
||||
if m.Cont && m.Transit {
|
||||
@@ -229,13 +229,13 @@ func (m *ISCSICommand) scsiCmdRespBytes() []byte {
|
||||
for i := 0; i < 3*4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
buf.Write(util.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:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
for i := 0; i < 3*4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
@@ -259,24 +259,22 @@ func (m *ISCSICommand) dataInBytes() []byte {
|
||||
}
|
||||
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))
|
||||
buf.WriteByte(0x00) // 4
|
||||
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||
// Skip through to byte 16
|
||||
for i := 0; i < 6; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
for i := 0; i < 8; i++ {
|
||||
buf.WriteByte(byte(m.LUN[i]))
|
||||
}
|
||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
buf.Write(util.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:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.DataSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.BufferOffset))[4:])
|
||||
for i := 0; i < 4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
@@ -290,123 +288,79 @@ func (m *ISCSICommand) dataInBytes() []byte {
|
||||
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 {
|
||||
func (m *ISCSICommand) textRespBytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.WriteByte(byte(OpTextResp))
|
||||
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 {
|
||||
if m.Final {
|
||||
b |= 0x80
|
||||
}
|
||||
if id.HasACC {
|
||||
if m.Cont {
|
||||
b |= 0x40
|
||||
}
|
||||
b |= byte(id.TargetGroupSupport) << 4 & 0x30
|
||||
if id.ThirdPartyCopy {
|
||||
b |= 0x08
|
||||
}
|
||||
if id.Protect {
|
||||
b |= 0x01
|
||||
}
|
||||
// byte 1
|
||||
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(b)
|
||||
buf.WriteByte(b)
|
||||
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||
// Skip through to byte 12
|
||||
for i := 0; i < 2*4; 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.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
for i := 0; i < 4; i++ {
|
||||
buf.WriteByte(0xff)
|
||||
}
|
||||
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
|
||||
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
for i := 0; i < 3*4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
if c.ThinProvReturnsZeros {
|
||||
lowLBA[0] &= 0x40
|
||||
rd := m.RawData
|
||||
for len(rd)%4 != 0 {
|
||||
rd = append(rd, 0)
|
||||
}
|
||||
buf.Write(rd)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (m *ISCSICommand) noopInBytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.WriteByte(byte(OpNoopIn))
|
||||
var b byte
|
||||
b |= 0x80
|
||||
// byte 1
|
||||
buf.WriteByte(b)
|
||||
|
||||
b = 0
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(b)
|
||||
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||
// Skip through to byte 12
|
||||
for i := 0; i < 2*4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
for i := 0; i < 4; i++ {
|
||||
buf.WriteByte(0xff)
|
||||
}
|
||||
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
for i := 0; i < 3*4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
rd := m.RawData
|
||||
for len(rd)%4 != 0 {
|
||||
rd = append(rd, 0)
|
||||
}
|
||||
buf.Write(rd)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
125
pkg/port/iscsit/conn.go
Normal file
125
pkg/port/iscsit/conn.go
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright 2016 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 (
|
||||
"net"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
)
|
||||
|
||||
const (
|
||||
CONN_STATE_FREE = 0
|
||||
CONN_STATE_SECURITY = 1
|
||||
CONN_STATE_SECURITY_AUTH = 2
|
||||
CONN_STATE_SECURITY_DONE = 3
|
||||
CONN_STATE_SECURITY_LOGIN = 4
|
||||
CONN_STATE_SECURITY_FULL = 5
|
||||
CONN_STATE_LOGIN = 6
|
||||
CONN_STATE_LOGIN_FULL = 7
|
||||
CONN_STATE_FULL = 8
|
||||
CONN_STATE_KERNEL = 9
|
||||
CONN_STATE_CLOSE = 10
|
||||
CONN_STATE_EXIT = 11
|
||||
CONN_STATE_SCSI = 12
|
||||
CONN_STATE_INIT = 13
|
||||
CONN_STATE_START = 14
|
||||
CONN_STATE_READY = 15
|
||||
)
|
||||
|
||||
var (
|
||||
DATAIN byte = 0x01
|
||||
DATAOUT byte = 0x10
|
||||
)
|
||||
|
||||
type iscsiConnection struct {
|
||||
state int
|
||||
authState int
|
||||
session *ISCSISession
|
||||
sessionType int
|
||||
sessionParam []ISCSISessionParam
|
||||
tid int
|
||||
cid uint16
|
||||
rxIOState int
|
||||
txIOState int
|
||||
refcount int
|
||||
conn net.Conn
|
||||
initiator string
|
||||
initiatorAlias string
|
||||
|
||||
rxBuffer []byte
|
||||
txBuffer []byte
|
||||
req *ISCSICommand
|
||||
resp *ISCSICommand
|
||||
|
||||
// StatSN - the status sequence number on this connection
|
||||
statSN uint32
|
||||
// ExpStatSN - the expected status sequence number on this connection
|
||||
expStatSN uint32
|
||||
// CmdSN - the command sequence number at the target
|
||||
cmdSN uint32
|
||||
// ExpCmdSN - the next expected command sequence number at the target
|
||||
expCmdSN uint32
|
||||
// MaxCmdSN - the maximum CmdSN acceptable at the target from this initiator
|
||||
maxCmdSN uint32
|
||||
|
||||
rxTask *iscsiTask
|
||||
txTask *iscsiTask
|
||||
|
||||
authMethod AuthMethod
|
||||
}
|
||||
|
||||
type taskState int
|
||||
|
||||
const (
|
||||
taskPending taskState = 1
|
||||
taskSCSI taskState = 2
|
||||
)
|
||||
|
||||
type iscsiTask struct {
|
||||
tag uint32
|
||||
conn *iscsiConnection
|
||||
cmd *ISCSICommand
|
||||
scmd *api.SCSICommand
|
||||
state taskState
|
||||
}
|
||||
|
||||
func (c *iscsiConnection) init() {
|
||||
c.state = CONN_STATE_FREE
|
||||
c.refcount = 1
|
||||
c.sessionParam = []ISCSISessionParam{}
|
||||
for _, param := range sessionKeys {
|
||||
c.sessionParam = append(c.sessionParam, ISCSISessionParam{Value: param.def})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *iscsiConnection) readData(size int) ([]byte, int, error) {
|
||||
var buf = make([]byte, size)
|
||||
length, err := c.conn.Read(buf)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
return buf, length, nil
|
||||
}
|
||||
|
||||
func (c *iscsiConnection) write(resp []byte) (int, error) {
|
||||
return c.conn.Write(resp)
|
||||
}
|
||||
|
||||
func (c *iscsiConnection) close() {
|
||||
c.conn.Close()
|
||||
}
|
||||
592
pkg/port/iscsit/iscsid.go
Normal file
592
pkg/port/iscsit/iscsid.go
Normal file
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
Copyright 2016 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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/port"
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
type ISCSITargetService struct {
|
||||
SCSI *scsi.SCSITargetService
|
||||
Name string
|
||||
Targets map[string]*ISCSITarget
|
||||
}
|
||||
|
||||
func init() {
|
||||
port.RegisterTargetService("iscsi", NewISCSITargetService)
|
||||
}
|
||||
|
||||
func NewISCSITargetService(base *scsi.SCSITargetService) (port.SCSITargetService, error) {
|
||||
return &ISCSITargetService{
|
||||
Name: "iscsi",
|
||||
Targets: map[string]*ISCSITarget{},
|
||||
SCSI: base,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) NewTarget(target string) (port.SCSITargetDriver, error) {
|
||||
if _, ok := s.Targets[target]; ok {
|
||||
return nil, fmt.Errorf("target name has been existed")
|
||||
}
|
||||
stgt, err := s.SCSI.NewSCSITarget(len(s.Targets), "iscsi", target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tgt := newISCSITarget(stgt)
|
||||
s.Targets[target] = tgt
|
||||
|
||||
return tgt, nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) Run() error {
|
||||
l, err := net.Listen("tcp", ":3260")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
for {
|
||||
glog.Info("Listening ...")
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
glog.Info("Accepting ...")
|
||||
iscsiConn := &iscsiConnection{conn: conn}
|
||||
iscsiConn.init()
|
||||
iscsiConn.rxIOState = IOSTATE_RX_BHS
|
||||
|
||||
glog.Infof("connection is connected from %s...\n", conn.RemoteAddr().String())
|
||||
// start a new thread to do with this command
|
||||
go s.handler(DATAIN, iscsiConn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) handler(events byte, conn *iscsiConnection) {
|
||||
|
||||
if events&DATAIN != 0 {
|
||||
glog.Infof("rx handler processing...")
|
||||
go s.rxHandler(conn)
|
||||
}
|
||||
if conn.state != CONN_STATE_CLOSE && events&DATAOUT != 0 {
|
||||
glog.Infof("tx handler processing...")
|
||||
s.txHandler(conn)
|
||||
}
|
||||
if conn.state == CONN_STATE_CLOSE {
|
||||
glog.Infof("iscsi connection[%d] closed", conn.cid)
|
||||
conn.close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) rxHandler(conn *iscsiConnection) {
|
||||
var (
|
||||
hdigest uint = 0
|
||||
ddigest uint = 0
|
||||
final bool = false
|
||||
cmd *ISCSICommand
|
||||
)
|
||||
if conn.state == CONN_STATE_SCSI {
|
||||
hdigest = conn.sessionParam[ISCSI_PARAM_HDRDGST_EN].Value & DIGEST_CRC32C
|
||||
ddigest = conn.sessionParam[ISCSI_PARAM_DATADGST_EN].Value & DIGEST_CRC32C
|
||||
}
|
||||
for {
|
||||
switch conn.rxIOState {
|
||||
case IOSTATE_RX_BHS:
|
||||
glog.Infof("rx handler: IOSTATE_RX_BHS")
|
||||
buf, length, err := conn.readData(BHS_SIZE)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
if length == 0 {
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
return
|
||||
}
|
||||
conn.rxBuffer = buf
|
||||
cmd, err = parseHeader(buf)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
return
|
||||
}
|
||||
conn.req = cmd
|
||||
if length == BHS_SIZE && cmd.DataLen != 0 {
|
||||
conn.rxIOState = IOSTATE_RX_INIT_AHS
|
||||
break
|
||||
}
|
||||
glog.Infof("got command: \n%s", cmd.String())
|
||||
glog.Infof("got buffer: %v", buf)
|
||||
final = true
|
||||
case IOSTATE_RX_INIT_AHS:
|
||||
conn.rxIOState = IOSTATE_RX_DATA
|
||||
break
|
||||
if hdigest != 0 {
|
||||
conn.rxIOState = IOSTATE_RX_INIT_HDIGEST
|
||||
}
|
||||
case IOSTATE_RX_DATA:
|
||||
if ddigest != 0 {
|
||||
conn.rxIOState = IOSTATE_RX_INIT_DDIGEST
|
||||
}
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
dl := ((cmd.DataLen + DataPadding - 1) / DataPadding) * DataPadding
|
||||
buf, length, err := conn.readData(dl)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
if length != dl {
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
return
|
||||
}
|
||||
cmd.RawData = buf[:cmd.DataLen]
|
||||
conn.rxBuffer = append(conn.rxBuffer, buf...)
|
||||
final = true
|
||||
glog.Infof("got command: \n%s", cmd.String())
|
||||
default:
|
||||
glog.Errorf("error %d %d\n", conn.state, conn.rxIOState)
|
||||
return
|
||||
}
|
||||
|
||||
if final {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if conn.state == CONN_STATE_SCSI {
|
||||
s.scsiCommandHandler(conn)
|
||||
} else {
|
||||
conn.txIOState = IOSTATE_TX_BHS
|
||||
conn.resp = &ISCSICommand{}
|
||||
switch conn.req.OpCode {
|
||||
case OpLoginReq:
|
||||
glog.Infof("OpLoginReq")
|
||||
if err := s.iscsiExecLogin(conn); err != nil {
|
||||
glog.Error(err)
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
}
|
||||
case OpLogoutReq:
|
||||
glog.Infof("OpLogoutReq")
|
||||
if err := iscsiExecLogout(conn); err != nil {
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
}
|
||||
case OpTextReq:
|
||||
glog.Infof("OpTextReq")
|
||||
if err := s.iscsiExecText(conn); err != nil {
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
}
|
||||
default:
|
||||
iscsiExecReject(conn)
|
||||
}
|
||||
glog.Infof("%#v", conn.resp.String())
|
||||
s.handler(DATAOUT, conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
var (
|
||||
target *ISCSITarget
|
||||
cmd = conn.req
|
||||
)
|
||||
conn.resp = &ISCSICommand{
|
||||
OpCode: OpLoginResp,
|
||||
Transit: true,
|
||||
CSG: cmd.CSG,
|
||||
NSG: FullFeaturePhase,
|
||||
StatSN: cmd.ExpStatSN,
|
||||
TaskTag: cmd.TaskTag,
|
||||
ExpCmdSN: cmd.CmdSN,
|
||||
MaxCmdSN: cmd.CmdSN,
|
||||
RawData: util.MarshalKVText([]util.KeyValue{
|
||||
{"HeaderDigest", "None"},
|
||||
{"DataDigest", "None"},
|
||||
}),
|
||||
}
|
||||
pairs := util.ParseKVText(cmd.RawData)
|
||||
if initiatorName, ok := pairs["InitiatorName"]; ok {
|
||||
conn.initiator = initiatorName
|
||||
}
|
||||
if alias, ok := pairs["InitiatorAlias"]; ok {
|
||||
conn.initiatorAlias = alias
|
||||
}
|
||||
targetName := pairs["TargetName"]
|
||||
if sessType, ok := pairs["SessionType"]; ok {
|
||||
if sessType == "Normal" {
|
||||
conn.sessionType = SESSION_NORMAL
|
||||
} else {
|
||||
conn.sessionType = SESSION_DISCOVERY
|
||||
}
|
||||
}
|
||||
if conn.sessionType == SESSION_DISCOVERY {
|
||||
conn.tid = 0xffff
|
||||
} else {
|
||||
for _, t := range s.Targets {
|
||||
if t.SCSITarget.Name == targetName {
|
||||
target = t
|
||||
break
|
||||
}
|
||||
}
|
||||
if target == nil {
|
||||
conn.state = CONN_STATE_EXIT
|
||||
return fmt.Errorf("No target found with name(%s)", targetName)
|
||||
}
|
||||
conn.tid = target.TID
|
||||
}
|
||||
switch conn.state {
|
||||
case CONN_STATE_FREE:
|
||||
conn.state = CONN_STATE_SECURITY
|
||||
case CONN_STATE_SECURITY:
|
||||
}
|
||||
|
||||
conn.state = CONN_STATE_LOGIN_FULL
|
||||
conn.expCmdSN = cmd.CmdSN
|
||||
conn.statSN += 1
|
||||
|
||||
switch conn.sessionType {
|
||||
case SESSION_NORMAL:
|
||||
if conn.session == nil {
|
||||
// create a new session
|
||||
sess, err := s.NewISCSISession(conn)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
conn.session = sess
|
||||
}
|
||||
case SESSION_DISCOVERY:
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func iscsiExecLogout(conn *iscsiConnection) error {
|
||||
cmd := conn.req
|
||||
conn.resp = &ISCSICommand{
|
||||
OpCode: OpLogoutResp,
|
||||
StatSN: cmd.ExpStatSN,
|
||||
TaskTag: cmd.TaskTag,
|
||||
}
|
||||
if conn.session == nil {
|
||||
conn.resp.ExpCmdSN = cmd.CmdSN
|
||||
conn.resp.MaxCmdSN = cmd.CmdSN
|
||||
} else {
|
||||
conn.resp.ExpCmdSN = conn.session.ExpCmdSN
|
||||
conn.resp.MaxCmdSN = conn.session.ExpCmdSN + 10
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) iscsiExecText(conn *iscsiConnection) error {
|
||||
var result = []util.KeyValue{}
|
||||
cmd := conn.req
|
||||
keys := util.ParseKVText(cmd.RawData)
|
||||
if st, ok := keys["SendTargets"]; ok {
|
||||
if st == "All" {
|
||||
list, err := s.SCSI.GetTargetList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, t := range list {
|
||||
result = append(result, util.KeyValue{"TargetName", t.Name})
|
||||
result = append(result, util.KeyValue{"TargetAddress", "127.0.0.1:3260,1"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.resp = &ISCSICommand{
|
||||
OpCode: OpTextResp,
|
||||
Final: true,
|
||||
NSG: FullFeaturePhase,
|
||||
StatSN: cmd.ExpStatSN,
|
||||
TaskTag: cmd.TaskTag,
|
||||
ExpCmdSN: cmd.CmdSN,
|
||||
MaxCmdSN: cmd.CmdSN,
|
||||
}
|
||||
conn.resp.RawData = util.MarshalKVText(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func iscsiExecNoopOut(conn *iscsiConnection) error {
|
||||
cmd := conn.req
|
||||
conn.resp = &ISCSICommand{
|
||||
OpCode: OpNoopIn,
|
||||
Final: true,
|
||||
NSG: FullFeaturePhase,
|
||||
StatSN: cmd.ExpStatSN,
|
||||
TaskTag: cmd.TaskTag,
|
||||
ExpCmdSN: cmd.CmdSN + 1,
|
||||
MaxCmdSN: cmd.CmdSN + 10,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func iscsiExecReject(conn *iscsiConnection) error {
|
||||
conn.resp = &ISCSICommand{
|
||||
OpCode: OpReject,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) txHandler(conn *iscsiConnection) {
|
||||
var (
|
||||
hdigest uint = 0
|
||||
ddigest uint = 0
|
||||
final bool = false
|
||||
)
|
||||
if conn.state == CONN_STATE_SCSI {
|
||||
hdigest = conn.sessionParam[ISCSI_PARAM_HDRDGST_EN].Value & DIGEST_CRC32C
|
||||
ddigest = conn.sessionParam[ISCSI_PARAM_DATADGST_EN].Value & DIGEST_CRC32C
|
||||
}
|
||||
if conn.state == CONN_STATE_SCSI && conn.txTask == nil {
|
||||
err := s.scsiCommandHandler(conn)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
for {
|
||||
switch conn.txIOState {
|
||||
case IOSTATE_TX_BHS:
|
||||
glog.V(2).Infof("ready to write response")
|
||||
glog.V(2).Infof("%s", conn.resp.String())
|
||||
glog.V(2).Infof("length of RawData is %d", len(conn.resp.RawData))
|
||||
glog.V(2).Infof("length of resp is %d", len(conn.resp.Bytes()))
|
||||
if l, err := conn.write(conn.resp.Bytes()); err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
} else {
|
||||
conn.txIOState = IOSTATE_TX_INIT_AHS
|
||||
glog.V(2).Infof("success to write %d length", l)
|
||||
}
|
||||
case IOSTATE_TX_INIT_AHS:
|
||||
if hdigest != 0 {
|
||||
conn.txIOState = IOSTATE_TX_INIT_HDIGEST
|
||||
} else {
|
||||
conn.txIOState = IOSTATE_TX_INIT_DATA
|
||||
}
|
||||
if conn.txIOState != IOSTATE_TX_AHS {
|
||||
final = true
|
||||
}
|
||||
case IOSTATE_TX_AHS:
|
||||
case IOSTATE_TX_INIT_DATA:
|
||||
final = true
|
||||
case IOSTATE_TX_DATA:
|
||||
if ddigest != 0 {
|
||||
conn.txIOState = IOSTATE_TX_INIT_DDIGEST
|
||||
}
|
||||
default:
|
||||
glog.Errorf("error %d %d\n", conn.state, conn.txIOState)
|
||||
return
|
||||
}
|
||||
|
||||
if final {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch conn.state {
|
||||
case CONN_STATE_CLOSE, CONN_STATE_EXIT:
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
case CONN_STATE_SECURITY_LOGIN:
|
||||
conn.state = CONN_STATE_LOGIN
|
||||
glog.Infof("CONN_STATE_LOGIN")
|
||||
case CONN_STATE_SECURITY_FULL, CONN_STATE_LOGIN_FULL:
|
||||
if conn.sessionType == SESSION_NORMAL {
|
||||
conn.state = CONN_STATE_KERNEL
|
||||
glog.Infof("CONN_STATE_KERNEL")
|
||||
conn.state = CONN_STATE_SCSI
|
||||
glog.Infof("CONN_STATE_SCSI")
|
||||
} else {
|
||||
conn.state = CONN_STATE_FULL
|
||||
glog.Infof("CONN_STATE_FULL")
|
||||
}
|
||||
conn.rxIOState = IOSTATE_RX_BHS
|
||||
s.handler(DATAIN, conn)
|
||||
case CONN_STATE_SCSI:
|
||||
conn.txTask = nil
|
||||
default:
|
||||
conn.rxIOState = IOSTATE_RX_BHS
|
||||
s.handler(DATAIN, conn)
|
||||
}
|
||||
glog.Infof("%d", conn.state)
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err error) {
|
||||
req := conn.req
|
||||
switch req.OpCode {
|
||||
case OpSCSICmd:
|
||||
glog.Infof("SCSI Command processing...")
|
||||
scmd := &api.SCSICommand{}
|
||||
task := &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: scmd}
|
||||
conn.rxTask = task
|
||||
if err = s.iscsiTaskQueueHandler(task); err != nil {
|
||||
return
|
||||
} else {
|
||||
conn.rxTask = nil
|
||||
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}}
|
||||
conn.txIOState = IOSTATE_TX_BHS
|
||||
conn.statSN += 1
|
||||
resp := &ISCSICommand{
|
||||
Immediate: true,
|
||||
Final: true,
|
||||
StatSN: req.ExpStatSN,
|
||||
TaskTag: req.TaskTag,
|
||||
ExpCmdSN: conn.session.ExpCmdSN,
|
||||
MaxCmdSN: conn.session.ExpCmdSN + 10,
|
||||
Status: scmd.Result,
|
||||
SCSIResponse: 0x00,
|
||||
HasStatus: true,
|
||||
}
|
||||
switch scmd.Direction {
|
||||
case api.SCSIDataRead:
|
||||
resp.OpCode = OpSCSIIn
|
||||
if scmd.InSDBBuffer.Buffer != nil {
|
||||
buf := scmd.InSDBBuffer.Buffer.Bytes()
|
||||
resp.RawData = buf
|
||||
} else {
|
||||
resp.RawData = []byte{}
|
||||
}
|
||||
case api.SCSIDataWrite:
|
||||
resp.OpCode = OpSCSIResp
|
||||
if scmd.InSDBBuffer.Buffer != nil {
|
||||
buf := scmd.InSDBBuffer.Buffer.Bytes()
|
||||
resp.RawData = buf
|
||||
} else {
|
||||
resp.RawData = []byte{}
|
||||
}
|
||||
case api.SCSIDataBidirection:
|
||||
case api.SCSIDataNone:
|
||||
resp.OpCode = OpSCSIResp
|
||||
}
|
||||
conn.resp = resp
|
||||
}
|
||||
case OpSCSIOut:
|
||||
case OpNoopOut:
|
||||
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag}
|
||||
conn.txIOState = IOSTATE_TX_BHS
|
||||
iscsiExecNoopOut(conn)
|
||||
case OpLogoutReq:
|
||||
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag}
|
||||
conn.txIOState = IOSTATE_TX_BHS
|
||||
iscsiExecLogout(conn)
|
||||
case OpTextReq, OpSNACKReq:
|
||||
err = fmt.Errorf("Cannot handle yet %s", opCodeMap[conn.req.OpCode])
|
||||
glog.Error(err)
|
||||
return
|
||||
default:
|
||||
err = fmt.Errorf("Unknown op %s", opCodeMap[conn.req.OpCode])
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
conn.rxIOState = IOSTATE_RX_BHS
|
||||
s.handler(DATAIN|DATAOUT, conn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) iscsiTaskQueueHandler(task *iscsiTask) error {
|
||||
conn := task.conn
|
||||
sess := conn.session
|
||||
cmd := task.cmd
|
||||
if cmd.Immediate {
|
||||
return s.iscsiExecTask(task)
|
||||
}
|
||||
cmdsn := cmd.CmdSN
|
||||
glog.V(2).Infof("CmdSN of command is %d, ExpCmdSN of session is %d", cmdsn, sess.ExpCmdSN)
|
||||
if cmdsn == sess.ExpCmdSN {
|
||||
retry:
|
||||
cmdsn += 1
|
||||
sess.ExpCmdSN = cmdsn
|
||||
glog.Infof("session's ExpCmdSN is %d", cmdsn)
|
||||
|
||||
glog.Infof("process task(%d)", task.cmd.CmdSN)
|
||||
if err := s.iscsiExecTask(task); err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
if len(sess.PendingTasks) == 0 {
|
||||
return nil
|
||||
}
|
||||
task = sess.PendingTasks.Pop().(*iscsiTask)
|
||||
cmd = task.cmd
|
||||
if cmd.CmdSN != cmdsn {
|
||||
sess.PendingTasks.Push(task)
|
||||
return nil
|
||||
}
|
||||
task.state = taskSCSI
|
||||
goto retry
|
||||
} else {
|
||||
if cmd.CmdSN < sess.ExpCmdSN {
|
||||
err := fmt.Errorf("unexpected cmd serial number: (%d, %d)", cmd.CmdSN, sess.ExpCmdSN)
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
glog.Infof("add task(%d) into task queue", task.cmd.CmdSN)
|
||||
// add this connection into queue and set this task as pending task
|
||||
task.state = taskPending
|
||||
sess.PendingTasks.Push(task)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) iscsiExecTask(task *iscsiTask) error {
|
||||
cmd := task.cmd
|
||||
switch cmd.OpCode {
|
||||
case OpSCSICmd:
|
||||
if cmd.Read {
|
||||
if cmd.Write {
|
||||
task.scmd.Direction = api.SCSIDataBidirection
|
||||
} else {
|
||||
task.scmd.Direction = api.SCSIDataRead
|
||||
}
|
||||
} else {
|
||||
if cmd.Write {
|
||||
task.scmd.Direction = api.SCSIDataWrite
|
||||
}
|
||||
}
|
||||
task.scmd.CommandITNID = task.conn.session.Tsih
|
||||
task.scmd.SCB = bytes.NewBuffer(cmd.CDB)
|
||||
task.scmd.SCBLength = len(cmd.CDB)
|
||||
task.scmd.Lun = cmd.LUN
|
||||
task.scmd.Tag = uint64(cmd.TaskTag)
|
||||
task.state = taskSCSI
|
||||
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer(cmd.RawData)
|
||||
// add scsi target process queue
|
||||
err := s.SCSI.AddCommandQueue(task.conn.session.Target.SCSITarget.TID, task.scmd)
|
||||
if err != nil {
|
||||
task.state = 0
|
||||
}
|
||||
return err
|
||||
case OpLogoutReq:
|
||||
|
||||
case OpNoopOut:
|
||||
// just do it in iscsi layer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -17,11 +17,38 @@ limitations under the License.
|
||||
// iSCSI Target Driver
|
||||
package iscsit
|
||||
|
||||
import (
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
import "github.com/gostor/gotgt/pkg/api"
|
||||
|
||||
const (
|
||||
IOSTATE_FREE = iota
|
||||
|
||||
IOSTATE_RX_BHS
|
||||
IOSTATE_RX_INIT_AHS
|
||||
IOSTATE_RX_AHS
|
||||
IOSTATE_RX_INIT_HDIGEST
|
||||
IOSTATE_RX_HDIGEST
|
||||
IOSTATE_RX_CHECK_HDIGEST
|
||||
IOSTATE_RX_INIT_DATA
|
||||
IOSTATE_RX_DATA
|
||||
IOSTATE_RX_INIT_DDIGEST
|
||||
IOSTATE_RX_DDIGEST
|
||||
IOSTATE_RX_CHECK_DDIGEST
|
||||
IOSTATE_RX_END
|
||||
|
||||
IOSTATE_TX_BHS
|
||||
IOSTATE_TX_INIT_AHS
|
||||
IOSTATE_TX_AHS
|
||||
IOSTATE_TX_INIT_HDIGEST
|
||||
IOSTATE_TX_HDIGEST
|
||||
IOSTATE_TX_INIT_DATA
|
||||
IOSTATE_TX_DATA
|
||||
IOSTATE_TX_INIT_DDIGEST
|
||||
IOSTATE_TX_DDIGEST
|
||||
IOSTATE_TX_END
|
||||
)
|
||||
|
||||
var ISCSI_OPCODE_MASK = 0x3F
|
||||
|
||||
type ISCSIDiscoveryMethod string
|
||||
|
||||
var (
|
||||
@@ -38,7 +65,7 @@ type ISCSIRedirectInfo struct {
|
||||
}
|
||||
|
||||
type ISCSITarget struct {
|
||||
*api.SCSITarget
|
||||
api.SCSITarget
|
||||
api.SCSITargetDriverCommon
|
||||
|
||||
Sessions []*ISCSISession
|
||||
@@ -51,9 +78,9 @@ type ISCSITarget struct {
|
||||
NopCount int
|
||||
}
|
||||
|
||||
func NewISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
||||
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
||||
return &ISCSITarget{
|
||||
SCSITarget: target,
|
||||
SCSITarget: *target,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,96 +118,3 @@ func (tgt *ISCSITarget) GetLu(lun uint8) (uint64, 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 OpLogoutReq:
|
||||
resp = &ISCSICommand{
|
||||
OpCode: OpLogoutResp,
|
||||
StatSN: m.ExpStatSN,
|
||||
TaskTag: m.TaskTag,
|
||||
ExpCmdSN: m.CmdSN,
|
||||
MaxCmdSN: m.CmdSN,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package iscsit
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
func (m *ISCSICommand) loginRespBytes() []byte {
|
||||
// rfc7143 11.13
|
||||
@@ -20,20 +24,20 @@ func (m *ISCSICommand) loginRespBytes() []byte {
|
||||
buf.WriteByte(b)
|
||||
|
||||
b = 0
|
||||
buf.WriteByte(b) // version-max
|
||||
buf.WriteByte(b) // version-active
|
||||
buf.WriteByte(b) // ahsLen
|
||||
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // data segment length, no padding
|
||||
buf.Write(MarshalUint64(m.ISID)[2:])
|
||||
buf.Write(MarshalUint64(uint64(m.TSIH))[6:])
|
||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
buf.WriteByte(b) // version-max
|
||||
buf.WriteByte(b) // version-active
|
||||
buf.WriteByte(b) // ahsLen
|
||||
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // data segment length, no padding
|
||||
buf.Write(util.MarshalUint64(m.ISID)[2:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.TSIH))[6:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(b)
|
||||
buf.WriteByte(b) // "reserved"
|
||||
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
buf.WriteByte(byte(m.StatusClass))
|
||||
buf.WriteByte(byte(m.StatusDetail))
|
||||
buf.WriteByte(b)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package iscsit
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
func (m *ISCSICommand) logoutRespBytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
@@ -11,13 +15,13 @@ func (m *ISCSICommand) logoutRespBytes() []byte {
|
||||
for i := 4; i < 16; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||
for i := 20; i < 24; 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:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||
for i := 36; i < 48; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -16,13 +16,133 @@ limitations under the License.
|
||||
|
||||
package iscsit
|
||||
|
||||
import "crypto/rand"
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
SESSION_NORMAL int = 0
|
||||
SESSION_DISCOVERY int = 1
|
||||
)
|
||||
|
||||
var DIGEST_CRC32C uint = 1 << 1
|
||||
var DIGEST_NONE uint = 1 << 0
|
||||
var DIGEST_ALL uint = DIGEST_NONE | DIGEST_CRC32C
|
||||
var BHS_SIZE = 48
|
||||
|
||||
const (
|
||||
MAX_QUEUE_CMD_MIN = 1
|
||||
MAX_QUEUE_CMD_DEF = 128
|
||||
MAX_QUEUE_CMD_MAX = 512
|
||||
)
|
||||
|
||||
const (
|
||||
ISCSI_PARAM_MAX_RECV_DLENGTH = iota
|
||||
ISCSI_PARAM_HDRDGST_EN
|
||||
ISCSI_PARAM_DATADGST_EN
|
||||
ISCSI_PARAM_INITIAL_R2T_EN
|
||||
ISCSI_PARAM_MAX_R2T
|
||||
ISCSI_PARAM_IMM_DATA_EN
|
||||
ISCSI_PARAM_FIRST_BURST
|
||||
ISCSI_PARAM_MAX_BURST
|
||||
ISCSI_PARAM_PDU_INORDER_EN
|
||||
ISCSI_PARAM_DATASEQ_INORDER_EN
|
||||
ISCSI_PARAM_ERL
|
||||
ISCSI_PARAM_IFMARKER_EN
|
||||
ISCSI_PARAM_OFMARKER_EN
|
||||
ISCSI_PARAM_DEFAULTTIME2WAIT
|
||||
ISCSI_PARAM_DEFAULTTIME2RETAIN
|
||||
ISCSI_PARAM_OFMARKINT
|
||||
ISCSI_PARAM_IFMARKINT
|
||||
ISCSI_PARAM_MAXCONNECTIONS
|
||||
/* iSCSI Extensions for RDMA (RFC5046) */
|
||||
ISCSI_PARAM_RDMA_EXTENSIONS
|
||||
ISCSI_PARAM_TARGET_RDSL
|
||||
ISCSI_PARAM_INITIATOR_RDSL
|
||||
ISCSI_PARAM_MAX_OUTST_PDU
|
||||
/* "local" parmas, never sent to the initiator */
|
||||
ISCSI_PARAM_FIRST_LOCAL
|
||||
ISCSI_PARAM_MAX_XMIT_DLENGTH = ISCSI_PARAM_FIRST_LOCAL
|
||||
ISCSI_PARAM_MAX_QUEUE_CMD
|
||||
/* must always be last */
|
||||
ISCSI_PARAM_MAX
|
||||
)
|
||||
|
||||
type ISCSISessionParam struct {
|
||||
State int
|
||||
Value uint
|
||||
}
|
||||
|
||||
/*
|
||||
* The defaults here are according to the spec and must not be changed,
|
||||
* otherwise the initiator may make the wrong assumption. If you want
|
||||
* to change a value, edit the value in iscsi_target_create.
|
||||
*
|
||||
* The param MaxXmitDataSegmentLength doesn't really exist. It's a way
|
||||
* to remember the RDSL of the initiator, which defaults to 8k if he has
|
||||
* not told us otherwise.
|
||||
*/
|
||||
type iscsiSessionKeys struct {
|
||||
name string
|
||||
def uint
|
||||
min uint
|
||||
max uint
|
||||
}
|
||||
|
||||
var sessionKeys []iscsiSessionKeys = []iscsiSessionKeys{
|
||||
// ISCSI_PARAM_MAX_RECV_DLENGTH
|
||||
{"MaxRecvDataSegmentLength", 8192, 512, 16777215},
|
||||
// ISCSI_PARAM_HDRDGST_EN
|
||||
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
|
||||
// ISCSI_PARAM_DATADGST_EN
|
||||
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
|
||||
// ISCSI_PARAM_INITIAL_R2T_EN
|
||||
{"InitialR2T", 1, 0, 1},
|
||||
// ISCSI_PARAM_MAX_R2T
|
||||
{"MaxOutstandingR2T", 1, 1, 65535},
|
||||
// ISCSI_PARAM_IMM_DATA_EN
|
||||
{"ImmediateData", 1, 0, 1},
|
||||
// ISCSI_PARAM_FIRST_BURST
|
||||
{"FirstBurstLength", 65536, 512, 16777215},
|
||||
// ISCSI_PARAM_MAX_BURST
|
||||
{"MaxBurstLength", 262144, 512, 16777215},
|
||||
// ISCSI_PARAM_PDU_INORDER_EN
|
||||
{"DataPDUInOrder", 1, 0, 1},
|
||||
// ISCSI_PARAM_DATASEQ_INORDER_EN
|
||||
{"DataSequenceInOrder", 1, 0, 1},
|
||||
// ISCSI_PARAM_ERL
|
||||
{"ErrorRecoveryLevel", 0, 0, 2},
|
||||
// ISCSI_PARAM_IFMARKER_EN
|
||||
{"IFMarker", 0, 0, 1},
|
||||
// ISCSI_PARAM_OFMARKER_EN
|
||||
{"OFMarker", 0, 0, 1},
|
||||
// ISCSI_PARAM_DEFAULTTIME2WAIT
|
||||
{"DefaultTime2Wait", 2, 0, 3600},
|
||||
// ISCSI_PARAM_DEFAULTTIME2RETAIN
|
||||
{"DefaultTime2Retain", 20, 0, 3600},
|
||||
// ISCSI_PARAM_OFMARKINT
|
||||
{"OFMarkInt", 2048, 1, 65535},
|
||||
// ISCSI_PARAM_IFMARKINT
|
||||
{"IFMarkInt", 2048, 1, 65535},
|
||||
// ISCSI_PARAM_MAXCONNECTIONS
|
||||
{"MaxConnections", 1, 1, 65535},
|
||||
// ISCSI_PARAM_RDMA_EXTENSIONS
|
||||
{"RDMAExtensions", 0, 0, 1},
|
||||
// ISCSI_PARAM_TARGET_RDSL
|
||||
{"TargetRecvDataSegmentLength", 8192, 512, 16777215},
|
||||
// ISCSI_PARAM_INITIATOR_RDSL
|
||||
{"InitiatorRecvDataSegmentLength", 8192, 512, 16777215},
|
||||
// ISCSI_PARAM_MAX_OUTST_PDU
|
||||
{"MaxOutstandingUnexpectedPDUs", 0, 2, 4294967295},
|
||||
// "local" parmas, never sent to the initiator
|
||||
// ISCSI_PARAM_MAX_XMIT_DLENGTH
|
||||
{"MaxXmitDataSegmentLength", 8192, 512, 16777215},
|
||||
// ISCSI_PARAM_MAX_QUEUE_CMD
|
||||
{"MaxQueueCmd", MAX_QUEUE_CMD_DEF, MAX_QUEUE_CMD_MIN, MAX_QUEUE_CMD_MAX},
|
||||
}
|
||||
|
||||
// Session is an iSCSI session.
|
||||
type ISCSISession struct {
|
||||
Refcount int
|
||||
@@ -30,58 +150,112 @@ type ISCSISession struct {
|
||||
InitiatorAlias string
|
||||
Target *ISCSITarget
|
||||
Isid uint64
|
||||
Tsih uint16
|
||||
Tsih uint64
|
||||
|
||||
ExpCmdSN uint32
|
||||
// only one connection per session
|
||||
Connections []*ISCSIConnection
|
||||
Commands []*ISCSICommand
|
||||
PendingCommands []*ISCSICommand
|
||||
ExpectionCommandSN uint32
|
||||
MaxQueueCommand uint32
|
||||
SessionParam []ISCSISessionParam
|
||||
Info string
|
||||
Rdma int
|
||||
Connections []*iscsiConnection
|
||||
Commands []*ISCSICommand
|
||||
PendingTasks taskQueue
|
||||
MaxQueueCommand uint32
|
||||
SessionParam []ISCSISessionParam
|
||||
Info string
|
||||
Rdma int
|
||||
}
|
||||
|
||||
type ISCSIHeader struct {
|
||||
type taskQueue []*iscsiTask
|
||||
|
||||
func (tq taskQueue) Len() int { return len(tq) }
|
||||
|
||||
func (tq taskQueue) Less(i, j int) bool {
|
||||
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
|
||||
return tq[i].cmd.CmdSN > tq[j].cmd.CmdSN
|
||||
}
|
||||
|
||||
type ISCSIPdu struct {
|
||||
Bhs ISCSIHeader
|
||||
AhsSize uint
|
||||
DataSize uint
|
||||
func (tq taskQueue) Swap(i, j int) {
|
||||
tq[i], tq[j] = tq[j], tq[i]
|
||||
}
|
||||
|
||||
type ISCSIConnection struct {
|
||||
State int
|
||||
RxIostate int
|
||||
TxIostate int
|
||||
Refcount int
|
||||
func (tq *taskQueue) Push(x interface{}) {
|
||||
item := x.(*iscsiTask)
|
||||
*tq = append(*tq, item)
|
||||
}
|
||||
|
||||
Session *ISCSISession
|
||||
func (tq *taskQueue) Pop() interface{} {
|
||||
old := *tq
|
||||
n := len(old)
|
||||
item := old[n-1]
|
||||
*tq = old[0 : n-1]
|
||||
return item
|
||||
}
|
||||
|
||||
TID int
|
||||
CID int
|
||||
Auth AuthMethod
|
||||
StatSN uint32
|
||||
ExpectionStatSN uint32
|
||||
CommandSN uint32
|
||||
ExpectionCommandSN uint32
|
||||
MaxCommandSN uint32
|
||||
Request ISCSIPdu
|
||||
Response ISCSIPdu
|
||||
// The BHS is 48 bytes long. The Opcode and DataSegmentLength fields
|
||||
// appear in all iSCSI PDUs. In addition, when used, the Initiator Task
|
||||
// Tag and Logical Unit Number always appear in the same location in the
|
||||
// header.
|
||||
type iscsiHeader struct {
|
||||
opcode uint8
|
||||
flags uint8 // Final bit
|
||||
rsvd2 [2]uint8
|
||||
hlength uint8 // AHSs total length
|
||||
dlength [3]uint8 // Data length
|
||||
lun [8]uint8
|
||||
itt uint8 // Initiator Task Tag
|
||||
ttt uint8 // Target Task Tag
|
||||
statsn uint8
|
||||
expStatSN uint8
|
||||
maxStatSN uint8
|
||||
other [12]uint8
|
||||
}
|
||||
|
||||
type iscsiPdu struct {
|
||||
bhs iscsiHeader
|
||||
ahsSize uint
|
||||
dataSize uint
|
||||
}
|
||||
|
||||
// New creates a new session.
|
||||
func NewISCSISession() (*ISCSISession, error) {
|
||||
var tsih uint16
|
||||
b := make([]byte, 2)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tsih += uint16(b[0]) << 8
|
||||
tsih += uint16(b[1])
|
||||
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
|
||||
var (
|
||||
target *ISCSITarget
|
||||
tsih uint64
|
||||
)
|
||||
|
||||
return &ISCSISession{
|
||||
Tsih: tsih,
|
||||
}, nil
|
||||
for _, t := range s.Targets {
|
||||
if t.TID == conn.tid {
|
||||
target = t
|
||||
break
|
||||
}
|
||||
}
|
||||
if target == nil {
|
||||
return nil, fmt.Errorf("No target found with tid(%d)", conn.tid)
|
||||
}
|
||||
|
||||
for {
|
||||
rand.Seed(int64(time.Now().UTC().Nanosecond()))
|
||||
tsih = uint64(rand.Uint32())
|
||||
for _, s := range target.Sessions {
|
||||
if s.Tsih == tsih {
|
||||
tsih = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
if tsih != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sess := &ISCSISession{
|
||||
Tsih: tsih,
|
||||
Initiator: conn.initiator,
|
||||
InitiatorAlias: conn.initiatorAlias,
|
||||
Target: target,
|
||||
Connections: []*iscsiConnection{conn},
|
||||
SessionParam: conn.sessionParam,
|
||||
MaxQueueCommand: uint32(conn.sessionParam[ISCSI_PARAM_MAX_QUEUE_CMD].Value),
|
||||
Rdma: 0,
|
||||
ExpCmdSN: conn.expCmdSN,
|
||||
}
|
||||
conn.session = sess
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
47
pkg/port/service.go
Normal file
47
pkg/port/service.go
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 port
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
)
|
||||
|
||||
type SCSITargetService interface {
|
||||
Run() error
|
||||
NewTarget(string) (SCSITargetDriver, error)
|
||||
}
|
||||
|
||||
type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error)
|
||||
|
||||
var registeredPlugins = map[string](TargetServiceFunc){}
|
||||
|
||||
func RegisterTargetService(name string, f TargetServiceFunc) {
|
||||
registeredPlugins[name] = f
|
||||
}
|
||||
|
||||
func NewTargetService(name string, s *scsi.SCSITargetService) (SCSITargetService, error) {
|
||||
if name == "" {
|
||||
return nil, nil
|
||||
}
|
||||
f, ok := registeredPlugins[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("SCSI target driver %s is not found.", name)
|
||||
}
|
||||
return f(s)
|
||||
}
|
||||
Reference in New Issue
Block a user