Merge pull request #31 from orzhang/login_er
login negotiation and session/conn reinstatment
This commit is contained in:
@@ -81,7 +81,7 @@ type ISCSICommand struct {
|
|||||||
// Continue bit.
|
// Continue bit.
|
||||||
Cont bool
|
Cont bool
|
||||||
// Current Stage, Next Stage.
|
// Current Stage, Next Stage.
|
||||||
CSG, NSG Stage
|
CSG, NSG iSCSILoginStage
|
||||||
// Initiator part of the SSID.
|
// Initiator part of the SSID.
|
||||||
ISID uint64
|
ISID uint64
|
||||||
// Target-assigned Session Identifying Handle.
|
// Target-assigned Session Identifying Handle.
|
||||||
@@ -214,8 +214,8 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
|
|||||||
// rfc7143 11.12.2
|
// rfc7143 11.12.2
|
||||||
return nil, fmt.Errorf("transit and continue bits set in same login request")
|
return nil, fmt.Errorf("transit and continue bits set in same login request")
|
||||||
}
|
}
|
||||||
m.CSG = Stage(data[1]&0xc) >> 2
|
m.CSG = iSCSILoginStage(data[1]&0xc) >> 2
|
||||||
m.NSG = Stage(data[1] & 0x3)
|
m.NSG = iSCSILoginStage(data[1] & 0x3)
|
||||||
m.ISID = uint64(ParseUint(data[8:14]))
|
m.ISID = uint64(ParseUint(data[8:14]))
|
||||||
m.TSIH = uint16(ParseUint(data[14:16]))
|
m.TSIH = uint16(ParseUint(data[14:16]))
|
||||||
m.ConnID = uint16(ParseUint(data[20:22]))
|
m.ConnID = uint16(ParseUint(data[20:22]))
|
||||||
@@ -228,8 +228,8 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
|
|||||||
// rfc7143 11.12.2
|
// rfc7143 11.12.2
|
||||||
return nil, fmt.Errorf("transit and continue bits set in same login request")
|
return nil, fmt.Errorf("transit and continue bits set in same login request")
|
||||||
}
|
}
|
||||||
m.CSG = Stage(data[1]&0xc) >> 2
|
m.CSG = iSCSILoginStage(data[1]&0xc) >> 2
|
||||||
m.NSG = Stage(data[1] & 0x3)
|
m.NSG = iSCSILoginStage(data[1] & 0x3)
|
||||||
m.StatSN = uint32(ParseUint(data[24:28]))
|
m.StatSN = uint32(ParseUint(data[24:28]))
|
||||||
m.ExpCmdSN = uint32(ParseUint(data[28:32]))
|
m.ExpCmdSN = uint32(ParseUint(data[28:32]))
|
||||||
m.MaxCmdSN = uint32(ParseUint(data[32:36]))
|
m.MaxCmdSN = uint32(ParseUint(data[32:36]))
|
||||||
@@ -421,6 +421,7 @@ func (m *ISCSICommand) scsiTMFRespBytes() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *ISCSICommand) r2tRespBytes() []byte {
|
func (m *ISCSICommand) r2tRespBytes() []byte {
|
||||||
|
|
||||||
// rfc7143 11.8
|
// rfc7143 11.8
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
buf.WriteByte(byte(OpReady))
|
buf.WriteByte(byte(OpReady))
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package iscsit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
@@ -48,26 +49,23 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type iscsiConnection struct {
|
type iscsiConnection struct {
|
||||||
state int
|
state int
|
||||||
authState int
|
authState int
|
||||||
session *ISCSISession
|
session *ISCSISession
|
||||||
sessionType int
|
tid int
|
||||||
sessionParam []ISCSISessionParam
|
cid uint16
|
||||||
tid int
|
rxIOState int
|
||||||
CID uint16
|
txIOState int
|
||||||
rxIOState int
|
refcount int
|
||||||
txIOState int
|
conn net.Conn
|
||||||
refcount int
|
|
||||||
conn net.Conn
|
|
||||||
initiator string
|
|
||||||
initiatorAlias string
|
|
||||||
tpgt uint16
|
|
||||||
|
|
||||||
rxBuffer []byte
|
rxBuffer []byte
|
||||||
txBuffer []byte
|
txBuffer []byte
|
||||||
req *ISCSICommand
|
req *ISCSICommand
|
||||||
resp *ISCSICommand
|
resp *ISCSICommand
|
||||||
|
|
||||||
|
loginParam *iscsiLoginParam
|
||||||
|
|
||||||
// StatSN - the status sequence number on this connection
|
// StatSN - the status sequence number on this connection
|
||||||
statSN uint32
|
statSN uint32
|
||||||
// ExpStatSN - the expected status sequence number on this connection
|
// ExpStatSN - the expected status sequence number on this connection
|
||||||
@@ -82,8 +80,6 @@ type iscsiConnection struct {
|
|||||||
rxTask *iscsiTask
|
rxTask *iscsiTask
|
||||||
txTask *iscsiTask
|
txTask *iscsiTask
|
||||||
|
|
||||||
authMethod AuthMethod
|
|
||||||
|
|
||||||
readLock *sync.RWMutex
|
readLock *sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,10 +109,14 @@ func (c *iscsiConnection) init() {
|
|||||||
c.state = CONN_STATE_FREE
|
c.state = CONN_STATE_FREE
|
||||||
c.refcount = 1
|
c.refcount = 1
|
||||||
c.readLock = new(sync.RWMutex)
|
c.readLock = new(sync.RWMutex)
|
||||||
c.sessionParam = []ISCSISessionParam{}
|
c.loginParam.sessionParam = []ISCSISessionParam{}
|
||||||
|
c.loginParam.tgtCSG = LoginOperationalNegotiation
|
||||||
|
c.loginParam.tgtNSG = LoginOperationalNegotiation
|
||||||
for _, param := range sessionKeys {
|
for _, param := range sessionKeys {
|
||||||
c.sessionParam = append(c.sessionParam, ISCSISessionParam{Value: param.def})
|
c.loginParam.sessionParam = append(c.loginParam.sessionParam,
|
||||||
|
ISCSISessionParam{idx: param.idx, Value: param.def})
|
||||||
}
|
}
|
||||||
|
sort.Sort(c.loginParam.sessionParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *iscsiConnection) readData(size int) ([]byte, int, error) {
|
func (c *iscsiConnection) readData(size int) ([]byte, int, error) {
|
||||||
@@ -135,3 +135,8 @@ func (c *iscsiConnection) write(resp []byte) (int, error) {
|
|||||||
func (c *iscsiConnection) close() {
|
func (c *iscsiConnection) close() {
|
||||||
c.conn.Close()
|
c.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *iscsiConnection) ReInstatement(newConn *iscsiConnection) {
|
||||||
|
conn.close()
|
||||||
|
conn.conn = newConn.conn
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,19 +22,26 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
"github.com/gostor/gotgt/pkg/config"
|
"github.com/gostor/gotgt/pkg/config"
|
||||||
"github.com/gostor/gotgt/pkg/scsi"
|
"github.com/gostor/gotgt/pkg/scsi"
|
||||||
"github.com/gostor/gotgt/pkg/util"
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
"github.com/satori/go.uuid"
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ISCSI_MAX_TSIH = uint16(0xffff)
|
||||||
|
ISCSI_UNSPEC_TSIH = uint16(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
type ISCSITargetDriver struct {
|
type ISCSITargetDriver struct {
|
||||||
SCSI *scsi.SCSITargetService
|
SCSI *scsi.SCSITargetService
|
||||||
Name string
|
Name string
|
||||||
iSCSITargets map[string]*ISCSITarget
|
iSCSITargets map[string]*ISCSITarget
|
||||||
|
TSIHPool map[uint16]bool
|
||||||
|
TSIHPoolMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -46,9 +53,31 @@ func NewISCSITargetDriver(base *scsi.SCSITargetService) (scsi.SCSITargetDriver,
|
|||||||
Name: "iscsi",
|
Name: "iscsi",
|
||||||
iSCSITargets: map[string]*ISCSITarget{},
|
iSCSITargets: map[string]*ISCSITarget{},
|
||||||
SCSI: base,
|
SCSI: base,
|
||||||
|
TSIHPool: map[uint16]bool{0: true, 65535: true},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ISCSITargetDriver) AllocTSIH() uint16 {
|
||||||
|
var i uint16
|
||||||
|
s.TSIHPoolMutex.Lock()
|
||||||
|
for i = uint16(0); i < ISCSI_MAX_TSIH; i++ {
|
||||||
|
exist := s.TSIHPool[i]
|
||||||
|
if !exist {
|
||||||
|
s.TSIHPool[i] = true
|
||||||
|
s.TSIHPoolMutex.Unlock()
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.TSIHPoolMutex.Unlock()
|
||||||
|
return ISCSI_UNSPEC_TSIH
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ISCSITargetDriver) ReleaseTSIH(tsih uint16) {
|
||||||
|
s.TSIHPoolMutex.Lock()
|
||||||
|
delete(s.TSIHPool, tsih)
|
||||||
|
s.TSIHPoolMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ISCSITargetDriver) NewTarget(tgtName string, configInfo *config.Config) error {
|
func (s *ISCSITargetDriver) NewTarget(tgtName string, configInfo *config.Config) error {
|
||||||
if _, ok := s.iSCSITargets[tgtName]; ok {
|
if _, ok := s.iSCSITargets[tgtName]; ok {
|
||||||
return fmt.Errorf("target name has been existed")
|
return fmt.Errorf("target name has been existed")
|
||||||
@@ -138,7 +167,10 @@ func (s *ISCSITargetDriver) Run() error {
|
|||||||
}
|
}
|
||||||
log.Info(conn.LocalAddr().String())
|
log.Info(conn.LocalAddr().String())
|
||||||
log.Info("Accepting ...")
|
log.Info("Accepting ...")
|
||||||
iscsiConn := &iscsiConnection{conn: conn}
|
|
||||||
|
iscsiConn := &iscsiConnection{conn: conn,
|
||||||
|
loginParam: &iscsiLoginParam{}}
|
||||||
|
|
||||||
iscsiConn.init()
|
iscsiConn.init()
|
||||||
iscsiConn.rxIOState = IOSTATE_RX_BHS
|
iscsiConn.rxIOState = IOSTATE_RX_BHS
|
||||||
|
|
||||||
@@ -160,7 +192,7 @@ func (s *ISCSITargetDriver) handler(events byte, conn *iscsiConnection) {
|
|||||||
s.txHandler(conn)
|
s.txHandler(conn)
|
||||||
}
|
}
|
||||||
if conn.state == CONN_STATE_CLOSE {
|
if conn.state == CONN_STATE_CLOSE {
|
||||||
log.Warningf("iscsi connection[%d] closed", conn.CID)
|
log.Warningf("iscsi connection[%d] closed", conn.cid)
|
||||||
conn.close()
|
conn.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,8 +207,8 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) {
|
|||||||
conn.readLock.Lock()
|
conn.readLock.Lock()
|
||||||
defer conn.readLock.Unlock()
|
defer conn.readLock.Unlock()
|
||||||
if conn.state == CONN_STATE_SCSI {
|
if conn.state == CONN_STATE_SCSI {
|
||||||
hdigest = conn.sessionParam[ISCSI_PARAM_HDRDGST_EN].Value & DIGEST_CRC32C
|
hdigest = conn.loginParam.sessionParam[ISCSI_PARAM_HDRDGST_EN].Value & DIGEST_CRC32C
|
||||||
ddigest = conn.sessionParam[ISCSI_PARAM_DATADGST_EN].Value & DIGEST_CRC32C
|
ddigest = conn.loginParam.sessionParam[ISCSI_PARAM_DATADGST_EN].Value & DIGEST_CRC32C
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
switch conn.rxIOState {
|
switch conn.rxIOState {
|
||||||
@@ -289,101 +321,62 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) {
|
|||||||
|
|
||||||
func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error {
|
func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error {
|
||||||
var (
|
var (
|
||||||
target *ISCSITarget
|
cmd = conn.req
|
||||||
cmd = conn.req
|
err error
|
||||||
TPGT uint16
|
negoKeys []util.KeyValue
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
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"},
|
|
||||||
{"ImmediateData", "Yes"},
|
|
||||||
{"InitialR2T", "Yes"},
|
|
||||||
{"MaxBurstLength", "262144"},
|
|
||||||
{"FirstBurstLength", "65536"},
|
|
||||||
{"DefaultTime2Wait", "2"},
|
|
||||||
{"DefaultTime2Retain", "0"},
|
|
||||||
{"MaxOutstandingR2T", "1"},
|
|
||||||
{"IFMarker", "No"},
|
|
||||||
{"OFMarker", "No"},
|
|
||||||
{"DataPDUInOrder", "Yes"},
|
|
||||||
{"DataSequenceInOrder", "Yes"},
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
conn.CID = cmd.ConnID
|
|
||||||
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.iSCSITargets {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
TPGT, err = target.FindTPG(conn.conn.LocalAddr().String())
|
conn.cid = cmd.ConnID
|
||||||
|
conn.loginParam.iniCSG = cmd.CSG
|
||||||
|
conn.loginParam.iniNSG = cmd.NSG
|
||||||
|
conn.loginParam.iniCont = cmd.Cont
|
||||||
|
conn.loginParam.iniTrans = cmd.Transit
|
||||||
|
conn.loginParam.isid = cmd.ISID
|
||||||
|
conn.loginParam.tsih = cmd.TSIH
|
||||||
|
conn.expCmdSN = cmd.CmdSN
|
||||||
|
conn.statSN += 1
|
||||||
|
|
||||||
|
if conn.loginParam.iniCSG == SecurityNegotiation {
|
||||||
|
conn.state = CONN_STATE_EXIT
|
||||||
|
return fmt.Errorf("Doesn't support Auth")
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs := util.ParseKVText(cmd.RawData)
|
||||||
|
|
||||||
|
negoKeys, err = loginKVProcess(conn, pairs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !conn.loginParam.keyDeclared {
|
||||||
|
negoKeys = loginKVDeclare(conn, negoKeys)
|
||||||
|
conn.loginParam.keyDeclared = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !conn.loginParam.paramInit {
|
||||||
|
err = s.BindISCSISession(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.state = CONN_STATE_EXIT
|
conn.state = CONN_STATE_EXIT
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conn.tpgt = TPGT
|
conn.loginParam.paramInit = true
|
||||||
conn.tid = target.TID
|
|
||||||
|
|
||||||
}
|
}
|
||||||
switch conn.state {
|
if conn.loginParam.tgtNSG == FullFeaturePhase &&
|
||||||
case CONN_STATE_FREE:
|
conn.loginParam.tgtTrans {
|
||||||
conn.state = CONN_STATE_SECURITY
|
conn.state = CONN_STATE_LOGIN_FULL
|
||||||
case CONN_STATE_SECURITY:
|
} else {
|
||||||
|
conn.state = CONN_STATE_LOGIN
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.state = CONN_STATE_LOGIN_FULL
|
conn.resp = &ISCSICommand{
|
||||||
conn.expCmdSN = cmd.CmdSN
|
OpCode: OpLoginResp,
|
||||||
conn.statSN += 1
|
Transit: conn.loginParam.tgtTrans,
|
||||||
|
CSG: cmd.CSG,
|
||||||
switch conn.sessionType {
|
NSG: conn.loginParam.tgtNSG,
|
||||||
case SESSION_NORMAL:
|
StatSN: cmd.ExpStatSN,
|
||||||
if conn.session == nil {
|
TaskTag: cmd.TaskTag,
|
||||||
// create a new session
|
ExpCmdSN: cmd.CmdSN,
|
||||||
sess, err := s.NewISCSISession(conn, cmd.ISID)
|
MaxCmdSN: cmd.CmdSN,
|
||||||
if err != nil {
|
RawData: util.MarshalKVText(negoKeys),
|
||||||
log.Error(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(sess)}
|
|
||||||
scsi.AddITNexus(&sess.Target.SCSITarget, itnexus)
|
|
||||||
sess.ITNexusID = itnexus.ID
|
|
||||||
conn.session = sess
|
|
||||||
}
|
|
||||||
case SESSION_DISCOVERY:
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -476,14 +469,13 @@ func iscsiExecReject(conn *iscsiConnection) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func iscsiExecR2T(conn *iscsiConnection) error {
|
func iscsiExecR2T(conn *iscsiConnection) error {
|
||||||
|
var val uint
|
||||||
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}}
|
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}}
|
||||||
conn.txIOState = IOSTATE_TX_BHS
|
conn.txIOState = IOSTATE_TX_BHS
|
||||||
conn.statSN += 1
|
conn.statSN += 1
|
||||||
task := conn.rxTask
|
task := conn.rxTask
|
||||||
resp := &ISCSICommand{
|
resp := &ISCSICommand{
|
||||||
OpCode: OpReady,
|
OpCode: OpReady,
|
||||||
Immediate: true,
|
|
||||||
Final: true,
|
|
||||||
StatSN: conn.req.ExpStatSN,
|
StatSN: conn.req.ExpStatSN,
|
||||||
TaskTag: conn.req.TaskTag,
|
TaskTag: conn.req.TaskTag,
|
||||||
ExpCmdSN: conn.session.ExpCmdSN,
|
ExpCmdSN: conn.session.ExpCmdSN,
|
||||||
@@ -492,7 +484,8 @@ func iscsiExecR2T(conn *iscsiConnection) error {
|
|||||||
BufferOffset: uint32(task.offset),
|
BufferOffset: uint32(task.offset),
|
||||||
DesiredLength: uint32(task.r2tCount),
|
DesiredLength: uint32(task.r2tCount),
|
||||||
}
|
}
|
||||||
if val := sessionKeys[ISCSI_PARAM_MAX_BURST].def; task.r2tCount > int(val) {
|
|
||||||
|
if val = conn.loginParam.sessionParam[ISCSI_PARAM_MAX_BURST].Value; task.r2tCount > int(val) {
|
||||||
resp.DesiredLength = uint32(val)
|
resp.DesiredLength = uint32(val)
|
||||||
}
|
}
|
||||||
conn.resp = resp
|
conn.resp = resp
|
||||||
@@ -506,8 +499,8 @@ func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) {
|
|||||||
final bool = false
|
final bool = false
|
||||||
)
|
)
|
||||||
if conn.state == CONN_STATE_SCSI {
|
if conn.state == CONN_STATE_SCSI {
|
||||||
hdigest = conn.sessionParam[ISCSI_PARAM_HDRDGST_EN].Value & DIGEST_CRC32C
|
hdigest = conn.loginParam.sessionParam[ISCSI_PARAM_HDRDGST_EN].Value & DIGEST_CRC32C
|
||||||
ddigest = conn.sessionParam[ISCSI_PARAM_DATADGST_EN].Value & DIGEST_CRC32C
|
ddigest = conn.loginParam.sessionParam[ISCSI_PARAM_DATADGST_EN].Value & DIGEST_CRC32C
|
||||||
}
|
}
|
||||||
if conn.state == CONN_STATE_SCSI && conn.txTask == nil {
|
if conn.state == CONN_STATE_SCSI && conn.txTask == nil {
|
||||||
err := s.scsiCommandHandler(conn)
|
err := s.scsiCommandHandler(conn)
|
||||||
@@ -564,10 +557,14 @@ func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) {
|
|||||||
case CONN_STATE_SECURITY_LOGIN:
|
case CONN_STATE_SECURITY_LOGIN:
|
||||||
conn.state = CONN_STATE_LOGIN
|
conn.state = CONN_STATE_LOGIN
|
||||||
log.Debugf("CONN_STATE_LOGIN")
|
log.Debugf("CONN_STATE_LOGIN")
|
||||||
|
case CONN_STATE_LOGIN:
|
||||||
|
log.Debugf("CONN_STATE_LOGIN")
|
||||||
|
conn.rxIOState = IOSTATE_RX_BHS
|
||||||
|
s.handler(DATAIN, conn)
|
||||||
case CONN_STATE_SECURITY_FULL, CONN_STATE_LOGIN_FULL:
|
case CONN_STATE_SECURITY_FULL, CONN_STATE_LOGIN_FULL:
|
||||||
if conn.sessionType == SESSION_NORMAL {
|
if conn.session.SessionType == SESSION_NORMAL {
|
||||||
conn.state = CONN_STATE_KERNEL
|
conn.state = CONN_STATE_KERNEL
|
||||||
log.Infof("CONN_STATE_KERNEL")
|
log.Debugf("CONN_STATE_KERNEL")
|
||||||
conn.state = CONN_STATE_SCSI
|
conn.state = CONN_STATE_SCSI
|
||||||
log.Debugf("CONN_STATE_SCSI")
|
log.Debugf("CONN_STATE_SCSI")
|
||||||
} else {
|
} else {
|
||||||
@@ -583,7 +580,6 @@ func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) {
|
|||||||
conn.rxIOState = IOSTATE_RX_BHS
|
conn.rxIOState = IOSTATE_RX_BHS
|
||||||
s.handler(DATAIN, conn)
|
s.handler(DATAIN, conn)
|
||||||
}
|
}
|
||||||
log.Infof("%d", conn.state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error) {
|
func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error) {
|
||||||
@@ -604,15 +600,24 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error
|
|||||||
if task.scmd.OutSDBBuffer.Buffer == nil {
|
if task.scmd.OutSDBBuffer.Buffer == nil {
|
||||||
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer([]byte{})
|
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer([]byte{})
|
||||||
}
|
}
|
||||||
task.scmd.OutSDBBuffer.Buffer.Write(conn.req.RawData)
|
if conn.session.SessionParam[ISCSI_PARAM_IMM_DATA_EN].Value == 1 {
|
||||||
|
task.scmd.OutSDBBuffer.Buffer.Write(conn.req.RawData)
|
||||||
|
}
|
||||||
if task.r2tCount > 0 {
|
if task.r2tCount > 0 {
|
||||||
// prepare to receive more data
|
// prepare to receive more data
|
||||||
conn.session.ExpCmdSN += 1
|
conn.session.ExpCmdSN += 1
|
||||||
task.state = taskPending
|
task.state = taskPending
|
||||||
conn.session.PendingTasks.Push(task)
|
conn.session.PendingTasks.Push(task)
|
||||||
conn.rxTask = task
|
conn.rxTask = task
|
||||||
iscsiExecR2T(conn)
|
if conn.session.SessionParam[ISCSI_PARAM_INITIAL_R2T_EN].Value == 1 {
|
||||||
break
|
iscsiExecR2T(conn)
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
log.Debugf("Not ready to exec the task")
|
||||||
|
conn.rxIOState = IOSTATE_RX_BHS
|
||||||
|
s.handler(DATAIN, conn)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
task.offset = 0
|
task.offset = 0
|
||||||
@@ -691,7 +696,12 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error
|
|||||||
return nil
|
return nil
|
||||||
} else if task.r2tCount > 0 {
|
} else if task.r2tCount > 0 {
|
||||||
// prepare to receive more data
|
// prepare to receive more data
|
||||||
task.r2tSN += 1
|
if task.unsolCount == 0 {
|
||||||
|
task.r2tSN += 1
|
||||||
|
} else {
|
||||||
|
task.r2tSN = 0
|
||||||
|
task.unsolCount = 0
|
||||||
|
}
|
||||||
conn.rxTask = task
|
conn.rxTask = task
|
||||||
iscsiExecR2T(conn)
|
iscsiExecR2T(conn)
|
||||||
break
|
break
|
||||||
@@ -803,12 +813,12 @@ func (s *ISCSITargetDriver) iscsiExecTask(task *iscsiTask) error {
|
|||||||
task.scmd.Direction = api.SCSIDataWrite
|
task.scmd.Direction = api.SCSIDataWrite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
task.scmd.ITNexusID = task.conn.session.ITNexusID
|
task.scmd.ITNexusID = task.conn.session.ITNexus.ID
|
||||||
task.scmd.SCB = bytes.NewBuffer(cmd.CDB)
|
task.scmd.SCB = bytes.NewBuffer(cmd.CDB)
|
||||||
task.scmd.SCBLength = len(cmd.CDB)
|
task.scmd.SCBLength = len(cmd.CDB)
|
||||||
task.scmd.Lun = cmd.LUN
|
task.scmd.Lun = cmd.LUN
|
||||||
task.scmd.Tag = uint64(cmd.TaskTag)
|
task.scmd.Tag = uint64(cmd.TaskTag)
|
||||||
task.scmd.RelTargetPortID = task.conn.tpgt
|
task.scmd.RelTargetPortID = task.conn.session.TPGT
|
||||||
task.state = taskSCSI
|
task.state = taskSCSI
|
||||||
if task.scmd.OutSDBBuffer.Buffer == nil {
|
if task.scmd.OutSDBBuffer.Buffer == nil {
|
||||||
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer(cmd.RawData)
|
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer(cmd.RawData)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package iscsit
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
)
|
)
|
||||||
@@ -81,15 +82,14 @@ type ISCSITarget struct {
|
|||||||
// TPGT number is the key
|
// TPGT number is the key
|
||||||
TPGTs map[uint16]*iSCSITPGT
|
TPGTs map[uint16]*iSCSITPGT
|
||||||
// TSIH is the key
|
// TSIH is the key
|
||||||
Sessions map[uint16]*ISCSISession
|
Sessions map[uint16]*ISCSISession
|
||||||
SessionParam []ISCSISessionParam
|
SessionsRWMutex sync.RWMutex
|
||||||
Alias string
|
Alias string
|
||||||
MaxSessions int
|
MaxSessions int
|
||||||
RedirectInfo ISCSIRedirectInfo
|
RedirectInfo ISCSIRedirectInfo
|
||||||
Rdma int
|
Rdma int
|
||||||
NopInterval int
|
NopInterval int
|
||||||
NopCount int
|
NopCount int
|
||||||
TSIHCounter uint16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -121,10 +121,9 @@ func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
|||||||
|
|
||||||
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
||||||
return &ISCSITarget{
|
return &ISCSITarget{
|
||||||
SCSITarget: *target,
|
SCSITarget: *target,
|
||||||
TPGTs: make(map[uint16]*iSCSITPGT),
|
TPGTs: make(map[uint16]*iSCSITPGT),
|
||||||
Sessions: make(map[uint16]*ISCSISession),
|
Sessions: make(map[uint16]*ISCSISession),
|
||||||
TSIHCounter: 1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,174 @@ package iscsit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
// log "github.com/Sirupsen/logrus"
|
||||||
"github.com/gostor/gotgt/pkg/util"
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
iSCSILoginParamTextKV = []util.KeyValue{
|
||||||
|
{"HeaderDigest", "None"},
|
||||||
|
{"DataDigest", "None"},
|
||||||
|
{"ImmediateData", "Yes"},
|
||||||
|
{"InitialR2T", "Yes"},
|
||||||
|
{"MaxBurstLength", "262144"},
|
||||||
|
{"FirstBurstLength", "65536"},
|
||||||
|
{"DefaultTime2Wait", "2"},
|
||||||
|
{"DefaultTime2Retain", "0"},
|
||||||
|
{"MaxOutstandingR2T", "1"},
|
||||||
|
{"IFMarker", "No"},
|
||||||
|
{"OFMarker", "No"},
|
||||||
|
{"DataPDUInOrder", "Yes"},
|
||||||
|
{"DataSequenceInOrder", "Yes"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
type iSCSILoginStage int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SecurityNegotiation iSCSILoginStage = 0
|
||||||
|
LoginOperationalNegotiation = 1
|
||||||
|
FullFeaturePhase = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s iSCSILoginStage) String() string {
|
||||||
|
switch s {
|
||||||
|
case SecurityNegotiation:
|
||||||
|
return "Security Negotiation"
|
||||||
|
case LoginOperationalNegotiation:
|
||||||
|
return "Login Operational Negotiation"
|
||||||
|
case FullFeaturePhase:
|
||||||
|
return "Full Feature Phase"
|
||||||
|
}
|
||||||
|
return "Unknown Stage"
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginKVDeclare(conn *iscsiConnection, negoKV []util.KeyValue) []util.KeyValue {
|
||||||
|
|
||||||
|
negoKV = append(negoKV, util.KeyValue{"TargetPortalGroupTag",
|
||||||
|
numberKeyInConv(uint(conn.loginParam.tpgt))})
|
||||||
|
negoKV = append(negoKV, util.KeyValue{"MaxRecvDataSegmentLength",
|
||||||
|
numberKeyInConv(sessionKeys["MaxRecvDataSegmentLength"].def)})
|
||||||
|
return negoKV
|
||||||
|
|
||||||
|
}
|
||||||
|
func loginKVProcess(conn *iscsiConnection, loginKV map[string]string) ([]util.KeyValue, error) {
|
||||||
|
var (
|
||||||
|
uintVal uint
|
||||||
|
ok bool
|
||||||
|
defSessKey *iscsiSessionKeys
|
||||||
|
negoKV []util.KeyValue
|
||||||
|
kvChanges int
|
||||||
|
)
|
||||||
|
|
||||||
|
for key, val := range loginKV {
|
||||||
|
// The MaxRecvDataSegmentLength of initiator
|
||||||
|
// is the MaxXmitDataSegmentLength of target
|
||||||
|
if key == "MaxRecvDataSegmentLength" {
|
||||||
|
defSessKey, ok = sessionKeys["MaxXmitDataSegmentLength"]
|
||||||
|
uintVal, ok = defSessKey.conv(val)
|
||||||
|
conn.loginParam.sessionParam[defSessKey.idx].Value = uintVal
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == "InitiatorName" {
|
||||||
|
conn.loginParam.initiator = val
|
||||||
|
continue
|
||||||
|
} else if key == "InitiatorAlias" {
|
||||||
|
conn.loginParam.initiatorAlias = val
|
||||||
|
continue
|
||||||
|
} else if key == "TargetName" {
|
||||||
|
conn.loginParam.target = val
|
||||||
|
continue
|
||||||
|
} else if key == "SessionType" {
|
||||||
|
if val == "Normal" {
|
||||||
|
conn.loginParam.sessionType = SESSION_NORMAL
|
||||||
|
} else {
|
||||||
|
conn.loginParam.sessionType = SESSION_DISCOVERY
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
defSessKey, ok = sessionKeys[key]
|
||||||
|
if ok {
|
||||||
|
uintVal, ok = defSessKey.conv(val)
|
||||||
|
|
||||||
|
//hack here
|
||||||
|
if key == "HeaderDigest" || key == "DataDigest" {
|
||||||
|
if uintVal == DIGEST_ALL {
|
||||||
|
uintVal = DIGEST_NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
if defSessKey.constValue {
|
||||||
|
//the Negotiation Key cannot be changed! Uses Target default key
|
||||||
|
if uintVal != defSessKey.def {
|
||||||
|
kvChanges++
|
||||||
|
}
|
||||||
|
negoKV = append(negoKV, util.KeyValue{key, defSessKey.inConv(defSessKey.def)})
|
||||||
|
} else {
|
||||||
|
if (uintVal >= defSessKey.min) && (uintVal <= defSessKey.max) {
|
||||||
|
conn.loginParam.sessionParam[defSessKey.idx].Value = uintVal
|
||||||
|
negoKV = append(negoKV, util.KeyValue{key, defSessKey.inConv(uintVal)})
|
||||||
|
} else {
|
||||||
|
// the value out of the acceptable range, Uses target default key
|
||||||
|
negoKV = append(negoKV, util.KeyValue{key, defSessKey.inConv(defSessKey.def)})
|
||||||
|
kvChanges++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Unknown Key, reject it
|
||||||
|
return negoKV, fmt.Errorf("Unknowen Nego KV [%s:%s]", key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if kvChanges == 0 {
|
||||||
|
if (conn.loginParam.iniNSG == FullFeaturePhase) && conn.loginParam.iniTrans {
|
||||||
|
conn.loginParam.tgtNSG = FullFeaturePhase
|
||||||
|
conn.loginParam.tgtTrans = true
|
||||||
|
} else {
|
||||||
|
//Currently, we just reject these kind of cases
|
||||||
|
return negoKV, fmt.Errorf("reject CSG=%d,NSG=%d,trans",
|
||||||
|
conn.loginParam.iniCSG, conn.loginParam.iniNSG, conn.loginParam.iniTrans)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conn.loginParam.tgtNSG = LoginOperationalNegotiation
|
||||||
|
conn.loginParam.tgtTrans = false
|
||||||
|
}
|
||||||
|
return negoKV, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type iscsiLoginParam struct {
|
||||||
|
paramInit bool
|
||||||
|
|
||||||
|
iniCSG iSCSILoginStage
|
||||||
|
iniNSG iSCSILoginStage
|
||||||
|
iniTrans bool
|
||||||
|
iniCont bool
|
||||||
|
|
||||||
|
tgtCSG iSCSILoginStage
|
||||||
|
tgtNSG iSCSILoginStage
|
||||||
|
tgtTrans bool
|
||||||
|
tgtCont bool
|
||||||
|
|
||||||
|
sessionType int
|
||||||
|
sessionParam ISCSISessionParamList
|
||||||
|
keyDeclared bool
|
||||||
|
|
||||||
|
initiator string
|
||||||
|
initiatorAlias string
|
||||||
|
target string
|
||||||
|
targetAlias string
|
||||||
|
|
||||||
|
tpgt uint16
|
||||||
|
isid uint64
|
||||||
|
tsih uint16
|
||||||
|
|
||||||
|
authMethod AuthMethod
|
||||||
|
}
|
||||||
|
|
||||||
func (m *ISCSICommand) loginRespBytes() []byte {
|
func (m *ISCSICommand) loginRespBytes() []byte {
|
||||||
// rfc7143 11.13
|
// rfc7143 11.13
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
@@ -51,23 +215,3 @@ func (m *ISCSICommand) loginRespBytes() []byte {
|
|||||||
buf.Write(rd)
|
buf.Write(rd)
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stage int
|
|
||||||
|
|
||||||
const (
|
|
||||||
SecurityNegotiation Stage = 0
|
|
||||||
LoginOperationalNegotiation = 1
|
|
||||||
FullFeaturePhase = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s Stage) String() string {
|
|
||||||
switch s {
|
|
||||||
case SecurityNegotiation:
|
|
||||||
return "Security Negotiation"
|
|
||||||
case LoginOperationalNegotiation:
|
|
||||||
return "Login Operational Negotiation"
|
|
||||||
case FullFeaturePhase:
|
|
||||||
return "Full Feature Phase"
|
|
||||||
}
|
|
||||||
return "Unknown Stage"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,9 +18,13 @@ package iscsit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"strconv"
|
||||||
"time"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
"github.com/gostor/gotgt/pkg/scsi"
|
||||||
"github.com/satori/go.uuid"
|
"github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -73,9 +77,27 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ISCSISessionParam struct {
|
type ISCSISessionParam struct {
|
||||||
|
idx uint
|
||||||
State int
|
State int
|
||||||
Value uint
|
Value uint
|
||||||
}
|
}
|
||||||
|
type ISCSISessionParamList []ISCSISessionParam
|
||||||
|
|
||||||
|
func (list ISCSISessionParamList) Len() int {
|
||||||
|
return len(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (list ISCSISessionParamList) Less(i, j int) bool {
|
||||||
|
if list[i].idx <= list[j].idx {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (list ISCSISessionParamList) Swap(i, j int) {
|
||||||
|
list[i], list[j] = list[j], list[i]
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The defaults here are according to the spec and must not be changed,
|
* The defaults here are according to the spec and must not be changed,
|
||||||
@@ -86,63 +108,130 @@ type ISCSISessionParam struct {
|
|||||||
* to remember the RDSL of the initiator, which defaults to 8k if he has
|
* to remember the RDSL of the initiator, which defaults to 8k if he has
|
||||||
* not told us otherwise.
|
* not told us otherwise.
|
||||||
*/
|
*/
|
||||||
|
type KeyConvFunc func(value string) (uint, bool)
|
||||||
|
type KeyInConvFunc func(value uint) string
|
||||||
|
|
||||||
type iscsiSessionKeys struct {
|
type iscsiSessionKeys struct {
|
||||||
name string
|
idx uint
|
||||||
def uint
|
constValue bool
|
||||||
min uint
|
def uint
|
||||||
max uint
|
min uint
|
||||||
|
max uint
|
||||||
|
conv KeyConvFunc
|
||||||
|
inConv KeyInConvFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
var sessionKeys []iscsiSessionKeys = []iscsiSessionKeys{
|
func digestKeyConv(value string) (uint, bool) {
|
||||||
|
var crc uint
|
||||||
|
valueArray := strings.Split(value, ",")
|
||||||
|
if len(valueArray) == 0 {
|
||||||
|
return crc, false
|
||||||
|
}
|
||||||
|
for _, tmpV := range valueArray {
|
||||||
|
if strings.EqualFold(tmpV, "crc32c") {
|
||||||
|
crc |= DIGEST_CRC32C
|
||||||
|
} else if strings.EqualFold(tmpV, "none") {
|
||||||
|
crc |= DIGEST_NONE
|
||||||
|
} else {
|
||||||
|
return crc, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func digestKeyInConv(value uint) string {
|
||||||
|
str := ""
|
||||||
|
switch value {
|
||||||
|
case DIGEST_NONE:
|
||||||
|
str = "None"
|
||||||
|
case DIGEST_CRC32C:
|
||||||
|
str = "CRC32C"
|
||||||
|
case DIGEST_ALL:
|
||||||
|
str = "None,CRC32C"
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberKeyConv(value string) (uint, bool) {
|
||||||
|
v, err := strconv.Atoi(value)
|
||||||
|
if err == nil {
|
||||||
|
return uint(v), true
|
||||||
|
}
|
||||||
|
return uint(v), false
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberKeyInConv(value uint) string {
|
||||||
|
s := strconv.Itoa(int(value))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolKeyConv(value string) (uint, bool) {
|
||||||
|
if strings.EqualFold(value, "yes") {
|
||||||
|
return 1, true
|
||||||
|
} else if strings.EqualFold(value, "no") {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolKeyInConv(value uint) string {
|
||||||
|
if value == 0 {
|
||||||
|
return "No"
|
||||||
|
}
|
||||||
|
return "Yes"
|
||||||
|
}
|
||||||
|
|
||||||
|
var sessionKeys map[string]*iscsiSessionKeys = map[string]*iscsiSessionKeys{
|
||||||
// ISCSI_PARAM_MAX_RECV_DLENGTH
|
// ISCSI_PARAM_MAX_RECV_DLENGTH
|
||||||
{"MaxRecvDataSegmentLength", 8192, 512, 16777215},
|
"MaxRecvDataSegmentLength": {ISCSI_PARAM_MAX_RECV_DLENGTH, true, 32768, 512, 16777215, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_HDRDGST_EN
|
// ISCSI_PARAM_HDRDGST_EN
|
||||||
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
|
"HeaderDigest": {ISCSI_PARAM_HDRDGST_EN, false, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, digestKeyConv, digestKeyInConv},
|
||||||
// ISCSI_PARAM_DATADGST_EN
|
// ISCSI_PARAM_DATADGST_EN
|
||||||
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
|
"DataDigest": {ISCSI_PARAM_DATADGST_EN, false, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, digestKeyConv, digestKeyInConv},
|
||||||
// ISCSI_PARAM_INITIAL_R2T_EN
|
// ISCSI_PARAM_INITIAL_R2T_EN
|
||||||
{"InitialR2T", 1, 0, 1},
|
"InitialR2T": {ISCSI_PARAM_INITIAL_R2T_EN, false, 1, 0, 1, boolKeyConv, boolKeyInConv},
|
||||||
// ISCSI_PARAM_MAX_R2T
|
// ISCSI_PARAM_MAX_R2T
|
||||||
{"MaxOutstandingR2T", 1, 1, 65535},
|
"MaxOutstandingR2T": {ISCSI_PARAM_MAX_R2T, true, 1, 1, 65535, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_IMM_DATA_EN
|
// ISCSI_PARAM_IMM_DATA_EN
|
||||||
{"ImmediateData", 1, 0, 1},
|
"ImmediateData": {ISCSI_PARAM_IMM_DATA_EN, true, 1, 0, 1, boolKeyConv, boolKeyInConv},
|
||||||
// ISCSI_PARAM_FIRST_BURST
|
// ISCSI_PARAM_FIRST_BURST
|
||||||
{"FirstBurstLength", 65536, 512, 16777215},
|
"FirstBurstLength": {ISCSI_PARAM_FIRST_BURST, true, 65536, 512, 16777215, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_MAX_BURST
|
// ISCSI_PARAM_MAX_BURST
|
||||||
{"MaxBurstLength", 262144, 512, 16777215},
|
"MaxBurstLength": {ISCSI_PARAM_MAX_BURST, true, 262144, 512, 16777215, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_PDU_INORDER_EN
|
// ISCSI_PARAM_PDU_INORDER_EN
|
||||||
{"DataPDUInOrder", 1, 0, 1},
|
"DataPDUInOrder": {ISCSI_PARAM_PDU_INORDER_EN, true, 1, 0, 1, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_DATASEQ_INORDER_EN
|
// ISCSI_PARAM_DATASEQ_INORDER_EN
|
||||||
{"DataSequenceInOrder", 1, 0, 1},
|
"DataSequenceInOrder": {ISCSI_PARAM_DATASEQ_INORDER_EN, true, 1, 0, 1, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_ERL
|
// ISCSI_PARAM_ERL
|
||||||
{"ErrorRecoveryLevel", 0, 0, 2},
|
"ErrorRecoveryLevel": {ISCSI_PARAM_ERL, true, 0, 0, 2, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_IFMARKER_EN
|
// ISCSI_PARAM_IFMARKER_EN
|
||||||
{"IFMarker", 0, 0, 1},
|
"IFMarker": {ISCSI_PARAM_IFMARKER_EN, true, 0, 0, 1, boolKeyConv, boolKeyInConv},
|
||||||
// ISCSI_PARAM_OFMARKER_EN
|
// ISCSI_PARAM_OFMARKER_EN
|
||||||
{"OFMarker", 0, 0, 1},
|
"OFMarker": {ISCSI_PARAM_OFMARKER_EN, true, 0, 0, 1, boolKeyConv, boolKeyInConv},
|
||||||
// ISCSI_PARAM_DEFAULTTIME2WAIT
|
// ISCSI_PARAM_DEFAULTTIME2WAIT
|
||||||
{"DefaultTime2Wait", 2, 0, 3600},
|
"DefaultTime2Wait": {ISCSI_PARAM_DEFAULTTIME2WAIT, true, 2, 0, 3600, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_DEFAULTTIME2RETAIN
|
// ISCSI_PARAM_DEFAULTTIME2RETAIN
|
||||||
{"DefaultTime2Retain", 20, 0, 3600},
|
"DefaultTime2Retain": {ISCSI_PARAM_DEFAULTTIME2RETAIN, false, 20, 0, 3600, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_OFMARKINT
|
// ISCSI_PARAM_OFMARKINT
|
||||||
{"OFMarkInt", 2048, 1, 65535},
|
"OFMarkInt": {ISCSI_PARAM_OFMARKINT, true, 2048, 1, 65535, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_IFMARKINT
|
// ISCSI_PARAM_IFMARKINT
|
||||||
{"IFMarkInt", 2048, 1, 65535},
|
"IFMarkInt": {ISCSI_PARAM_IFMARKINT, true, 2048, 1, 65535, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_MAXCONNECTIONS
|
// ISCSI_PARAM_MAXCONNECTIONS
|
||||||
{"MaxConnections", 1, 1, 65535},
|
"MaxConnections": {ISCSI_PARAM_MAXCONNECTIONS, true, 1, 1, 65535, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_RDMA_EXTENSIONS
|
// ISCSI_PARAM_RDMA_EXTENSIONS
|
||||||
{"RDMAExtensions", 0, 0, 1},
|
"RDMAExtensions": {ISCSI_PARAM_RDMA_EXTENSIONS, true, 0, 0, 1, boolKeyConv, boolKeyInConv},
|
||||||
// ISCSI_PARAM_TARGET_RDSL
|
// ISCSI_PARAM_TARGET_RDSL
|
||||||
{"TargetRecvDataSegmentLength", 8192, 512, 16777215},
|
"TargetRecvDataSegmentLength": {ISCSI_PARAM_TARGET_RDSL, true, 8192, 512, 16777215, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_INITIATOR_RDSL
|
// ISCSI_PARAM_INITIATOR_RDSL
|
||||||
{"InitiatorRecvDataSegmentLength", 8192, 512, 16777215},
|
"InitiatorRecvDataSegmentLength": {ISCSI_PARAM_INITIATOR_RDSL, true, 8192, 512, 16777215, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_MAX_OUTST_PDU
|
// ISCSI_PARAM_MAX_OUTST_PDU
|
||||||
{"MaxOutstandingUnexpectedPDUs", 0, 2, 4294967295},
|
"MaxOutstandingUnexpectedPDUs": {ISCSI_PARAM_MAX_OUTST_PDU, true, 0, 2, 4294967295, numberKeyConv, numberKeyInConv},
|
||||||
// "local" parmas, never sent to the initiator
|
// "local" parmas, never sent to the initiator
|
||||||
// ISCSI_PARAM_MAX_XMIT_DLENGTH
|
// ISCSI_PARAM_MAX_XMIT_DLENGTH
|
||||||
{"MaxXmitDataSegmentLength", 8192, 512, 16777215},
|
"MaxXmitDataSegmentLength": {ISCSI_PARAM_MAX_XMIT_DLENGTH, true, 8192, 512, 16777215, numberKeyConv, numberKeyInConv},
|
||||||
// ISCSI_PARAM_MAX_QUEUE_CMD
|
// ISCSI_PARAM_MAX_QUEUE_CMD
|
||||||
{"MaxQueueCmd", MAX_QUEUE_CMD_DEF, MAX_QUEUE_CMD_MIN, MAX_QUEUE_CMD_MAX},
|
"MaxQueueCmd": {ISCSI_PARAM_MAX_QUEUE_CMD, true, MAX_QUEUE_CMD_DEF, MAX_QUEUE_CMD_MIN, MAX_QUEUE_CMD_MAX, numberKeyConv, numberKeyInConv},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session is an iSCSI session.
|
// Session is an iSCSI session.
|
||||||
@@ -152,18 +241,22 @@ type ISCSISession struct {
|
|||||||
InitiatorAlias string
|
InitiatorAlias string
|
||||||
Target *ISCSITarget
|
Target *ISCSITarget
|
||||||
ISID uint64
|
ISID uint64
|
||||||
TSIH uint64
|
TSIH uint16
|
||||||
ITNexusID uuid.UUID
|
TPGT uint16
|
||||||
|
SessionType int
|
||||||
|
ITNexus *api.ITNexus
|
||||||
|
|
||||||
ExpCmdSN uint32
|
ExpCmdSN uint32
|
||||||
// only one connection per session
|
MaxCmdSN uint32
|
||||||
Connections []*iscsiConnection
|
// currently, this is only one connection per session
|
||||||
Commands []*ISCSICommand
|
Connections map[uint16]*iscsiConnection
|
||||||
PendingTasks taskQueue
|
ConnectionsRWMutex sync.RWMutex
|
||||||
MaxQueueCommand uint32
|
Commands []*ISCSICommand
|
||||||
SessionParam []ISCSISessionParam
|
PendingTasks taskQueue
|
||||||
Info string
|
MaxQueueCommand uint32
|
||||||
Rdma int
|
SessionParam ISCSISessionParamList
|
||||||
|
Info string
|
||||||
|
Rdma int
|
||||||
}
|
}
|
||||||
|
|
||||||
type taskQueue []*iscsiTask
|
type taskQueue []*iscsiTask
|
||||||
@@ -192,11 +285,140 @@ func (tq *taskQueue) Pop() interface{} {
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ISCSITargetDriver) LookupISCSISession(tgtName string, iniName string, isid uint64, tsih uint16, tpgt uint16) *ISCSISession {
|
||||||
|
var (
|
||||||
|
tgt *ISCSITarget
|
||||||
|
sess *ISCSISession
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
tgt, ok = s.iSCSITargets[tgtName]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tgt.SessionsRWMutex.RLock()
|
||||||
|
defer tgt.SessionsRWMutex.RUnlock()
|
||||||
|
sess, ok = tgt.Sessions[tsih]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if (sess.ISID == isid) && (sess.TPGT == tpgt) {
|
||||||
|
return sess
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ISCSITargetDriver) UnBindISCSISession(sess *ISCSISession) {
|
||||||
|
target := sess.Target
|
||||||
|
target.SessionsRWMutex.Lock()
|
||||||
|
defer target.SessionsRWMutex.Unlock()
|
||||||
|
delete(target.Sessions, sess.TSIH)
|
||||||
|
scsi.RemoveITNexus(&sess.Target.SCSITarget, sess.ITNexus)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ISCSITargetDriver) BindISCSISession(conn *iscsiConnection) error {
|
||||||
|
var (
|
||||||
|
target *ISCSITarget
|
||||||
|
existSess *ISCSISession
|
||||||
|
existConn *iscsiConnection
|
||||||
|
newSess *ISCSISession
|
||||||
|
tpgt uint16
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
//Find TPGT and Target ID
|
||||||
|
if conn.loginParam.sessionType == SESSION_DISCOVERY {
|
||||||
|
conn.tid = 0xffff
|
||||||
|
} else {
|
||||||
|
for _, t := range s.iSCSITargets {
|
||||||
|
if t.SCSITarget.Name == conn.loginParam.target {
|
||||||
|
target = t
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if target == nil {
|
||||||
|
return fmt.Errorf("No target found with name(%s)", conn.loginParam.target)
|
||||||
|
}
|
||||||
|
|
||||||
|
tpgt, err = target.FindTPG(conn.conn.LocalAddr().String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn.loginParam.tpgt = tpgt
|
||||||
|
conn.tid = target.TID
|
||||||
|
}
|
||||||
|
|
||||||
|
existSess = s.LookupISCSISession(conn.loginParam.target, conn.loginParam.initiator,
|
||||||
|
conn.loginParam.isid, conn.loginParam.tsih, conn.loginParam.tpgt)
|
||||||
|
if existSess != nil {
|
||||||
|
existConn = existSess.LookupConnection(conn.cid)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.loginParam.sessionType == SESSION_DISCOVERY &&
|
||||||
|
conn.loginParam.tsih != ISCSI_UNSPEC_TSIH &&
|
||||||
|
existSess != nil {
|
||||||
|
return fmt.Errorf("initiator err, invalid request")
|
||||||
|
}
|
||||||
|
|
||||||
|
if existSess == nil && conn.loginParam.tsih != 0 &&
|
||||||
|
existSess.TSIH != conn.loginParam.tsih {
|
||||||
|
return fmt.Errorf("initiator err, no session")
|
||||||
|
}
|
||||||
|
|
||||||
|
if existSess == nil {
|
||||||
|
newSess, err = s.NewISCSISession(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if newSess.SessionType == SESSION_NORMAL {
|
||||||
|
log.Infof("New Session initiator name:%v,target name:%v,ISID:0x%x",
|
||||||
|
conn.loginParam.initiator, conn.loginParam.target, conn.loginParam.isid)
|
||||||
|
//register normal session
|
||||||
|
itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(newSess)}
|
||||||
|
scsi.AddITNexus(&newSess.Target.SCSITarget, itnexus)
|
||||||
|
newSess.ITNexus = itnexus
|
||||||
|
conn.session = newSess
|
||||||
|
|
||||||
|
newSess.Target.SessionsRWMutex.Lock()
|
||||||
|
newSess.Target.Sessions[newSess.TSIH] = newSess
|
||||||
|
newSess.Target.SessionsRWMutex.Unlock()
|
||||||
|
} else {
|
||||||
|
conn.session = newSess
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if conn.loginParam.tsih == ISCSI_UNSPEC_TSIH {
|
||||||
|
log.Infof("Session Reinstatement initiator name:%v,target name:%v,ISID:0x%x",
|
||||||
|
conn.loginParam.initiator, conn.loginParam.target, conn.loginParam.isid)
|
||||||
|
newSess, err = s.ReInstatement(existConn.session, conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(newSess)}
|
||||||
|
scsi.AddITNexus(&newSess.Target.SCSITarget, itnexus)
|
||||||
|
newSess.ITNexus = itnexus
|
||||||
|
conn.session = newSess
|
||||||
|
|
||||||
|
newSess.Target.SessionsRWMutex.Lock()
|
||||||
|
newSess.Target.Sessions[newSess.TSIH] = newSess
|
||||||
|
newSess.Target.SessionsRWMutex.Unlock()
|
||||||
|
} else {
|
||||||
|
if existConn != nil {
|
||||||
|
log.Infof("Connection Reinstatement initiator name:%v,target name:%v,ISID:0x%x,CID:%v",
|
||||||
|
conn.loginParam.initiator, conn.loginParam.target, conn.loginParam.isid)
|
||||||
|
existConn.ReInstatement(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a new session.
|
// New creates a new session.
|
||||||
func (s *ISCSITargetDriver) NewISCSISession(conn *iscsiConnection, isid uint64) (*ISCSISession, error) {
|
func (s *ISCSITargetDriver) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
|
||||||
var (
|
var (
|
||||||
target *ISCSITarget
|
target *ISCSITarget
|
||||||
tsih uint64
|
tsih uint16
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, t := range s.iSCSITargets {
|
for _, t := range s.iSCSITargets {
|
||||||
@@ -205,40 +427,54 @@ func (s *ISCSITargetDriver) NewISCSISession(conn *iscsiConnection, isid uint64)
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if target == nil {
|
if target == nil && conn.tid != 0xffff {
|
||||||
return nil, fmt.Errorf("No target found with tid(%d)", conn.tid)
|
return nil, fmt.Errorf("No target found with tid(%d)", conn.tid)
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
tsih = s.AllocTSIH()
|
||||||
rand.Seed(int64(time.Now().UTC().Nanosecond()))
|
if tsih == ISCSI_UNSPEC_TSIH {
|
||||||
tsih = uint64(rand.Uint32())
|
return nil, fmt.Errorf("TSIH Pool exhausted tid(%d)", conn.tid)
|
||||||
for _, s := range target.Sessions {
|
|
||||||
if s.TSIH == tsih {
|
|
||||||
tsih = 0
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tsih != 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := &ISCSISession{
|
sess := &ISCSISession{
|
||||||
TSIH: tsih,
|
TSIH: tsih,
|
||||||
ISID: isid,
|
ISID: conn.loginParam.isid,
|
||||||
Initiator: conn.initiator,
|
TPGT: conn.loginParam.tpgt,
|
||||||
InitiatorAlias: conn.initiatorAlias,
|
Initiator: conn.loginParam.initiator,
|
||||||
|
InitiatorAlias: conn.loginParam.initiatorAlias,
|
||||||
|
SessionType: conn.loginParam.sessionType,
|
||||||
Target: target,
|
Target: target,
|
||||||
Connections: []*iscsiConnection{conn},
|
Connections: map[uint16]*iscsiConnection{conn.cid: conn},
|
||||||
SessionParam: conn.sessionParam,
|
SessionParam: conn.loginParam.sessionParam,
|
||||||
MaxQueueCommand: uint32(conn.sessionParam[ISCSI_PARAM_MAX_QUEUE_CMD].Value),
|
MaxQueueCommand: uint32(conn.loginParam.sessionParam[ISCSI_PARAM_MAX_QUEUE_CMD].Value),
|
||||||
Rdma: 0,
|
Rdma: 0,
|
||||||
ExpCmdSN: conn.expCmdSN,
|
ExpCmdSN: conn.expCmdSN,
|
||||||
}
|
}
|
||||||
conn.session = sess
|
|
||||||
return sess, nil
|
return sess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sess *ISCSISession) LookupConnection(cid uint16) *iscsiConnection {
|
||||||
|
sess.ConnectionsRWMutex.RLock()
|
||||||
|
defer sess.ConnectionsRWMutex.RUnlock()
|
||||||
|
conn := sess.Connections[cid]
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ISCSITargetDriver) ReInstatement(existSess *ISCSISession, conn *iscsiConnection) (*ISCSISession, error) {
|
||||||
|
newSess, err := s.NewISCSISession(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newSess.ExpCmdSN = existSess.ExpCmdSN
|
||||||
|
newSess.MaxCmdSN = existSess.MaxCmdSN + 1
|
||||||
|
s.UnBindISCSISession(existSess)
|
||||||
|
for _, tmpConn := range existSess.Connections {
|
||||||
|
tmpConn.close()
|
||||||
|
}
|
||||||
|
existSess.Connections = map[uint16]*iscsiConnection{}
|
||||||
|
return newSess, nil
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* iSCSI I_T nexus identifer = (iSCSI Initiator Name + 'i' + ISID, iSCSI Target Name + 't' + Portal Group Tag)
|
* iSCSI I_T nexus identifer = (iSCSI Initiator Name + 'i' + ISID, iSCSI Target Name + 't' + Portal Group Tag)
|
||||||
*/
|
*/
|
||||||
@@ -246,6 +482,6 @@ func GeniSCSIITNexusID(sess *ISCSISession) string {
|
|||||||
strID := fmt.Sprintf("%si0x%12x,%st%d",
|
strID := fmt.Sprintf("%si0x%12x,%st%d",
|
||||||
sess.Initiator, sess.ISID,
|
sess.Initiator, sess.ISID,
|
||||||
sess.Target.SCSITarget.Name,
|
sess.Target.SCSITarget.Name,
|
||||||
sess.Connections[0].tpgt)
|
sess.TPGT)
|
||||||
return strID
|
return strID
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ verify:
|
|||||||
bs.DataAdvise(int64(offset), int64(length), util.POSIX_FADV_WILLNEED)
|
bs.DataAdvise(int64(offset), int64(length), util.POSIX_FADV_WILLNEED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Infof("io done %s", string(scb))
|
|
||||||
return nil
|
return nil
|
||||||
sense:
|
sense:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user