#include <sys/cpuvar.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/scsi/generic/persist.h>
#include <sys/scsi/scsi_names.h>
#include <sys/socket.h>
#include <sys/strsubr.h>
#include <sys/sysmacros.h>
#include <sys/note.h>
#include <sys/sdt.h>
#include <sys/errno.h>
#include <sys/stmf.h>
#include <sys/stmf_ioctl.h>
#include <sys/portif.h>
#include <sys/idm/idm.h>
#include <sys/idm/idm_text.h>
#include <sys/idm/idm_so.h>
#define ISCSIT_LOGIN_SM_STRINGS
#include "iscsit.h"
#include "iscsit_auth.h"
typedef struct {
list_node_t le_ctx_node;
iscsit_login_event_t le_ctx_event;
idm_pdu_t *le_pdu;
} login_event_ctx_t;
#ifndef TRUE
#define TRUE B_TRUE
#endif
#ifndef FALSE
#define FALSE B_FALSE
#endif
#define DEFAULT_RADIUS_PORT 1812
static void
login_sm_complete(void *ict_void);
static void
login_sm_event_dispatch(iscsit_conn_login_t *lsm, iscsit_conn_t *ict,
login_event_ctx_t *ctx);
static void
login_sm_init(iscsit_conn_t *ict, login_event_ctx_t *ctx);
static void
login_sm_waiting(iscsit_conn_t *ict, login_event_ctx_t *ctx);
static void
login_sm_processing(iscsit_conn_t *ict, login_event_ctx_t *ctx);
static void
login_sm_responding(iscsit_conn_t *ict, login_event_ctx_t *ctx);
static void
login_sm_responded(iscsit_conn_t *ict, login_event_ctx_t *ctx);
static void
login_sm_ffp(iscsit_conn_t *ict, login_event_ctx_t *ctx);
static void
login_sm_done(iscsit_conn_t *ict, login_event_ctx_t *ctx);
static void
login_sm_error(iscsit_conn_t *ict, login_event_ctx_t *ctx);
static void
login_sm_new_state(iscsit_conn_t *ict, login_event_ctx_t *ctx,
iscsit_login_state_t new_state);
static void
login_sm_send_ack(iscsit_conn_t *ict, idm_pdu_t *pdu);
static idm_status_t
login_sm_validate_ack(iscsit_conn_t *ict, idm_pdu_t *pdu);
static boolean_t
login_sm_is_last_response(idm_pdu_t *pdu);
static void
login_sm_handle_initial_login(iscsit_conn_t *ict, idm_pdu_t *pdu);
static void
login_sm_send_next_response(iscsit_conn_t *ict, idm_pdu_t *pdu);
static void
login_sm_process_request(iscsit_conn_t *ict);
static idm_status_t
login_sm_req_pdu_check(iscsit_conn_t *ict, idm_pdu_t *pdu);
static idm_status_t
login_sm_process_nvlist(iscsit_conn_t *ict);
static idm_status_t
login_sm_check_security(iscsit_conn_t *ict);
static idm_pdu_t *
login_sm_build_login_response(iscsit_conn_t *ict);
static void
login_sm_ffp_actions(iscsit_conn_t *ict);
static idm_status_t
login_sm_validate_initial_parameters(iscsit_conn_t *ict);
static idm_status_t
login_sm_session_bind(iscsit_conn_t *ict);
static idm_status_t
login_sm_set_auth(iscsit_conn_t *ict);
static idm_status_t
login_sm_session_register(iscsit_conn_t *ict);
static kv_status_t
iscsit_handle_key(iscsit_conn_t *ict, nvpair_t *nvp, char *nvp_name);
static kv_status_t
iscsit_handle_common_key(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
iscsit_handle_security_key(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
iscsit_reply_security_key(iscsit_conn_t *ict);
static kv_status_t
iscsit_handle_operational_key(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
iscsit_reply_numerical(iscsit_conn_t *ict,
const char *nvp_name, const uint64_t value);
static kv_status_t
iscsit_reply_string(iscsit_conn_t *ict,
const char *nvp_name, const char *text);
static kv_status_t
iscsit_handle_digest(iscsit_conn_t *ict, nvpair_t *choices,
const idm_kv_xlate_t *ikvx);
static kv_status_t
iscsit_handle_boolean(iscsit_conn_t *ict, nvpair_t *nvp, boolean_t value,
const idm_kv_xlate_t *ikvx, boolean_t iscsit_value);
static kv_status_t
iscsit_handle_numerical(iscsit_conn_t *ict, nvpair_t *nvp, uint64_t value,
const idm_kv_xlate_t *ikvx,
uint64_t iscsi_min_value, uint64_t iscsi_max_value,
uint64_t iscsit_max_value);
static void
iscsit_process_negotiated_values(iscsit_conn_t *ict);
static void
login_resp_complete_cb(idm_pdu_t *pdu, idm_status_t status);
static idm_status_t
iscsit_add_declarative_keys(iscsit_conn_t *ict);
static char *
iscsit_fold_name(char *name, size_t *buflen);
uint64_t max_dataseglen_target = ISCSIT_MAX_RECV_DATA_SEGMENT_LENGTH;
extern kmutex_t login_sm_session_mutex;
idm_status_t
iscsit_login_sm_init(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
bzero(lsm, sizeof (iscsit_conn_login_t));
(void) nvlist_alloc(&lsm->icl_negotiated_values, NV_UNIQUE_NAME,
KM_SLEEP);
iscsit_conn_hold(ict);
lsm->icl_login_resp_tmpl = kmem_zalloc(sizeof (iscsi_login_rsp_hdr_t),
KM_SLEEP);
idm_sm_audit_init(&lsm->icl_state_audit);
mutex_init(&lsm->icl_mutex, NULL, MUTEX_DEFAULT, NULL);
list_create(&lsm->icl_login_events, sizeof (login_event_ctx_t),
offsetof(login_event_ctx_t, le_ctx_node));
list_create(&lsm->icl_pdu_list, sizeof (idm_pdu_t),
offsetof(idm_pdu_t, isp_client_lnd));
lsm->icl_login_state = ILS_LOGIN_INIT;
lsm->icl_login_last_state = ILS_LOGIN_INIT;
ict->ict_op.op_discovery_session = B_FALSE;
ict->ict_op.op_initial_r2t = ISCSI_DEFAULT_INITIALR2T;
ict->ict_op.op_immed_data = ISCSI_DEFAULT_IMMEDIATE_DATA;
ict->ict_op.op_data_pdu_in_order = ISCSI_DEFAULT_DATA_PDU_IN_ORDER;
ict->ict_op.op_data_sequence_in_order =
ISCSI_DEFAULT_DATA_SEQUENCE_IN_ORDER;
ict->ict_op.op_max_connections = ISCSI_DEFAULT_MAX_CONNECTIONS;
ict->ict_op.op_max_recv_data_segment_length =
ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
ict->ict_op.op_max_burst_length = ISCSI_DEFAULT_MAX_BURST_LENGTH;
ict->ict_op.op_first_burst_length = ISCSI_DEFAULT_FIRST_BURST_LENGTH;
ict->ict_op.op_default_time_2_wait = ISCSI_DEFAULT_TIME_TO_WAIT;
ict->ict_op.op_default_time_2_retain = ISCSI_DEFAULT_TIME_TO_RETAIN;
ict->ict_op.op_max_outstanding_r2t = ISCSI_DEFAULT_MAX_OUT_R2T;
ict->ict_op.op_error_recovery_level =
ISCSI_DEFAULT_ERROR_RECOVERY_LEVEL;
return (IDM_STATUS_SUCCESS);
}
static void
login_resp_complete_cb(idm_pdu_t *pdu, idm_status_t status)
{
iscsit_conn_t *ict = pdu->isp_private;
ASSERT((pdu->isp_flags & IDM_PDU_LOGIN_TX) != 0);
idm_pdu_free(pdu);
if ((status != IDM_STATUS_SUCCESS) ||
(ict->ict_login_sm.icl_login_resp_err_class != 0)) {
iscsit_login_sm_event(ict, ILE_LOGIN_ERROR, NULL);
}
iscsit_conn_rele(ict);
}
void
iscsit_login_sm_fini(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
mutex_enter(&lsm->icl_mutex);
list_destroy(&lsm->icl_pdu_list);
list_destroy(&lsm->icl_login_events);
kmem_free(lsm->icl_login_resp_tmpl, sizeof (iscsi_login_rsp_hdr_t));
if (lsm->icl_login_resp_itb != NULL) {
idm_itextbuf_free(lsm->icl_login_resp_itb);
lsm->icl_login_resp_itb = NULL;
}
nvlist_free(lsm->icl_negotiated_values);
mutex_destroy(&lsm->icl_mutex);
}
void
iscsit_login_sm_event(iscsit_conn_t *ict, iscsit_login_event_t event,
idm_pdu_t *pdu)
{
if ((ict->ict_login_sm.icl_login_state == ILS_LOGIN_ERROR) ||
(ict->ict_login_sm.icl_login_state == ILS_LOGIN_DONE))
return;
mutex_enter(&ict->ict_login_sm.icl_mutex);
iscsit_login_sm_event_locked(ict, event, pdu);
mutex_exit(&ict->ict_login_sm.icl_mutex);
}
void
iscsit_login_sm_event_locked(iscsit_conn_t *ict, iscsit_login_event_t event,
idm_pdu_t *pdu)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
login_event_ctx_t *ctx;
ASSERT(mutex_owned(&lsm->icl_mutex));
ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP);
ctx->le_ctx_event = event;
ctx->le_pdu = pdu;
list_insert_tail(&lsm->icl_login_events, ctx);
if (!lsm->icl_busy) {
lsm->icl_busy = B_TRUE;
while (!list_is_empty(&lsm->icl_login_events)) {
ctx = list_head(&lsm->icl_login_events);
list_remove(&lsm->icl_login_events, ctx);
idm_sm_audit_event(&lsm->icl_state_audit,
SAS_ISCSIT_LOGIN, (int)lsm->icl_login_state,
(int)ctx->le_ctx_event, (uintptr_t)pdu);
if ((lsm->icl_login_state == ILS_LOGIN_ERROR) ||
(lsm->icl_login_state == ILS_LOGIN_DONE)) {
kmem_free(ctx, sizeof (*ctx));
continue;
}
mutex_exit(&lsm->icl_mutex);
login_sm_event_dispatch(lsm, ict, ctx);
mutex_enter(&lsm->icl_mutex);
}
lsm->icl_busy = B_FALSE;
if (lsm->icl_login_complete) {
lsm->icl_busy = B_TRUE;
if (taskq_dispatch(iscsit_global.global_dispatch_taskq,
login_sm_complete, ict, DDI_SLEEP) ==
TASKQID_INVALID) {
cmn_err(CE_WARN, "iscsit_login_sm_event_locked:"
" Failed to dispatch task");
}
}
}
}
static void
login_sm_complete(void *ict_void)
{
iscsit_conn_t *ict = ict_void;
iscsit_conn_rele(ict);
}
static void
login_sm_event_dispatch(iscsit_conn_login_t *lsm, iscsit_conn_t *ict,
login_event_ctx_t *ctx)
{
idm_pdu_t *pdu = ctx->le_pdu;
DTRACE_PROBE2(login__event, iscsit_conn_t *, ict,
login_event_ctx_t *, ctx);
IDM_SM_LOG(CE_NOTE, "login_sm_event_dispatch: ict %p event %s(%d)",
(void *)ict,
iscsit_ile_name[ctx->le_ctx_event], ctx->le_ctx_event);
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RCV:
if (login_sm_req_pdu_check(ict, pdu) != IDM_STATUS_SUCCESS) {
idm_pdu_t *rpdu;
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_INVALID_REQUEST);
if (ict->ict_login_sm.icl_login_resp_tmpl->opcode == 0)
login_sm_handle_initial_login(ict, pdu);
rpdu = login_sm_build_login_response(ict);
login_sm_send_next_response(ict, rpdu);
idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
kmem_free(ctx, sizeof (*ctx));
return;
}
break;
default:
break;
}
switch (lsm->icl_login_state) {
case ILS_LOGIN_INIT:
login_sm_init(ict, ctx);
break;
case ILS_LOGIN_WAITING:
login_sm_waiting(ict, ctx);
break;
case ILS_LOGIN_PROCESSING:
login_sm_processing(ict, ctx);
break;
case ILS_LOGIN_RESPONDING:
login_sm_responding(ict, ctx);
break;
case ILS_LOGIN_RESPONDED:
login_sm_responded(ict, ctx);
break;
case ILS_LOGIN_FFP:
login_sm_ffp(ict, ctx);
break;
case ILS_LOGIN_DONE:
login_sm_done(ict, ctx);
break;
case ILS_LOGIN_ERROR:
login_sm_error(ict, ctx);
break;
}
kmem_free(ctx, sizeof (*ctx));
}
static void
login_sm_init(iscsit_conn_t *ict, login_event_ctx_t *ctx)
{
idm_pdu_t *pdu;
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RCV:
pdu = ctx->le_pdu;
login_sm_handle_initial_login(ict, pdu);
mutex_enter(&ict->ict_login_sm.icl_mutex);
list_insert_tail(&ict->ict_login_sm.icl_pdu_list, pdu);
mutex_exit(&ict->ict_login_sm.icl_mutex);
if (pdu->isp_hdr->flags & ISCSI_FLAG_LOGIN_CONTINUE) {
login_sm_send_ack(ict, pdu);
login_sm_new_state(ict, ctx, ILS_LOGIN_WAITING);
} else {
login_sm_new_state(ict, ctx, ILS_LOGIN_PROCESSING);
}
break;
case ILE_LOGIN_CONN_ERROR:
case ILE_LOGIN_ERROR:
login_sm_new_state(ict, ctx, ILS_LOGIN_ERROR);
break;
default:
ASSERT(0);
}
}
static void
login_sm_waiting(iscsit_conn_t *ict, login_event_ctx_t *ctx)
{
idm_pdu_t *pdu;
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RCV:
pdu = ctx->le_pdu;
mutex_enter(&ict->ict_login_sm.icl_mutex);
list_insert_tail(&ict->ict_login_sm.icl_pdu_list, pdu);
mutex_exit(&ict->ict_login_sm.icl_mutex);
if (!(pdu->isp_hdr->flags & ISCSI_FLAG_LOGIN_CONTINUE)) {
login_sm_new_state(ict, ctx, ILS_LOGIN_PROCESSING);
} else {
login_sm_send_ack(ict, pdu);
}
break;
case ILE_LOGIN_ERROR:
login_sm_new_state(ict, ctx, ILS_LOGIN_ERROR);
break;
case ILE_LOGIN_RESP_COMPLETE:
break;
default:
ASSERT(0);
}
}
static void
login_sm_processing(iscsit_conn_t *ict, login_event_ctx_t *ctx)
{
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RESP_READY:
login_sm_new_state(ict, ctx, ILS_LOGIN_RESPONDING);
break;
case ILE_LOGIN_RCV:
idm_pdu_complete(ctx->le_pdu, IDM_STATUS_SUCCESS);
case ILE_LOGIN_CONN_ERROR:
case ILE_LOGIN_ERROR:
login_sm_new_state(ict, ctx, ILS_LOGIN_ERROR);
break;
default:
ASSERT(0);
}
}
static void
login_sm_responding(iscsit_conn_t *ict, login_event_ctx_t *ctx)
{
idm_pdu_t *pdu, *rpdu;
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RCV:
pdu = ctx->le_pdu;
if (login_sm_validate_ack(ict, pdu) == IDM_STATUS_SUCCESS) {
rpdu = login_sm_build_login_response(ict);
login_sm_send_next_response(ict, rpdu);
} else {
login_sm_new_state(ict, ctx, ILS_LOGIN_ERROR);
}
idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
break;
case ILE_LOGIN_FFP:
login_sm_new_state(ict, ctx, ILS_LOGIN_FFP);
break;
case ILE_LOGIN_RESP_COMPLETE:
login_sm_new_state(ict, ctx, ILS_LOGIN_RESPONDED);
break;
case ILE_LOGIN_CONN_ERROR:
case ILE_LOGIN_ERROR:
login_sm_new_state(ict, ctx, ILS_LOGIN_ERROR);
break;
default:
ASSERT(0);
}
}
static void
login_sm_responded(iscsit_conn_t *ict, login_event_ctx_t *ctx)
{
idm_pdu_t *pdu;
iscsi_login_hdr_t *lh;
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RCV:
pdu = ctx->le_pdu;
lh = (iscsi_login_hdr_t *)pdu->isp_hdr;
ict->ict_login_sm.icl_login_csg =
ISCSI_LOGIN_CURRENT_STAGE(lh->flags);
ict->ict_login_sm.icl_login_nsg =
ISCSI_LOGIN_NEXT_STAGE(lh->flags);
ict->ict_login_sm.icl_login_transit =
lh->flags & ISCSI_FLAG_LOGIN_TRANSIT;
mutex_enter(&ict->ict_login_sm.icl_mutex);
list_insert_tail(&ict->ict_login_sm.icl_pdu_list, pdu);
mutex_exit(&ict->ict_login_sm.icl_mutex);
if (pdu->isp_hdr->flags & ISCSI_FLAG_LOGIN_CONTINUE) {
login_sm_send_ack(ict, pdu);
login_sm_new_state(ict, ctx, ILS_LOGIN_WAITING);
} else {
login_sm_new_state(ict, ctx, ILS_LOGIN_PROCESSING);
}
break;
case ILE_LOGIN_CONN_ERROR:
case ILE_LOGIN_ERROR:
login_sm_new_state(ict, ctx, ILS_LOGIN_ERROR);
break;
default:
ASSERT(0);
}
}
static void
login_sm_ffp(iscsit_conn_t *ict, login_event_ctx_t *ctx)
{
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RESP_COMPLETE:
login_sm_new_state(ict, ctx, ILS_LOGIN_DONE);
break;
case ILE_LOGIN_CONN_ERROR:
case ILE_LOGIN_ERROR:
login_sm_new_state(ict, ctx, ILS_LOGIN_ERROR);
break;
default:
ASSERT(0);
}
}
static void
login_sm_done(iscsit_conn_t *ict, login_event_ctx_t *ctx)
{
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RCV:
idm_pdu_complete(ctx->le_pdu, IDM_STATUS_SUCCESS);
break;
case ILE_LOGIN_CONN_ERROR:
break;
default:
ASSERT(0);
}
}
static void
login_sm_error(iscsit_conn_t *ict, login_event_ctx_t *ctx)
{
switch (ctx->le_ctx_event) {
case ILE_LOGIN_RCV:
idm_pdu_complete(ctx->le_pdu, IDM_STATUS_SUCCESS);
break;
case ILE_LOGIN_CONN_ERROR:
break;
default:
ASSERT(0);
}
}
static void
login_sm_new_state(iscsit_conn_t *ict, login_event_ctx_t *ctx,
iscsit_login_state_t new_state)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
idm_pdu_t *rpdu;
ASSERT(new_state != ILS_UNDEFINED);
ASSERT3U(new_state, <, ILS_MAX_STATE);
new_state = (new_state < ILS_MAX_STATE) ?
new_state : ILS_UNDEFINED;
IDM_SM_LOG(CE_NOTE, "login_sm_new_state: conn %p "
"%s (%d) --> %s (%d)\n", (void *)ict->ict_ic,
iscsit_ils_name[lsm->icl_login_state], lsm->icl_login_state,
iscsit_ils_name[new_state], new_state);
DTRACE_PROBE3(login__state__change,
iscsit_conn_t *, ict, login_event_ctx_t *, ctx,
iscsit_login_state_t, new_state);
mutex_enter(&lsm->icl_mutex);
idm_sm_audit_state_change(&lsm->icl_state_audit, SAS_ISCSIT_LOGIN,
(int)lsm->icl_login_state, (int)new_state);
lsm->icl_login_last_state = lsm->icl_login_state;
lsm->icl_login_state = new_state;
mutex_exit(&lsm->icl_mutex);
switch (new_state) {
case ILS_LOGIN_WAITING:
break;
case ILS_LOGIN_PROCESSING:
login_sm_process_request(ict);
break;
case ILS_LOGIN_RESPONDING:
rpdu = login_sm_build_login_response(ict);
login_sm_send_next_response(ict, rpdu);
break;
case ILS_LOGIN_RESPONDED:
if (lsm->icl_login_resp_itb != NULL) {
idm_itextbuf_free(lsm->icl_login_resp_itb);
lsm->icl_login_resp_itb = NULL;
}
break;
case ILS_LOGIN_FFP:
login_sm_ffp_actions(ict);
break;
case ILS_LOGIN_DONE:
case ILS_LOGIN_ERROR:
lsm->icl_login_complete = B_TRUE;
break;
case ILS_LOGIN_INIT:
default:
ASSERT(0);
}
}
static void
login_sm_send_ack(iscsit_conn_t *ict, idm_pdu_t *pdu)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
idm_pdu_t *lack;
lack = idm_pdu_alloc(sizeof (iscsi_hdr_t), 0);
idm_pdu_init(lack, ict->ict_ic, ict, login_resp_complete_cb);
lack->isp_flags |= IDM_PDU_LOGIN_TX;
bcopy(lsm->icl_login_resp_tmpl, lack->isp_hdr, sizeof (iscsi_hdr_t));
iscsit_conn_hold(ict);
idm_pdu_tx(lack);
}
static idm_status_t
login_sm_validate_ack(iscsit_conn_t *ict, idm_pdu_t *pdu)
{
iscsi_hdr_t *ihp = pdu->isp_hdr;
if (ihp->flags & ISCSI_FLAG_TEXT_CONTINUE) {
return (IDM_STATUS_FAIL);
}
if (ntoh24(ihp->dlength) != 0) {
return (IDM_STATUS_FAIL);
}
return (IDM_STATUS_SUCCESS);
}
static boolean_t
login_sm_is_last_response(idm_pdu_t *pdu)
{
if (pdu->isp_hdr->flags & ISCSI_FLAG_LOGIN_CONTINUE) {
return (B_FALSE);
}
return (B_TRUE);
}
static void
login_sm_handle_initial_login(iscsit_conn_t *ict, idm_pdu_t *pdu)
{
iscsi_login_hdr_t *lh_req = (iscsi_login_hdr_t *)pdu->isp_hdr;
iscsi_login_rsp_hdr_t *lh_resp =
ict->ict_login_sm.icl_login_resp_tmpl;
ASSERT(ict->ict_sess == NULL);
ict->ict_login_sm.icl_cmdsn = ntohl(lh_req->cmdsn);
ict->ict_login_sm.icl_tsih = ntohs(lh_req->tsid);
bcopy(lh_req->isid, ict->ict_login_sm.icl_isid, ISCSI_ISID_LEN);
ict->ict_cid = ntohs(lh_req->cid);
ict->ict_login_sm.icl_login_csg =
ISCSI_LOGIN_CURRENT_STAGE(lh_req->flags);
ict->ict_login_sm.icl_login_nsg =
ISCSI_LOGIN_NEXT_STAGE(lh_req->flags);
ict->ict_login_sm.icl_login_transit =
lh_req->flags & ISCSI_FLAG_LOGIN_TRANSIT;
lh_resp->opcode = ISCSI_OP_LOGIN_RSP;
lh_resp->max_version = ISCSIT_MAX_VERSION;
#if (ISCSIT_MAX_VERSION > 0)
if (ISCSIT_MAX_VERSION >= lh_req->min_version) {
lh_resp->active_version =
MIN(lh_req->max_version, ISCSIT_MAX_VERSION);
} else {
ASSERT(ISCSIT_MAX_VERSION <= lh_req->max_version);
lh_resp->active_version = ISCSIT_MAX_VERSION;
}
#endif
lh_resp->hlength = 0;
bcopy(lh_req->isid, lh_resp->isid, ISCSI_ISID_LEN);
lh_resp->tsid = lh_req->tsid;
lh_resp->itt = lh_req->itt;
}
static void
login_sm_send_next_response(iscsit_conn_t *ict, idm_pdu_t *pdu)
{
iscsi_login_rsp_hdr_t *lh_resp = (iscsi_login_rsp_hdr_t *)pdu->isp_hdr;
ASSERT((pdu->isp_flags & IDM_PDU_LOGIN_TX) != 0);
hton24(lh_resp->dlength, pdu->isp_datalen);
if (lh_resp->status_class == ISCSI_STATUS_CLASS_SUCCESS) {
ASSERT(ict->ict_sess != NULL);
if ((lh_resp->flags & ISCSI_FLAG_LOGIN_TRANSIT) &&
(ISCSI_LOGIN_NEXT_STAGE(lh_resp->flags) ==
ISCSI_FULL_FEATURE_PHASE) &&
!(lh_resp->flags & ISCSI_FLAG_LOGIN_CONTINUE)) {
iscsit_login_sm_event(ict, ILE_LOGIN_FFP, NULL);
}
if (login_sm_is_last_response(pdu) == B_TRUE) {
iscsit_login_sm_event(ict, ILE_LOGIN_RESP_COMPLETE,
NULL);
}
iscsit_conn_hold(ict);
pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
iscsit_pdu_tx(pdu);
} else {
lh_resp->expcmdsn = htonl(ict->ict_login_sm.icl_cmdsn);
lh_resp->maxcmdsn = htonl(ict->ict_login_sm.icl_cmdsn + 1);
iscsit_conn_hold(ict);
idm_pdu_tx(pdu);
}
}
static void
login_sm_process_request(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
uint8_t error_class = 0;
uint8_t error_detail = 0;
ASSERT(lsm->icl_request_nvlist == NULL);
if (idm_pdu_list_to_nvlist(&lsm->icl_pdu_list,
&lsm->icl_request_nvlist, &error_detail) != IDM_STATUS_SUCCESS) {
error_class = ISCSI_STATUS_CLASS_TARGET_ERR;
SET_LOGIN_ERROR(ict, error_class, error_detail);
goto request_fail;
}
ASSERT(lsm->icl_response_nvlist == NULL);
if (nvlist_alloc(&lsm->icl_response_nvlist, NV_UNIQUE_NAME,
KM_NOSLEEP) != 0) {
error_class = ISCSI_STATUS_CLASS_TARGET_ERR;
error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
SET_LOGIN_ERROR(ict, error_class, error_detail);
goto request_fail;
}
if (!ict->ict_op.op_initial_params_set) {
if (login_sm_validate_initial_parameters(ict) !=
IDM_STATUS_SUCCESS) {
goto request_fail;
}
if (login_sm_session_bind(ict) != IDM_STATUS_SUCCESS) {
goto request_fail;
}
if (login_sm_set_auth(ict) != IDM_STATUS_SUCCESS) {
goto request_fail;
}
if (ict->ict_op.op_discovery_session == B_FALSE) {
if ((lsm->icl_auth.ca_tgt_alias[0]) != '\0') {
(void) iscsit_reply_string(ict,
"TargetAlias",
&lsm->icl_auth.ca_tgt_alias[0]);
}
(void) iscsit_reply_numerical(ict,
"TargetPortalGroupTag",
(uint64_t)lsm->icl_tpgt_tag);
}
ict->ict_op.op_initial_params_set = B_TRUE;
}
if (login_sm_process_nvlist(ict) != IDM_STATUS_SUCCESS) {
goto request_fail;
}
if (login_sm_check_security(ict) != IDM_STATUS_SUCCESS) {
goto request_fail;
}
if (lsm->icl_request_nvlist != NULL) {
nvlist_free(lsm->icl_request_nvlist);
lsm->icl_request_nvlist = NULL;
}
ASSERT(lsm->icl_login_resp_itb == NULL);
if (lsm->icl_response_nvlist) {
lsm->icl_login_resp_itb = idm_nvlist_to_itextbuf(
lsm->icl_response_nvlist);
if (lsm->icl_login_resp_itb == NULL) {
SET_LOGIN_ERROR(ict,
ISCSI_STATUS_CLASS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
}
nvlist_free(lsm->icl_response_nvlist);
lsm->icl_response_nvlist = NULL;
}
iscsit_login_sm_event(ict, ILE_LOGIN_RESP_READY, NULL);
return;
request_fail:
if (lsm->icl_request_nvlist != NULL) {
nvlist_free(lsm->icl_request_nvlist);
lsm->icl_request_nvlist = NULL;
}
if (lsm->icl_response_nvlist != NULL) {
nvlist_free(lsm->icl_response_nvlist);
lsm->icl_response_nvlist = NULL;
}
if (ict->ict_login_sm.icl_login_resp_err_class ==
ISCSI_STATUS_CLASS_SUCCESS) {
SET_LOGIN_ERROR(ict,
ISCSI_STATUS_CLASS_TARGET_ERR,
ISCSI_LOGIN_STATUS_TARGET_ERROR);
}
iscsit_login_sm_event(ict, ILE_LOGIN_RESP_READY, NULL);
}
static void
login_sm_ffp_actions(iscsit_conn_t *ict)
{
iscsit_process_negotiated_values(ict);
}
static idm_status_t
login_sm_validate_initial_parameters(iscsit_conn_t *ict)
{
int nvrc;
char *string_val;
char *u8_iscsi_name;
size_t u8_iscsi_name_len;
uint8_t error_class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
uint8_t error_detail = ISCSI_LOGIN_STATUS_MISSING_FIELDS;
idm_status_t status = IDM_STATUS_FAIL;
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
if ((nvrc = nvlist_lookup_string(lsm->icl_request_nvlist,
"InitiatorName", &string_val)) != 0) {
goto initial_params_done;
}
u8_iscsi_name = iscsit_fold_name(string_val, &u8_iscsi_name_len);
if (u8_iscsi_name == NULL)
goto initial_params_done;
nvrc = nvlist_add_string(lsm->icl_negotiated_values, "InitiatorName",
u8_iscsi_name);
kmem_free(u8_iscsi_name, u8_iscsi_name_len);
if (nvrc != 0)
goto initial_params_done;
if ((nvrc = nvlist_lookup_string(lsm->icl_negotiated_values,
"InitiatorName", &string_val)) != 0) {
goto initial_params_done;
}
lsm->icl_initiator_name = string_val;
idm_conn_set_initiator_name(ict->ict_ic, lsm->icl_initiator_name);
if ((nvrc = nvlist_remove(lsm->icl_request_nvlist,
"InitiatorName", DATA_TYPE_STRING)) != 0) {
goto initial_params_done;
}
ict->ict_op.op_discovery_session = B_FALSE;
nvrc = nvlist_lookup_string(lsm->icl_request_nvlist,
"SessionType", &string_val);
if (nvrc != ENOENT && nvrc != 0) {
goto initial_params_done;
}
if (nvrc == 0) {
if (strcmp(string_val, "Discovery") == 0) {
ict->ict_op.op_discovery_session = B_TRUE;
} else if (strcmp(string_val, "Normal") != 0) {
goto initial_params_done;
}
if ((nvrc = nvlist_add_string(lsm->icl_negotiated_values,
"SessionType", string_val)) != 0) {
goto initial_params_done;
}
if ((nvrc = nvlist_remove(lsm->icl_request_nvlist,
"SessionType", DATA_TYPE_STRING)) != 0) {
goto initial_params_done;
}
}
lsm->icl_target_name = NULL;
nvrc = nvlist_lookup_string(lsm->icl_request_nvlist,
"TargetName", &string_val);
if (nvrc != ENOENT && nvrc != 0) {
goto initial_params_done;
}
if (nvrc == 0) {
u8_iscsi_name = iscsit_fold_name(string_val,
&u8_iscsi_name_len);
if (u8_iscsi_name == NULL)
goto initial_params_done;
nvrc = nvlist_add_string(lsm->icl_negotiated_values,
"TargetName", u8_iscsi_name);
kmem_free(u8_iscsi_name, u8_iscsi_name_len);
if (nvrc != 0)
goto initial_params_done;
if ((nvrc = nvlist_lookup_string(lsm->icl_negotiated_values,
"TargetName", &string_val)) != 0) {
goto initial_params_done;
}
lsm->icl_target_name = string_val;
idm_conn_set_target_name(ict->ict_ic, lsm->icl_target_name);
if ((nvrc = nvlist_remove(lsm->icl_request_nvlist,
"TargetName", DATA_TYPE_STRING)) != 0) {
goto initial_params_done;
}
} else if (ict->ict_op.op_discovery_session == B_FALSE) {
goto initial_params_done;
}
idm_conn_set_isid(ict->ict_ic, lsm->icl_isid);
(void) snprintf(ict->ict_ic->ic_tsih, ISCSI_MAX_TSIH_LEN + 1, "0x%04x",
lsm->icl_tsih);
IDM_SM_LOG(CE_NOTE, "conn %p: initiator=%s", (void *)ict->ict_ic,
(lsm->icl_initiator_name == NULL) ? "N/A" :
lsm->icl_initiator_name);
IDM_SM_LOG(CE_NOTE, "conn %p: target=%s", (void *)ict->ict_ic,
(lsm->icl_target_name == NULL) ? "N/A" :
lsm->icl_target_name);
IDM_SM_LOG(CE_NOTE, "conn %p: sessiontype=%s", (void *)ict->ict_ic,
ict->ict_op.op_discovery_session ? "Discovery" : "Normal");
status = IDM_STATUS_SUCCESS;
error_class = ISCSI_STATUS_CLASS_SUCCESS;
error_detail = ISCSI_LOGIN_STATUS_ACCEPT;
initial_params_done:
SET_LOGIN_ERROR(ict, error_class, error_detail);
return (status);
}
int
iscsit_is_v4_mapped(struct sockaddr_storage *sa, struct sockaddr_storage *v4sa)
{
struct sockaddr_in *sin;
struct in_addr *in;
struct sockaddr_in6 *sin6;
struct in6_addr *in6;
int ret = 0;
sin6 = (struct sockaddr_in6 *)sa;
in6 = &sin6->sin6_addr;
if ((sa->ss_family == AF_INET6) &&
(IN6_IS_ADDR_V4MAPPED(in6) || IN6_IS_ADDR_V4COMPAT(in6))) {
sin = (struct sockaddr_in *)v4sa;
in = &sin->sin_addr;
v4sa->ss_family = AF_INET;
sin->sin_port = sin6->sin6_port;
IN6_V4MAPPED_TO_INADDR(in6, in);
ret = 1;
}
return (ret);
}
static idm_status_t
login_sm_session_bind(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_tgt_t *tgt = NULL;
iscsit_tpgt_t *tpgt = NULL;
iscsit_portal_t *portal = NULL;
iscsit_sess_t *existing_sess = NULL;
iscsit_sess_t *new_sess = NULL;
iscsit_conn_t *existing_ict = NULL;
uint8_t error_class;
uint8_t error_detail;
mutex_enter(&login_sm_session_mutex);
if (lsm->icl_target_name != NULL) {
ISCSIT_GLOBAL_LOCK(RW_READER);
tgt = iscsit_tgt_lookup_locked(lsm->icl_target_name);
if (tgt == NULL) {
ISCSIT_GLOBAL_UNLOCK();
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_TGT_NOT_FOUND);
goto session_bind_error;
} else {
mutex_enter(&tgt->target_mutex);
tpgt = avl_first(&tgt->target_tpgt_list);
if (IS_DEFAULT_TPGT(tpgt)) {
lsm->icl_tpgt_tag = ISCSIT_DEFAULT_TPGT;
} else {
struct sockaddr_storage v4sa, *sa;
sa = &ict->ict_ic->ic_laddr;
portal = iscsit_tgt_lookup_portal(tgt,
sa, &tpgt);
if (portal == NULL &&
iscsit_is_v4_mapped(sa, &v4sa)) {
portal = iscsit_tgt_lookup_portal(tgt,
&v4sa, &tpgt);
}
if (portal == NULL) {
SET_LOGIN_ERROR(ict,
ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_TGT_NOT_FOUND);
mutex_exit(&tgt->target_mutex);
ISCSIT_GLOBAL_UNLOCK();
goto session_bind_error;
}
lsm->icl_tpgt_tag = tpgt->tpgt_tag;
iscsit_portal_rele(portal);
iscsit_tpgt_rele(tpgt);
}
mutex_enter(&iscsit_global.global_state_mutex);
if ((tgt->target_state != TS_STMF_ONLINE) ||
((iscsit_global.global_svc_state != ISE_ENABLED) &&
((iscsit_global.global_svc_state != ISE_BUSY)))) {
mutex_exit(&iscsit_global.global_state_mutex);
SET_LOGIN_ERROR(ict,
ISCSI_STATUS_CLASS_TARGET_ERR,
ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
mutex_exit(&tgt->target_mutex);
ISCSIT_GLOBAL_UNLOCK();
goto session_bind_error;
}
mutex_exit(&iscsit_global.global_state_mutex);
mutex_exit(&tgt->target_mutex);
ISCSIT_GLOBAL_UNLOCK();
}
}
ASSERT((tgt != NULL) || (ict->ict_op.op_discovery_session == B_TRUE));
existing_sess = iscsit_tgt_lookup_sess(tgt, lsm->icl_initiator_name,
lsm->icl_isid, lsm->icl_tsih, lsm->icl_tpgt_tag);
if (existing_sess != NULL) {
existing_ict = iscsit_sess_lookup_conn(existing_sess,
ict->ict_cid);
}
if ((ict->ict_op.op_discovery_session == B_TRUE) &&
((lsm->icl_tsih != ISCSI_UNSPEC_TSIH) || (existing_sess != NULL))) {
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_INVALID_REQUEST);
goto session_bind_error;
}
if ((existing_sess == NULL) && (lsm->icl_tsih != ISCSI_UNSPEC_TSIH)) {
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_NO_SESSION);
goto session_bind_error;
}
if ((existing_sess != NULL) && (lsm->icl_tsih != 0) &&
(existing_sess->ist_tsih != lsm->icl_tsih)) {
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_NO_SESSION);
goto session_bind_error;
}
if (existing_sess == NULL) {
ASSERT(lsm->icl_tsih == ISCSI_UNSPEC_TSIH);
new_sess = iscsit_sess_create(tgt, ict, lsm->icl_cmdsn,
lsm->icl_isid, lsm->icl_tpgt_tag, lsm->icl_initiator_name,
lsm->icl_target_name, &error_class, &error_detail);
ASSERT(new_sess != NULL);
if (error_class != ISCSI_STATUS_CLASS_SUCCESS) {
SET_LOGIN_ERROR(ict, error_class, error_detail);
goto session_bind_error;
}
if (!ict->ict_op.op_discovery_session) {
if (login_sm_session_register(ict) !=
IDM_STATUS_SUCCESS) {
goto session_bind_error;
}
}
} else {
if (lsm->icl_tsih == ISCSI_UNSPEC_TSIH) {
new_sess = iscsit_sess_reinstate(tgt, existing_sess,
ict, &error_class, &error_detail);
ASSERT(new_sess != NULL);
if (error_class != ISCSI_STATUS_CLASS_SUCCESS) {
SET_LOGIN_ERROR(ict, error_class, error_detail);
goto session_bind_error;
}
if (!ict->ict_op.op_discovery_session) {
if (login_sm_session_register(ict) !=
IDM_STATUS_SUCCESS) {
goto session_bind_error;
}
}
} else {
cmn_err(CE_NOTE, "login_sm_session_bind: add new "
"conn/sess continue");
if (existing_ict != NULL) {
if (iscsit_conn_reinstate(existing_ict, ict) !=
IDM_STATUS_SUCCESS) {
SET_LOGIN_ERROR(ict,
ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_INIT_ERR);
goto session_bind_error;
}
}
iscsit_sess_sm_event(existing_sess, SE_CONN_IN_LOGIN,
ict);
}
}
if (tgt != NULL)
iscsit_tgt_rele(tgt);
if (existing_sess != NULL)
iscsit_sess_rele(existing_sess);
if (existing_ict != NULL)
iscsit_conn_rele(existing_ict);
mutex_exit(&login_sm_session_mutex);
return (IDM_STATUS_SUCCESS);
session_bind_error:
if (tgt != NULL)
iscsit_tgt_rele(tgt);
if (existing_sess != NULL)
iscsit_sess_rele(existing_sess);
if (existing_ict != NULL)
iscsit_conn_rele(existing_ict);
mutex_exit(&login_sm_session_mutex);
return (IDM_STATUS_FAIL);
}
static idm_status_t
login_sm_set_auth(iscsit_conn_t *ict)
{
idm_status_t idmrc = IDM_STATUS_SUCCESS;
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_ini_t *ini;
iscsit_tgt_t *tgt;
char *auth = "";
char *radiusserver = "";
char *radiussecret = "";
char *chapuser = "";
char *chapsecret = "";
char *targetchapuser = "";
char *targetchapsecret = "";
char *targetalias = "";
int i;
ISCSIT_GLOBAL_LOCK(RW_READER);
if (ict->ict_op.op_discovery_session == B_TRUE) {
lsm->icl_auth.ca_method_valid_list[0] = AM_NONE;
ISCSIT_GLOBAL_UNLOCK();
return (idmrc);
}
(void) nvlist_lookup_string(iscsit_global.global_props,
PROP_AUTH, &auth);
(void) nvlist_lookup_string(iscsit_global.global_props,
PROP_RADIUS_SERVER, &radiusserver);
(void) nvlist_lookup_string(iscsit_global.global_props,
PROP_RADIUS_SECRET, &radiussecret);
ini = iscsit_ini_lookup_locked(lsm->icl_initiator_name);
if (ini != NULL) {
(void) nvlist_lookup_string(ini->ini_props, PROP_CHAP_USER,
&chapuser);
(void) nvlist_lookup_string(ini->ini_props, PROP_CHAP_SECRET,
&chapsecret);
}
tgt = ict->ict_sess->ist_tgt;
if (tgt != NULL) {
(void) nvlist_lookup_string(tgt->target_props, PROP_AUTH,
&auth);
(void) nvlist_lookup_string(tgt->target_props,
PROP_TARGET_CHAP_USER, &targetchapuser);
(void) nvlist_lookup_string(tgt->target_props,
PROP_TARGET_CHAP_SECRET, &targetchapsecret);
(void) nvlist_lookup_string(tgt->target_props,
PROP_ALIAS, &targetalias);
}
i = 0;
if (strcmp(auth, PA_AUTH_RADIUS) == 0) {
lsm->icl_auth.ca_method_valid_list[i++] = AM_CHAP;
lsm->icl_auth.ca_use_radius = B_TRUE;
} else if (strcmp(auth, PA_AUTH_CHAP) == 0) {
lsm->icl_auth.ca_method_valid_list[i++] = AM_CHAP;
lsm->icl_auth.ca_use_radius = B_FALSE;
} else if ((strcmp(auth, PA_AUTH_NONE) == 0) ||
(strcmp(auth, "") == 0)) {
lsm->icl_auth.ca_method_valid_list[i++] = AM_NONE;
}
if (strcmp(chapuser, "") == 0) {
(void) strlcpy(lsm->icl_auth.ca_ini_chapuser,
lsm->icl_initiator_name,
min(iscsitAuthStringMaxLength, MAX_ISCSI_NODENAMELEN));
} else {
(void) strlcpy(lsm->icl_auth.ca_ini_chapuser, chapuser,
iscsitAuthStringMaxLength);
}
if ((lsm->icl_target_name != NULL) &&
(strcmp(targetchapuser, "") == 0)) {
(void) strlcpy(lsm->icl_auth.ca_tgt_chapuser,
lsm->icl_target_name,
min(iscsitAuthStringMaxLength, MAX_ISCSI_NODENAMELEN));
} else {
(void) strlcpy(lsm->icl_auth.ca_tgt_chapuser,
targetchapuser, iscsitAuthStringMaxLength);
}
if (strcmp(chapsecret, "") == 0) {
lsm->icl_auth.ca_ini_chapsecretlen = 0;
} else {
if (iscsi_base64_str_to_binary(chapsecret,
strnlen(chapsecret, iscsitAuthStringMaxLength),
lsm->icl_auth.ca_ini_chapsecret, iscsitAuthStringMaxLength,
&lsm->icl_auth.ca_ini_chapsecretlen) != 0) {
cmn_err(CE_WARN, "Corrupted CHAP secret"
" for initiator %s", lsm->icl_initiator_name);
lsm->icl_auth.ca_ini_chapsecretlen = 0;
}
}
if (strcmp(targetchapsecret, "") == 0) {
lsm->icl_auth.ca_tgt_chapsecretlen = 0;
} else {
if (iscsi_base64_str_to_binary(targetchapsecret,
strnlen(targetchapsecret, iscsitAuthStringMaxLength),
lsm->icl_auth.ca_tgt_chapsecret, iscsitAuthStringMaxLength,
&lsm->icl_auth.ca_tgt_chapsecretlen) != 0) {
cmn_err(CE_WARN, "Corrupted CHAP secret"
" for target %s", lsm->icl_target_name);
lsm->icl_auth.ca_tgt_chapsecretlen = 0;
}
}
if (strcmp(radiussecret, "") == 0) {
lsm->icl_auth.ca_radius_secretlen = 0;
} else {
if (iscsi_base64_str_to_binary(radiussecret,
strnlen(radiussecret, iscsitAuthStringMaxLength),
lsm->icl_auth.ca_radius_secret, iscsitAuthStringMaxLength,
&lsm->icl_auth.ca_radius_secretlen) != 0) {
cmn_err(CE_WARN, "Corrupted RADIUS secret");
lsm->icl_auth.ca_radius_secretlen = 0;
}
}
(void) strlcpy(lsm->icl_auth.ca_tgt_alias, targetalias,
MAX_ISCSI_NODENAMELEN);
if ((strcmp(auth, PA_AUTH_RADIUS) == 0) &&
((lsm->icl_auth.ca_radius_secretlen == 0) ||
(strcmp(radiusserver, "") == 0) ||
it_common_convert_sa(radiusserver,
&lsm->icl_auth.ca_radius_server,
DEFAULT_RADIUS_PORT) == NULL)) {
cmn_err(CE_WARN, "RADIUS authentication selected "
"for target %s but RADIUS parameters are not "
"configured.", lsm->icl_target_name);
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_TARGET_ERR,
ISCSI_LOGIN_STATUS_TARGET_ERROR);
idmrc = IDM_STATUS_FAIL;
} else if ((strcmp(auth, PA_AUTH_CHAP) == 0) &&
(lsm->icl_auth.ca_ini_chapsecretlen == 0)) {
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_AUTH_FAILED);
idmrc = IDM_STATUS_FAIL;
}
ISCSIT_GLOBAL_UNLOCK();
return (idmrc);
}
static idm_status_t
login_sm_session_register(iscsit_conn_t *ict)
{
iscsit_sess_t *ist = ict->ict_sess;
stmf_scsi_session_t *ss;
iscsi_transport_id_t *iscsi_tptid;
uint16_t ident_len, adn_len, tptid_sz;
char prop_buf[KSTAT_STRLEN + 1];
char peer_buf[IDM_SA_NTOP_BUFSIZ];
mutex_enter(&ist->ist_tgt->target_mutex);
if (ist->ist_tgt->target_state != TS_STMF_ONLINE) {
mutex_exit(&ist->ist_tgt->target_mutex);
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_TGT_REMOVED);
return (IDM_STATUS_FAIL);
}
ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0,
0);
if (ss == NULL) {
mutex_exit(&ist->ist_tgt->target_mutex);
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
return (IDM_STATUS_FAIL);
}
ident_len = strlen(ist->ist_initiator_name) + 1;
ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) +
ident_len, KM_SLEEP);
(void) strcpy((char *)ss->ss_rport_id->ident, ist->ist_initiator_name);
ss->ss_rport_id->ident_length = ident_len - 1;
ss->ss_rport_id->protocol_id = PROTOCOL_iSCSI;
ss->ss_rport_id->piv = 1;
ss->ss_rport_id->code_set = CODE_SET_ASCII;
ss->ss_rport_id->association = ID_IS_TARGET_PORT;
adn_len = (ident_len + 3) & ~ 3;
tptid_sz = sizeof (iscsi_transport_id_t) - 1 + adn_len;
ss->ss_rport = stmf_remote_port_alloc(tptid_sz);
ss->ss_rport->rport_tptid->protocol_id = PROTOCOL_iSCSI;
ss->ss_rport->rport_tptid->format_code = 0;
iscsi_tptid = (iscsi_transport_id_t *)ss->ss_rport->rport_tptid;
SCSI_WRITE16(&iscsi_tptid->add_len, adn_len);
(void) strlcpy((char *)iscsi_tptid->iscsi_name,
ist->ist_initiator_name, ident_len);
ss->ss_lport = ist->ist_lport;
if (stmf_register_scsi_session(ict->ict_sess->ist_lport, ss) !=
STMF_SUCCESS) {
mutex_exit(&ist->ist_tgt->target_mutex);
kmem_free(ss->ss_rport_id,
sizeof (scsi_devid_desc_t) +
strlen(ist->ist_initiator_name) + 1);
stmf_remote_port_free(ss->ss_rport);
stmf_free(ss);
SET_LOGIN_ERROR(ict, ISCSI_STATUS_CLASS_TARGET_ERR,
ISCSI_LOGIN_STATUS_TARGET_ERROR);
return (IDM_STATUS_FAIL);
}
ss->ss_port_private = ict->ict_sess;
ict->ict_sess->ist_stmf_sess = ss;
mutex_exit(&ist->ist_tgt->target_mutex);
(void) snprintf(prop_buf, sizeof (prop_buf), "peername_%"PRIxPTR"",
(uintptr_t)ict->ict_sess);
(void) idm_sa_ntop(&ict->ict_ic->ic_raddr, peer_buf,
sizeof (peer_buf));
(void) stmf_add_rport_info(ss, prop_buf, peer_buf);
return (IDM_STATUS_SUCCESS);
}
static idm_status_t
login_sm_req_pdu_check(iscsit_conn_t *ict, idm_pdu_t *pdu)
{
uint8_t csg_req;
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsi_login_hdr_t *lh = (iscsi_login_hdr_t *)pdu->isp_hdr;
iscsi_login_rsp_hdr_t *lh_resp = lsm->icl_login_resp_tmpl;
csg_req = ISCSI_LOGIN_CURRENT_STAGE(lh->flags);
switch (csg_req) {
case ISCSI_SECURITY_NEGOTIATION_STAGE:
case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
if ((csg_req != lsm->icl_login_csg) &&
(lsm->icl_login_state != ILS_LOGIN_INIT)) {
goto pdu_check_fail;
}
break;
case ISCSI_FULL_FEATURE_PHASE:
default:
goto pdu_check_fail;
}
if (ict->ict_sess != NULL) {
if ((ict->ict_cid != ntohs(lh->cid)) ||
(bcmp(ict->ict_sess->ist_isid, lh->isid,
ISCSI_ISID_LEN) != 0)) {
goto pdu_check_fail;
}
}
#if (ISCSIT_MAX_VERSION > 0)
if ((lh->min_version > ISCSIT_MAX_VERSION) ||
(lh->max_version < ISCSIT_MIN_VERSION)) {
goto pdu_check_fail;
}
#endif
if ((lh_resp->opcode == ISCSI_OP_LOGIN_RSP) &&
((lh->min_version > lh_resp->active_version) ||
(lh->max_version < lh_resp->active_version))) {
goto pdu_check_fail;
}
return (IDM_STATUS_SUCCESS);
pdu_check_fail:
return (IDM_STATUS_FAIL);
}
static idm_status_t
login_sm_process_nvlist(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
char *nvp_name;
nvpair_t *nvp;
nvpair_t *next_nvp;
nvpair_t *negotiated_nvp;
kv_status_t kvrc;
uint8_t error_class;
uint8_t error_detail;
idm_status_t idm_status;
error_class = ISCSI_STATUS_CLASS_SUCCESS;
error_detail = ISCSI_LOGIN_STATUS_ACCEPT;
kvrc = idm_negotiate_key_values(ict->ict_ic, lsm->icl_request_nvlist,
lsm->icl_response_nvlist, lsm->icl_negotiated_values);
idm_kvstat_to_error(kvrc, &error_class, &error_detail);
if (error_class != ISCSI_STATUS_CLASS_SUCCESS) {
SET_LOGIN_ERROR(ict, error_class, error_detail);
idm_status = IDM_STATUS_FAIL;
return (idm_status);
}
if (kvrc == KV_HANDLED_NO_TRANSIT) {
lsm->icl_login_transit = B_FALSE;
}
if (!ict->ict_op.op_declarative_params_set &&
lsm->icl_login_csg == ISCSI_OP_PARMS_NEGOTIATION_STAGE) {
if (iscsit_add_declarative_keys(ict) != IDM_STATUS_SUCCESS) {
idm_status = IDM_STATUS_FAIL;
return (idm_status);
}
ict->ict_op.op_declarative_params_set = B_TRUE;
}
nvp = nvlist_next_nvpair(lsm->icl_request_nvlist, NULL);
while (nvp != NULL) {
next_nvp = nvlist_next_nvpair(lsm->icl_request_nvlist, nvp);
nvp_name = nvpair_name(nvp);
if (nvlist_lookup_nvpair(lsm->icl_negotiated_values,
nvp_name, &negotiated_nvp) == 0) {
kvrc = KV_HANDLED;
} else {
kvrc = iscsit_handle_key(ict, nvp, nvp_name);
}
idm_kvstat_to_error(kvrc, &error_class, &error_detail);
if (error_class != ISCSI_STATUS_CLASS_SUCCESS) {
break;
}
nvp = next_nvp;
}
if (error_class == ISCSI_STATUS_CLASS_SUCCESS) {
idm_status = IDM_STATUS_SUCCESS;
} else {
SET_LOGIN_ERROR(ict, error_class, error_detail);
idm_status = IDM_STATUS_FAIL;
}
return (idm_status);
}
static idm_status_t
login_sm_check_security(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
conn_auth_t *auth = &lsm->icl_auth;
iscsit_auth_method_t *am_list = &auth->ca_method_valid_list[0];
kv_status_t kvrc;
uint8_t error_class;
uint8_t error_detail;
idm_status_t idm_status;
error_class = ISCSI_STATUS_CLASS_SUCCESS;
error_detail = ISCSI_LOGIN_STATUS_ACCEPT;
if (lsm->icl_login_csg == ISCSI_SECURITY_NEGOTIATION_STAGE) {
kvrc = iscsit_reply_security_key(ict);
idm_kvstat_to_error(kvrc, &error_class, &error_detail);
} else if (!ict->ict_login_sm.icl_auth_pass) {
if (am_list[0] == AM_NONE || am_list[0] == 0) {
ict->ict_login_sm.icl_auth_pass = 1;
} else {
error_class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
error_detail = ISCSI_LOGIN_STATUS_AUTH_FAILED;
}
}
if (error_class == ISCSI_STATUS_CLASS_SUCCESS) {
idm_status = IDM_STATUS_SUCCESS;
} else {
SET_LOGIN_ERROR(ict, error_class, error_detail);
idm_status = IDM_STATUS_FAIL;
}
return (idm_status);
}
static idm_pdu_t *
login_sm_build_login_response(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsi_login_rsp_hdr_t *lh;
int transit, text_transit = 1;
idm_pdu_t *login_resp;
if (lsm->icl_login_resp_itb) {
login_resp = idm_pdu_alloc(sizeof (iscsi_hdr_t),
ISCSI_DEFAULT_MAX_RECV_SEG_LEN);
lsm->icl_login_resp_buf = idm_pdu_init_text_data(
login_resp, lsm->icl_login_resp_itb,
ISCSI_DEFAULT_MAX_RECV_SEG_LEN,
lsm->icl_login_resp_buf, &text_transit);
if (text_transit) {
idm_itextbuf_free(lsm->icl_login_resp_itb);
lsm->icl_login_resp_itb = NULL;
lsm->icl_login_resp_buf = NULL;
}
} else {
login_resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), 0);
}
idm_pdu_init(login_resp,
ict->ict_ic, ict, login_resp_complete_cb);
login_resp->isp_flags |= IDM_PDU_LOGIN_TX;
bcopy(lsm->icl_login_resp_tmpl,
login_resp->isp_hdr, sizeof (iscsi_login_rsp_hdr_t));
lh = (iscsi_login_rsp_hdr_t *)login_resp->isp_hdr;
lh->status_class = lsm->icl_login_resp_err_class;
lh->status_detail = lsm->icl_login_resp_err_detail;
lh->flags = 0;
lh->flags |= lsm->icl_login_csg << 2;
if (lh->status_class == ISCSI_STATUS_CLASS_SUCCESS) {
if (lsm->icl_login_transit &&
lsm->icl_auth_pass != 0) {
transit = 1;
} else {
transit = 0;
}
if (transit == 1 && text_transit == 1) {
lh->flags |= lsm->icl_login_nsg;
lsm->icl_login_csg = lsm->icl_login_nsg;
lh->flags |= ISCSI_FLAG_LOGIN_TRANSIT;
} else {
lh->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT;
}
if (transit && (lh->flags & ISCSI_FLAG_LOGIN_TRANSIT) &&
lsm->icl_login_csg == ISCSI_FULL_FEATURE_PHASE) {
lh->tsid = htons(ict->ict_sess->ist_tsih);
}
} else {
login_resp->isp_data = 0;
login_resp->isp_datalen = 0;
}
return (login_resp);
}
static kv_status_t
iscsit_handle_key(iscsit_conn_t *ict, nvpair_t *nvp, char *nvp_name)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
kv_status_t kvrc;
const idm_kv_xlate_t *ikvx;
ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
if (ikvx->ik_key_id == KI_MAX_KEY) {
kvrc = iscsit_reply_string(ict, nvp_name,
ISCSI_TEXT_NOTUNDERSTOOD);
} else {
kvrc = iscsit_handle_common_key(ict, nvp, ikvx);
if (kvrc == KV_UNHANDLED) {
switch (lsm->icl_login_csg) {
case ISCSI_SECURITY_NEGOTIATION_STAGE:
kvrc = iscsit_handle_security_key(
ict, nvp, ikvx);
break;
case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
kvrc = iscsit_handle_operational_key(
ict, nvp, ikvx);
break;
case ISCSI_FULL_FEATURE_PHASE:
default:
ASSERT(0);
kvrc = KV_UNHANDLED;
}
}
}
return (kvrc);
}
static kv_status_t
iscsit_handle_common_key(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
kv_status_t kvrc;
char *string_val;
int nvrc;
switch (ikvx->ik_key_id) {
case KI_INITIATOR_NAME:
case KI_INITIATOR_ALIAS:
nvrc = nvlist_add_nvpair(lsm->icl_negotiated_values, nvp);
kvrc = idm_nvstat_to_kvstat(nvrc);
break;
case KI_TARGET_NAME:
nvrc = nvpair_value_string(nvp, &string_val);
ASSERT(nvrc == 0);
nvrc = nvlist_add_nvpair(lsm->icl_negotiated_values, nvp);
kvrc = idm_nvstat_to_kvstat(nvrc);
break;
case KI_TARGET_ALIAS:
case KI_TARGET_ADDRESS:
case KI_TARGET_PORTAL_GROUP_TAG:
kvrc = KV_TARGET_ONLY;
break;
case KI_SESSION_TYPE:
nvrc = nvlist_add_nvpair(lsm->icl_negotiated_values, nvp);
kvrc = idm_nvstat_to_kvstat(nvrc);
nvrc = nvpair_value_string(nvp, &string_val);
ASSERT(nvrc == 0);
ict->ict_op.op_discovery_session =
strcmp(string_val, "Discovery") == 0 ? B_TRUE : B_FALSE;
break;
default:
kvrc = KV_UNHANDLED;
break;
}
return (kvrc);
}
static kv_status_t
iscsit_handle_security_key(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
iscsikey_id_t kv_id;
kv_status_t kvrc;
iscsit_auth_handler_t handler;
if (ikvx != NULL) {
kv_id = ikvx->ik_key_id;
} else {
kv_id = 0;
}
handler = iscsit_auth_get_handler(client, kv_id);
if (handler) {
kvrc = handler(ict, nvp, ikvx);
} else {
kvrc = KV_UNHANDLED;
}
return (kvrc);
}
static kv_status_t
iscsit_reply_security_key(iscsit_conn_t *ict)
{
return (iscsit_handle_security_key(ict, NULL, NULL));
}
static kv_status_t
iscsit_handle_operational_key(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
kv_status_t kvrc = KV_UNHANDLED;
boolean_t bool_val;
uint64_t num_val;
int nvrc;
switch (ikvx->ik_key_id) {
case KI_HEADER_DIGEST:
case KI_DATA_DIGEST:
break;
case KI_INITIAL_R2T:
case KI_IMMEDIATE_DATA:
case KI_DATA_PDU_IN_ORDER:
case KI_DATA_SEQUENCE_IN_ORDER:
case KI_IFMARKER:
case KI_OFMARKER:
nvrc = nvpair_value_boolean_value(nvp, &bool_val);
ASSERT(nvrc == 0);
break;
case KI_MAX_CONNECTIONS:
case KI_MAX_RECV_DATA_SEGMENT_LENGTH:
case KI_MAX_BURST_LENGTH:
case KI_FIRST_BURST_LENGTH:
case KI_DEFAULT_TIME_2_WAIT:
case KI_DEFAULT_TIME_2_RETAIN:
case KI_MAX_OUTSTANDING_R2T:
case KI_ERROR_RECOVERY_LEVEL:
nvrc = nvpair_value_uint64(nvp, &num_val);
ASSERT(nvrc == 0);
break;
case KI_OFMARKERINT:
case KI_IFMARKERINT:
break;
default:
break;
}
switch (ikvx->ik_key_id) {
case KI_HEADER_DIGEST:
kvrc = iscsit_handle_digest(ict, nvp, ikvx);
break;
case KI_DATA_DIGEST:
kvrc = iscsit_handle_digest(ict, nvp, ikvx);
break;
case KI_INITIAL_R2T:
kvrc = iscsit_handle_boolean(ict, nvp, bool_val, ikvx,
B_TRUE);
break;
case KI_IMMEDIATE_DATA:
kvrc = iscsit_handle_boolean(ict, nvp, bool_val, ikvx,
bool_val);
break;
case KI_DATA_PDU_IN_ORDER:
kvrc = iscsit_handle_boolean(ict, nvp, bool_val, ikvx,
B_TRUE);
break;
case KI_DATA_SEQUENCE_IN_ORDER:
kvrc = iscsit_handle_boolean(ict, nvp, bool_val, ikvx,
bool_val);
break;
case KI_OFMARKER:
case KI_IFMARKER:
kvrc = iscsit_handle_boolean(ict, nvp, bool_val, ikvx,
B_FALSE);
break;
case KI_MAX_CONNECTIONS:
kvrc = iscsit_handle_numerical(ict, nvp, num_val, ikvx,
ISCSI_MIN_CONNECTIONS,
ISCSI_MAX_CONNECTIONS,
ISCSIT_MAX_CONNECTIONS);
break;
case KI_MAX_RECV_DATA_SEGMENT_LENGTH:
kvrc = iscsit_handle_numerical(ict, nvp, num_val, ikvx,
ISCSI_MIN_RECV_DATA_SEGMENT_LENGTH,
ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH,
ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
break;
case KI_MAX_BURST_LENGTH:
kvrc = iscsit_handle_numerical(ict, nvp, num_val, ikvx,
ISCSI_MIN_MAX_BURST_LENGTH,
ISCSI_MAX_BURST_LENGTH,
ISCSIT_MAX_BURST_LENGTH);
break;
case KI_FIRST_BURST_LENGTH:
kvrc = iscsit_handle_numerical(ict, nvp, num_val, ikvx,
ISCSI_MIN_FIRST_BURST_LENGTH,
ISCSI_MAX_FIRST_BURST_LENGTH,
ISCSIT_MAX_FIRST_BURST_LENGTH);
break;
case KI_DEFAULT_TIME_2_WAIT:
kvrc = iscsit_handle_numerical(ict, nvp, num_val, ikvx,
ISCSI_MIN_TIME2WAIT,
ISCSI_MAX_TIME2WAIT,
ISCSIT_MAX_TIME2WAIT);
break;
case KI_DEFAULT_TIME_2_RETAIN:
kvrc = iscsit_handle_numerical(ict, nvp, num_val, ikvx,
ISCSI_MIN_TIME2RETAIN,
ISCSI_MAX_TIME2RETAIN,
ISCSIT_MAX_TIME2RETAIN);
break;
case KI_MAX_OUTSTANDING_R2T:
kvrc = iscsit_handle_numerical(ict, nvp, num_val, ikvx,
ISCSI_MIN_MAX_OUTSTANDING_R2T,
ISCSI_MAX_OUTSTANDING_R2T,
ISCSIT_MAX_OUTSTANDING_R2T);
break;
case KI_ERROR_RECOVERY_LEVEL:
kvrc = iscsit_handle_numerical(ict, nvp, num_val, ikvx,
ISCSI_MIN_ERROR_RECOVERY_LEVEL,
ISCSI_MAX_ERROR_RECOVERY_LEVEL,
ISCSIT_MAX_ERROR_RECOVERY_LEVEL);
break;
case KI_OFMARKERINT:
case KI_IFMARKERINT:
kvrc = iscsit_reply_string(ict, ikvx->ik_key_name,
ISCSI_TEXT_IRRELEVANT);
break;
default:
kvrc = KV_UNHANDLED;
break;
}
return (kvrc);
}
static kv_status_t
iscsit_reply_numerical(iscsit_conn_t *ict,
const char *nvp_name, const uint64_t value)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
kv_status_t kvrc;
int nvrc;
nvrc = nvlist_add_uint64(lsm->icl_response_nvlist,
nvp_name, value);
kvrc = idm_nvstat_to_kvstat(nvrc);
return (kvrc);
}
static kv_status_t
iscsit_reply_string(iscsit_conn_t *ict,
const char *nvp_name, const char *text)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
kv_status_t kvrc;
int nvrc;
nvrc = nvlist_add_string(lsm->icl_response_nvlist,
nvp_name, text);
kvrc = idm_nvstat_to_kvstat(nvrc);
return (kvrc);
}
static kv_status_t
iscsit_handle_digest(iscsit_conn_t *ict, nvpair_t *choices,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
kv_status_t kvrc = KV_VALUE_ERROR;
int nvrc;
nvpair_t *digest_choice;
char *digest_choice_string;
digest_choice = idm_get_next_listvalue(choices, NULL);
while (digest_choice != NULL) {
nvrc = nvpair_value_string(digest_choice,
&digest_choice_string);
ASSERT(nvrc == 0);
if ((strcasecmp(digest_choice_string, "crc32c") == 0) ||
(strcasecmp(digest_choice_string, "none") == 0)) {
nvrc = nvlist_add_string(lsm->icl_negotiated_values,
ikvx->ik_key_name, digest_choice_string);
kvrc = idm_nvstat_to_kvstat(nvrc);
if (nvrc == 0) {
nvrc = nvlist_add_string(
lsm->icl_response_nvlist,
ikvx->ik_key_name, digest_choice_string);
kvrc = idm_nvstat_to_kvstat(nvrc);
}
break;
}
digest_choice = idm_get_next_listvalue(choices,
digest_choice);
}
if (digest_choice == NULL)
kvrc = KV_VALUE_ERROR;
return (kvrc);
}
static kv_status_t
iscsit_handle_boolean(iscsit_conn_t *ict, nvpair_t *nvp, boolean_t value,
const idm_kv_xlate_t *ikvx, boolean_t iscsit_value)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
kv_status_t kvrc;
int nvrc;
if (ikvx->ik_declarative) {
nvrc = nvlist_add_nvpair(lsm->icl_negotiated_values, nvp);
} else {
if (value != iscsit_value) {
value = iscsit_value;
nvrc = nvlist_add_boolean_value(
lsm->icl_negotiated_values,
ikvx->ik_key_name, value);
lsm->icl_login_transit = B_FALSE;
} else {
nvrc = nvlist_add_nvpair(lsm->icl_negotiated_values,
nvp);
}
if (nvrc == 0) {
nvrc = nvlist_add_boolean_value(
lsm->icl_response_nvlist, ikvx->ik_key_name, value);
}
}
kvrc = idm_nvstat_to_kvstat(nvrc);
return (kvrc);
}
static kv_status_t
iscsit_handle_numerical(iscsit_conn_t *ict, nvpair_t *nvp, uint64_t value,
const idm_kv_xlate_t *ikvx,
uint64_t iscsi_min_value, uint64_t iscsi_max_value,
uint64_t iscsit_max_value)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
kv_status_t kvrc;
int nvrc;
if ((value < iscsi_min_value) || (value > iscsi_max_value)) {
kvrc = KV_VALUE_ERROR;
} else if (ikvx->ik_declarative) {
nvrc = nvlist_add_nvpair(lsm->icl_negotiated_values, nvp);
kvrc = idm_nvstat_to_kvstat(nvrc);
} else {
if (value > iscsit_max_value) {
value = iscsit_max_value;
nvrc = nvlist_add_uint64(lsm->icl_negotiated_values,
ikvx->ik_key_name, value);
lsm->icl_login_transit = B_FALSE;
} else {
nvrc = nvlist_add_nvpair(lsm->icl_negotiated_values,
nvp);
}
if (nvrc == 0) {
nvrc = nvlist_add_uint64(lsm->icl_response_nvlist,
ikvx->ik_key_name, value);
}
kvrc = idm_nvstat_to_kvstat(nvrc);
}
return (kvrc);
}
static void
iscsit_process_negotiated_values(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
char *string_val;
boolean_t boolean_val;
uint64_t uint64_val;
int nvrc;
idm_notice_key_values(ict->ict_ic, lsm->icl_negotiated_values);
if ((nvrc = nvlist_lookup_string(lsm->icl_negotiated_values,
"InitiatorAlias", &string_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_sess->ist_initiator_alias =
kmem_alloc(strlen(string_val) + 1, KM_SLEEP);
(void) strcpy(ict->ict_sess->ist_initiator_alias, string_val);
if (ict->ict_sess->ist_stmf_sess)
ict->ict_sess->ist_stmf_sess->ss_rport_alias =
strdup(string_val);
}
if ((nvrc = nvlist_lookup_string(lsm->icl_negotiated_values,
"TargetAlias", &string_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_sess->ist_target_alias =
kmem_alloc(strlen(string_val) + 1, KM_SLEEP);
(void) strcpy(ict->ict_sess->ist_target_alias, string_val);
}
if ((nvrc = nvlist_lookup_boolean_value(lsm->icl_negotiated_values,
"InitialR2T", &boolean_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_initial_r2t = boolean_val;
}
if ((nvrc = nvlist_lookup_boolean_value(lsm->icl_negotiated_values,
"ImmediateData", &boolean_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_immed_data = boolean_val;
}
if ((nvrc = nvlist_lookup_boolean_value(lsm->icl_negotiated_values,
"DataPDUInOrder", &boolean_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_data_pdu_in_order = boolean_val;
}
if ((nvrc = nvlist_lookup_boolean_value(lsm->icl_negotiated_values,
"DataSequenceInOrder", &boolean_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_data_sequence_in_order = boolean_val;
}
if ((nvrc = nvlist_lookup_uint64(lsm->icl_negotiated_values,
"MaxConnections", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_max_connections = uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(lsm->icl_negotiated_values,
"MaxRecvDataSegmentLength", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_max_recv_data_segment_length = uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(lsm->icl_negotiated_values,
"MaxBurstLength", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_max_burst_length = uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(lsm->icl_negotiated_values,
"FirstBurstLength", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_first_burst_length = uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(lsm->icl_negotiated_values,
"DefaultTime2Wait", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_default_time_2_wait = uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(lsm->icl_negotiated_values,
"DefaultTime2Retain", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_default_time_2_retain = uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(lsm->icl_negotiated_values,
"MaxOutstandingR2T", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_max_outstanding_r2t = uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(lsm->icl_negotiated_values,
"ErrorRecoveryLevel", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
ict->ict_op.op_error_recovery_level = uint64_val;
}
}
static idm_status_t
iscsit_add_declarative_keys(iscsit_conn_t *ict)
{
nvlist_t *cfg_nv = NULL;
kv_status_t kvrc;
int nvrc;
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
uint8_t error_class;
uint8_t error_detail;
idm_status_t idm_status;
if ((nvrc = nvlist_alloc(&cfg_nv, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
kvrc = idm_nvstat_to_kvstat(nvrc);
goto alloc_fail;
}
if ((nvrc = nvlist_add_uint64(cfg_nv, "MaxRecvDataSegmentLength",
max_dataseglen_target)) != 0) {
kvrc = idm_nvstat_to_kvstat(nvrc);
goto done;
}
kvrc = idm_declare_key_values(ict->ict_ic, cfg_nv,
lsm->icl_response_nvlist);
done:
nvlist_free(cfg_nv);
alloc_fail:
idm_kvstat_to_error(kvrc, &error_class, &error_detail);
if (error_class == ISCSI_STATUS_CLASS_SUCCESS) {
idm_status = IDM_STATUS_SUCCESS;
} else {
SET_LOGIN_ERROR(ict, error_class, error_detail);
idm_status = IDM_STATUS_FAIL;
}
return (idm_status);
}
static char *
iscsit_fold_name(char *name, size_t *buflen)
{
char *ret;
const char *sns;
int errnum;
int flag = U8_TEXTPREP_NFKC;
size_t inlen, outlen, coff;
if (name == NULL)
return (NULL);
if (strncasecmp(name, SNS_EUI ".", strlen(SNS_EUI) + 1) == 0) {
sns = SNS_EUI;
*buflen = SNS_EUI_LEN_MAX + 1;
flag |= U8_TEXTPREP_TOUPPER;
} else if (strncasecmp(name, SNS_IQN ".", strlen(SNS_IQN) + 1) == 0) {
sns = SNS_IQN;
*buflen = SNS_IQN_LEN_MAX + 1;
flag |= U8_TEXTPREP_TOLOWER;
} else if (strncasecmp(name, SNS_NAA ".", strlen(SNS_NAA) + 1) == 0) {
sns = SNS_NAA;
*buflen = SNS_NAA_LEN_MAX + 1;
flag |= U8_TEXTPREP_TOUPPER;
} else {
return (NULL);
}
ret = kmem_zalloc(*buflen, KM_SLEEP);
coff = strlen(sns);
inlen = strlen(name) - coff;
outlen = *buflen - coff - 1;
if (u8_textprep_str(name + coff, &inlen, ret + coff, &outlen, flag,
U8_UNICODE_320, &errnum) == (size_t)-1) {
kmem_free(ret, *buflen);
return (NULL);
}
bcopy(sns, ret, coff);
return (ret);
}