220 lines
5.5 KiB
Go
220 lines
5.5 KiB
Go
package iscsit
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
// log "github.com/Sirupsen/logrus"
|
|
"github.com/gostor/gotgt/pkg/util"
|
|
)
|
|
|
|
var (
|
|
iSCSILoginParamTextKV = []util.KeyValue{
|
|
{"HeaderDigest", "None"},
|
|
{"DataDigest", "None"},
|
|
{"ImmediateData", "Yes"},
|
|
{"InitialR2T", "Yes"},
|
|
{"MaxBurstLength", "262144"},
|
|
{"FirstBurstLength", "65536"},
|
|
{"MaxRecvDataSegmentLength", "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 (conn *iscsiConnection) processLoginData() ([]util.KeyValue, error) {
|
|
var (
|
|
uintVal uint
|
|
ok bool
|
|
defSessKey *iscsiSessionKeys
|
|
negoKV []util.KeyValue
|
|
kvChanges int
|
|
)
|
|
loginKV := util.ParseKVText(conn.req.RawData)
|
|
|
|
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 {
|
|
// rfc7143 11.13
|
|
buf := &bytes.Buffer{}
|
|
// byte 0
|
|
buf.WriteByte(byte(OpLoginResp))
|
|
var b byte
|
|
if m.Transit {
|
|
b |= 0x80
|
|
}
|
|
if m.Cont {
|
|
b |= 0x40
|
|
}
|
|
b |= byte(m.CSG&0xff) << 2
|
|
b |= byte(m.NSG & 0xff)
|
|
// byte 1
|
|
buf.WriteByte(b)
|
|
|
|
b = 0
|
|
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(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)
|
|
buf.WriteByte(b) // "reserved"
|
|
var bs [8]byte
|
|
buf.Write(bs[:])
|
|
rd := m.RawData
|
|
for len(rd)%4 != 0 {
|
|
rd = append(rd, 0)
|
|
}
|
|
buf.Write(rd)
|
|
return buf.Bytes()
|
|
}
|