func (c *IscsiIpcConn) DestroySession(sid uint32) error {
	dSession := iSCSIUEventDestroySession{
		Type:            ISCSI_UEVENT_DESTROY_SESSION,
		IfError:         0,
		TransportHandle: c.TransportHandle,
	}
	dSession.DSession.Sid = sid
	if err := c.DoNetlink(unsafe.Pointer(&dSession)); err != nil {
		return err
	}
	if dSession.DSessionRet.Ret != 0 {
		return fmt.Errorf("error destroying session: %d", dSession.DSessionRet.Ret)
	}
	return nil
}
DestroySession attempts to destroy the identified session. May fail if
connections are still active
package iscsinl
import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"strconv"
	"sync/atomic"
	"syscall"
	"unsafe"
	"github.com/vishvananda/netlink/nl"
	"golang.org/x/sys/unix"
)
const STOP_CONN_RECOVER = 0x3
type IscsiEvent uint32
const (
	UEVENT_BASE                  IscsiEvent = 10
	KEVENT_BASE                             = 100
	ISCSI_UEVENT_CREATE_SESSION             = UEVENT_BASE + 1
	ISCSI_UEVENT_DESTROY_SESSION            = UEVENT_BASE + 2
	ISCSI_UEVENT_CREATE_CONN                = UEVENT_BASE + 3
	ISCSI_UEVENT_DESTROY_CONN               = UEVENT_BASE + 4
	ISCSI_UEVENT_BIND_CONN                  = UEVENT_BASE + 5
	ISCSI_UEVENT_SET_PARAM                  = UEVENT_BASE + 6
	ISCSI_UEVENT_START_CONN                 = UEVENT_BASE + 7
	ISCSI_UEVENT_STOP_CONN                  = UEVENT_BASE + 8
	ISCSI_UEVENT_SEND_PDU                   = UEVENT_BASE + 9
	ISCSI_KEVENT_RECV_PDU       = KEVENT_BASE + 1
	ISCSI_KEVENT_CONN_ERROR     = KEVENT_BASE + 2
	ISCSI_KEVENT_IF_ERROR       = KEVENT_BASE + 3
	ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6
)
type IscsiParam uint32
const (
	ISCSI_PARAM_MAX_RECV_DLENGTH IscsiParam = iota
	ISCSI_PARAM_MAX_XMIT_DLENGTH
	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_EXP_STATSN
	ISCSI_PARAM_TARGET_NAME
	ISCSI_PARAM_TPGT
	ISCSI_PARAM_PERSISTENT_ADDRESS
	ISCSI_PARAM_PERSISTENT_PORT
	ISCSI_PARAM_SESS_RECOVERY_TMO
	ISCSI_PARAM_CONN_PORT
	ISCSI_PARAM_CONN_ADDRESS
	ISCSI_PARAM_USERNAME
	ISCSI_PARAM_USERNAME_IN
	ISCSI_PARAM_PASSWORD
	ISCSI_PARAM_PASSWORD_IN
	ISCSI_PARAM_FAST_ABORT
	ISCSI_PARAM_ABORT_TMO
	ISCSI_PARAM_LU_RESET_TMO
	ISCSI_PARAM_HOST_RESET_TMO
	ISCSI_PARAM_PING_TMO
	ISCSI_PARAM_RECV_TMO
	ISCSI_PARAM_IFACE_NAME
	ISCSI_PARAM_ISID
	ISCSI_PARAM_INITIATOR_NAME
)
var paramToString = map[IscsiParam]string{
	ISCSI_PARAM_TARGET_NAME:        "Target Name",
	ISCSI_PARAM_INITIATOR_NAME:     "Initiator Name",
	ISCSI_PARAM_MAX_RECV_DLENGTH:   "Max Recv DLength",
	ISCSI_PARAM_MAX_XMIT_DLENGTH:   "Max Xmit DLenght",
	ISCSI_PARAM_FIRST_BURST:        "First Burst",
	ISCSI_PARAM_MAX_BURST:          "Max Burst",
	ISCSI_PARAM_PDU_INORDER_EN:     "PDU Inorder EN",
	ISCSI_PARAM_DATASEQ_INORDER_EN: "Data Seq In Order EN",
	ISCSI_PARAM_INITIAL_R2T_EN:     "Inital R2T EN",
	ISCSI_PARAM_IMM_DATA_EN:        "Immediate Data EN",
	ISCSI_PARAM_EXP_STATSN:         "Exp Statsn",
	ISCSI_PARAM_HDRDGST_EN:         "HDR Digest EN",
	ISCSI_PARAM_DATADGST_EN:        "Data Digest EN",
	ISCSI_PARAM_PING_TMO:           "Ping TMO",
	ISCSI_PARAM_RECV_TMO:           "Recv TMO",
}
func (p IscsiParam) String() string {
	val, ok := paramToString[p]
	if !ok {
		return fmt.Sprintf("IscsiParam(%d)", int(p))
	}
	return val
}
type IscsiErr uint32
const (
	ISCSI_OK                      IscsiErr = 0
	ISCSI_ERR_BASE                         = 1000
	ISCSI_ERR_DATASN                       = ISCSI_ERR_BASE + 1
	ISCSI_ERR_DATA_OFFSET                  = ISCSI_ERR_BASE + 2
	ISCSI_ERR_MAX_CMDSN                    = ISCSI_ERR_BASE + 3
	ISCSI_ERR_EXP_CMDSN                    = ISCSI_ERR_BASE + 4
	ISCSI_ERR_BAD_OPCODE                   = ISCSI_ERR_BASE + 5
	ISCSI_ERR_DATALEN                      = ISCSI_ERR_BASE + 6
	ISCSI_ERR_AHSLEN                       = ISCSI_ERR_BASE + 7
	ISCSI_ERR_PROTO                        = ISCSI_ERR_BASE + 8
	ISCSI_ERR_LUN                          = ISCSI_ERR_BASE + 9
	ISCSI_ERR_BAD_ITT                      = ISCSI_ERR_BASE + 10
	ISCSI_ERR_CONN_FAILED                  = ISCSI_ERR_BASE + 11
	ISCSI_ERR_R2TSN                        = ISCSI_ERR_BASE + 12
	ISCSI_ERR_SESSION_FAILED               = ISCSI_ERR_BASE + 13
	ISCSI_ERR_HDR_DGST                     = ISCSI_ERR_BASE + 14
	ISCSI_ERR_DATA_DGST                    = ISCSI_ERR_BASE + 15
	ISCSI_ERR_PARAM_NOT_FOUND              = ISCSI_ERR_BASE + 16
	ISCSI_ERR_NO_SCSI_CMD                  = ISCSI_ERR_BASE + 17
	ISCSI_ERR_INVALID_HOST                 = ISCSI_ERR_BASE + 18
	ISCSI_ERR_XMIT_FAILED                  = ISCSI_ERR_BASE + 19
	ISCSI_ERR_TCP_CONN_CLOSE               = ISCSI_ERR_BASE + 20
	ISCSI_ERR_SCSI_EH_SESSION_RST          = ISCSI_ERR_BASE + 21
	ISCSI_ERR_NOP_TIMEDOUT                 = ISCSI_ERR_BASE + 22
)
func (e IscsiErr) String() string {
	switch e {
	case ISCSI_OK:
		return "ISCSI_OK"
	case ISCSI_ERR_BASE:
		return "ISCSI_ERR_BASE"
	case ISCSI_ERR_DATASN:
		return "ISCSI_ERR_DATASN"
	case ISCSI_ERR_DATA_OFFSET:
		return "ISCSI_ERR_DATA_OFFSET"
	case ISCSI_ERR_MAX_CMDSN:
		return "ISCSI_ERR_MAX_CMDSN"
	case ISCSI_ERR_EXP_CMDSN:
		return "ISCSI_ERR_EXP_CMDSN"
	case ISCSI_ERR_BAD_OPCODE:
		return "ISCSI_ERR_BAD_OPCODE"
	case ISCSI_ERR_DATALEN:
		return "ISCSI_ERR_DATALEN"
	case ISCSI_ERR_AHSLEN:
		return "ISCSI_ERR_AHSLEN"
	case ISCSI_ERR_PROTO:
		return "ISCSI_ERR_PROTO"
	case ISCSI_ERR_LUN:
		return "ISCSI_ERR_LUN"
	case ISCSI_ERR_BAD_ITT:
		return "ISCSI_ERR_BAD_ITT"
	case ISCSI_ERR_CONN_FAILED:
		return "ISCSI_ERR_CONN_FAILED"
	case ISCSI_ERR_R2TSN:
		return "ISCSI_ERR_R2TSN"
	case ISCSI_ERR_SESSION_FAILED:
		return "ISCSI_ERR_SESSION_FAILED"
	case ISCSI_ERR_HDR_DGST:
		return "ISCSI_ERR_HDR_DGST"
	case ISCSI_ERR_DATA_DGST:
		return "ISCSI_ERR_DATA_DGST"
	case ISCSI_ERR_PARAM_NOT_FOUND:
		return "ISCSI_ERR_PARAM_NOT_FOUND"
	case ISCSI_ERR_NO_SCSI_CMD:
		return "ISCSI_ERR_NO_SCSI_CMD"
	case ISCSI_ERR_INVALID_HOST:
		return "ISCSI_ERR_INVALID_HOST"
	case ISCSI_ERR_XMIT_FAILED:
		return "ISCSI_ERR_XMIT_FAILED"
	case ISCSI_ERR_TCP_CONN_CLOSE:
		return "ISCSI_ERR_TCP_CONN_CLOSE"
	case ISCSI_ERR_SCSI_EH_SESSION_RST:
		return "ISCSI_ERR_SCSI_EH_SESSION_RST"
	case ISCSI_ERR_NOP_TIMEDOUT:
		return "ISCSI_ERR_NOP_TIMEDOUT"
	default:
		return strconv.Itoa(int(e))
	}
}
type iSCSIUEvent struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	UserArg         [24]byte
	ReturnArg       [16]byte
}
type iSCSIUEventCreateSession struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	CSession        struct {
		InitialCmdSN uint32
		CmdsMax      uint16
		QueueDepth   uint16
		_            [16]byte
	}
	CSessionRet struct {
		Sid    uint32
		HostNo uint32
		_      [8]byte
	}
}
type iSCSIUEventDestroySession struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	DSession        struct {
		Sid uint32
		_   [20]byte
	}
	DSessionRet struct {
		Ret uint32
		_   [12]byte
	}
}
type iSCSIUEventCreateConnection struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	CConn           struct {
		Sid uint32
		Cid uint32
		_   [16]byte
	}
	CConnRet struct {
		Sid uint32
		Cid uint32
		_   [8]byte
	}
}
type iSCSIUEventDestroyConnection struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	DConn           struct {
		Sid uint32
		Cid uint32
		_   [16]byte
	}
	DConnRet struct {
		Ret uint32
		_   [12]byte
	}
}
type iSCSIUEventBindConnection struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	BConn           struct {
		Sid          uint32
		Cid          uint32
		TransportEph uint64
		IsLeading    uint32
		_            [4]byte
	}
	BConnRet struct {
		Ret uint32
		_   [12]byte
	}
}
type iSCSIUEventSetParam struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	SetParam        struct {
		Sid   uint32
		Cid   uint32
		Param IscsiParam
		Len   uint32
		_     [8]byte
	}
	SetParamRet struct {
		Ret uint32
		_   [12]byte
	}
}
type iSCSIUEventStartConnection struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	StartConn       struct {
		Sid uint32
		Cid uint32
		_   [16]byte
	}
	StartConnRet struct {
		Ret uint32
		_   [12]byte
	}
}
type iSCSIUEventStopConnection struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	StopConn        struct {
		Sid        uint32
		Cid        uint32
		ConnHandle uint64
		Flag       uint32
		_          [4]byte
	}
	StopConnRet struct {
		Ret uint32
		_   [12]byte
	}
}
type iSCSIUEventSendPDU struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	SendPDU         struct {
		Sid      uint32
		Cid      uint32
		HdrSize  uint32
		DataSize uint32
		_        [8]byte
	}
	SendPDURet struct {
		Ret int32
		_   [12]byte
	}
}
type iSCSIKEventConnError struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	_               [24]byte
	ConnErr         struct {
		Sid   uint32
		Cid   uint32
		Error IscsiErr
		_     [4]byte
	}
}
type iSCSIKEventRecvEvent struct {
	Type            IscsiEvent
	IfError         uint32
	TransportHandle uint64
	_               [24]byte
	RecvReq         struct {
		Sid        uint32
		Cid        uint32
		RecvHandle uint64
	}
}
type IscsiIpcConn struct {
	Conn            *nl.NetlinkSocket
	TransportHandle uint64
	nextSeqNr       uint32
}
func ConnectNetlink() (*IscsiIpcConn, error) {
	conn, err := nl.Subscribe(unix.NETLINK_ISCSI, 1)
	if err != nil {
		if errors.Is(err, syscall.EPROTONOSUPPORT) {
			return nil, fmt.Errorf("failed to open iSCSI netlink socket (CONFIG_SCSI_ISCSI_ATTRS missing?): %w", err)
		}
		return nil, fmt.Errorf("failed to open iSCSI netlink socket: %w", err)
	}
	handleFile := "/sys/class/iscsi_transport/tcp/handle"
	data, err := ioutil.ReadFile(handleFile)
	if err != nil {
		return nil, fmt.Errorf("failed to read sysfs iscsi tcp handle: %w", err)
	}
	handle, err := strconv.ParseUint(string(data[:len(data)-1]), 10, 64)
	if err != nil {
		return nil, fmt.Errorf("failed to transport handle (%s) to int: %w", handleFile, err)
	}
	conn.SetReceiveTimeout(&unix.Timeval{Sec: 30})
	conn.SetSendTimeout(&unix.Timeval{Sec: 30})
	return &IscsiIpcConn{Conn: conn, TransportHandle: handle}, nil
}
func (c *IscsiIpcConn) WaitFor(Type IscsiEvent) (*syscall.NetlinkMessage, error) {
	for {
		msgs, _, err := c.Conn.Receive()
		if err != nil {
			return nil, err
		}
		for _, msg := range msgs {
			reader := bytes.NewReader(msg.Data)
			var uevent iSCSIUEvent
			err = binary.Read(reader, binary.LittleEndian, &uevent)
			if err != nil {
				return nil, err
			}
			if uevent.TransportHandle != c.TransportHandle {
				return nil, fmt.Errorf("wrong transport handle: %v", uevent.TransportHandle)
			}
			if uevent.Type == Type {
				return &msg, nil
			} else if uevent.Type == ISCSI_KEVENT_CONN_ERROR {
				reader.Seek(0, 0)
				var connErr iSCSIKEventConnError
				binary.Read(reader, binary.LittleEndian, &connErr)
				return nil, fmt.Errorf("connection error: %+v", connErr)
			} else if uevent.Type == ISCSI_KEVENT_IF_ERROR {
				return nil, fmt.Errorf("interface error: %v (invalid netlink message?)", uevent.IfError)
			}
			log.Printf("Dumping unexpected event of type %d", uevent.Type)
		}
	}
}
func FillNetlink(request *nl.NetlinkRequest, data ...interface{}) error {
	var buf bytes.Buffer
	for _, item := range data {
		switch v := item.(type) {
		case []byte:
			_, err := buf.Write(v)
			if err != nil {
				return err
			}
		default:
			err := binary.Write(&buf, binary.LittleEndian, item)
			if err != nil {
				return err
			}
		}
	}
	request.AddRawData(buf.Bytes())
	return nil
}
func (c *IscsiIpcConn) DoNetlink(ueventP unsafe.Pointer, data ...interface{}) error {
	uevent := (*iSCSIUEvent)(ueventP)
	request := nl.NetlinkRequest{
		NlMsghdr: unix.NlMsghdr{
			Len:   uint32(unix.SizeofNlMsghdr),
			Type:  uint16(uevent.Type),
			Flags: uint16(1),
			Seq:   atomic.AddUint32(&c.nextSeqNr, 1),
		},
	}
	if err := FillNetlink(&request, append([]interface{}{*uevent}, data...)...); err != nil {
		return err
	}
	if err := c.Conn.Send(&request); err != nil {
		return err
	}
	response, err := c.WaitFor(uevent.Type)
	if err != nil {
		return err
	}
	reader := bytes.NewReader(response.Data)
	return binary.Read(reader, binary.LittleEndian, uevent)
}
func (c *IscsiIpcConn) CreateSession(cmdsMax uint16, queueDepth uint16) (sid uint32, hostID uint32, err error) {
	cSession := iSCSIUEventCreateSession{
		Type:            ISCSI_UEVENT_CREATE_SESSION,
		IfError:         0,
		TransportHandle: c.TransportHandle,
	}
	cSession.CSession.CmdsMax = cmdsMax
	cSession.CSession.QueueDepth = queueDepth
	if err := c.DoNetlink(unsafe.Pointer(&cSession)); err != nil {
		return 0, 0, err
	}
	log.Println("Created new session ", cSession)
	return cSession.CSessionRet.Sid, cSession.CSessionRet.HostNo, nil
}