mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-21 22:56:55 +00:00
fix: iSCSI login and discovery bugs found in WSL2 smoke test
- Skip InitiatorAlias in negotiation (was returning NotUnderstood) - Capture TargetName in StageLoginOp direct-jump path (iscsiadm skips security stage, sends CSG=LoginOp directly -- nil SCSIHandler crash) - Add portalAddr to TargetServer for discovery responses (listener on [::] is not routable from WSL2 clients) - Add -portal flag to iscsi-target binary Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
6a400f6760
commit
6546c549eb
@@ -22,9 +22,10 @@ import (
|
||||
func main() {
|
||||
volPath := flag.String("vol", "", "path to BlockVol file")
|
||||
addr := flag.String("addr", ":3260", "listen address")
|
||||
portal := flag.String("portal", "", "advertised address for discovery (e.g. 10.0.0.1:3260,1)")
|
||||
iqn := flag.String("iqn", "iqn.2024.com.seaweedfs:vol1", "target IQN")
|
||||
create := flag.Bool("create", false, "create a new volume file")
|
||||
size := flag.String("size", "1G", "volume size (e.g., 1G, 100M) — used with -create")
|
||||
size := flag.String("size", "1G", "volume size (e.g., 1G, 100M) -- used with -create")
|
||||
flag.Parse()
|
||||
|
||||
if *volPath == "" {
|
||||
@@ -73,6 +74,9 @@ func main() {
|
||||
config.TargetName = *iqn
|
||||
config.TargetAlias = "SeaweedFS BlockVol"
|
||||
ts := iscsi.NewTargetServer(*addr, config, logger)
|
||||
if *portal != "" {
|
||||
ts.SetPortalAddr(*portal)
|
||||
}
|
||||
ts.AddVolume(*iqn, adapter)
|
||||
|
||||
// Graceful shutdown on signal
|
||||
|
||||
@@ -216,6 +216,27 @@ func (ln *LoginNegotiator) HandleLoginPDU(req *PDU, resolver TargetResolver) *PD
|
||||
setLoginReject(resp, LoginStatusInitiatorErr, LoginDetailMissingParam)
|
||||
return resp
|
||||
}
|
||||
// Session type
|
||||
if st, ok := params.Get("SessionType"); ok {
|
||||
ln.SessionType = st
|
||||
}
|
||||
if ln.SessionType == "" {
|
||||
ln.SessionType = "Normal"
|
||||
}
|
||||
// Target name (required for Normal sessions)
|
||||
if tn, ok := params.Get("TargetName"); ok {
|
||||
if ln.SessionType == "Normal" {
|
||||
if resolver == nil || !resolver.HasTarget(tn) {
|
||||
setLoginReject(resp, LoginStatusInitiatorErr, LoginDetailNotFound)
|
||||
return resp
|
||||
}
|
||||
}
|
||||
ln.TargetName = tn
|
||||
ln.targetOK = true
|
||||
} else if ln.SessionType == "Normal" && !ln.targetOK {
|
||||
setLoginReject(resp, LoginStatusInitiatorErr, LoginDetailMissingParam)
|
||||
return resp
|
||||
}
|
||||
} else {
|
||||
setLoginReject(resp, LoginStatusInitiatorErr, LoginDetailInitiatorError)
|
||||
return resp
|
||||
@@ -319,9 +340,9 @@ func (ln *LoginNegotiator) negotiateParams(req *Params, resp *Params) {
|
||||
case "DataDigest":
|
||||
resp.Set(key, "None")
|
||||
case "TargetName", "InitiatorName", "SessionType", "AuthMethod":
|
||||
// Already handled or declarative — skip
|
||||
case "TargetAlias":
|
||||
// Informational from initiator — skip
|
||||
// Already handled or declarative -- skip
|
||||
case "TargetAlias", "InitiatorAlias":
|
||||
// Informational -- skip
|
||||
default:
|
||||
// Unknown keys: respond with NotUnderstood
|
||||
resp.Set(key, "NotUnderstood")
|
||||
|
||||
@@ -17,11 +17,12 @@ var (
|
||||
// TargetServer manages the iSCSI target: TCP listener, volume registry,
|
||||
// and active sessions.
|
||||
type TargetServer struct {
|
||||
mu sync.RWMutex
|
||||
listener net.Listener
|
||||
config TargetConfig
|
||||
volumes map[string]BlockDevice // target IQN -> device
|
||||
addr string
|
||||
mu sync.RWMutex
|
||||
listener net.Listener
|
||||
config TargetConfig
|
||||
volumes map[string]BlockDevice // target IQN -> device
|
||||
addr string
|
||||
portalAddr string // advertised address for discovery (if empty, uses listener addr)
|
||||
|
||||
// Active session tracking for graceful shutdown
|
||||
activeMu sync.Mutex
|
||||
@@ -72,15 +73,28 @@ func (ts *TargetServer) HasTarget(name string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// SetPortalAddr sets the address advertised in discovery responses.
|
||||
// Use this when the listen address is 0.0.0.0 or [::] and clients
|
||||
// need a routable IP. Format: "ip:port,portal-group" (e.g. "10.0.0.1:3260,1").
|
||||
func (ts *TargetServer) SetPortalAddr(addr string) {
|
||||
ts.mu.Lock()
|
||||
defer ts.mu.Unlock()
|
||||
ts.portalAddr = addr
|
||||
}
|
||||
|
||||
// ListTargets implements TargetLister.
|
||||
func (ts *TargetServer) ListTargets() []DiscoveryTarget {
|
||||
ts.mu.RLock()
|
||||
defer ts.mu.RUnlock()
|
||||
addr := ts.portalAddr
|
||||
if addr == "" {
|
||||
addr = ts.ListenAddr()
|
||||
}
|
||||
targets := make([]DiscoveryTarget, 0, len(ts.volumes))
|
||||
for iqn := range ts.volumes {
|
||||
targets = append(targets, DiscoveryTarget{
|
||||
Name: iqn,
|
||||
Address: ts.ListenAddr(),
|
||||
Address: addr,
|
||||
})
|
||||
}
|
||||
return targets
|
||||
|
||||
Reference in New Issue
Block a user