/* * Process isns response, need to verify same transaction id, func_id * as the isns command, the isns command is in network byte order, * the isns response is in host byte order */ static int process_rsp(isns_pdu_t *cmd, isns_rsp_t *rsp) { queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_RSP"); /* * Process responses: * -verify sucessful response * -verify match xid * -process operating attributes * For DevAttrReg & DevAttrQry and most isns command, * the response func_id is command_func_id | 0x8000. */ rsp->status = ntohl(rsp->status); if (rsp->status != ISNS_RSP_SUCCESSFUL || rsp->xid != ntohs(cmd->xid) || rsp->func_id != (ntohs(cmd->func_id) | 0x8000)) { queue_prt(mgmtq, Q_ISNS_DBG, "cmd failed with: status= %d xid= %d %d "\ "response attribute %x\n", rsp->status, rsp->xid,\ ntohs(cmd->xid), rsp->func_id); return (-1); } return (0); }
/* * []---- * | session_validate -- do what the name says * | * | At this point the connection has processed the login command so that * | we have InitiatorName and ISID at a minimum. Check to see if there * | are other sessions which match. If so, log that one out and proceed with * | this session. If nothing matches, then link this into a global list. * | * | Once we support multiple connections per session need to scan list * | to see if other connection have the same CID. If so, log out that * | connection. * []---- */ Boolean_t session_validate(iscsi_sess_t *s) { iscsi_sess_t *check; queue_prt(s->s_mgmtq, Q_SESS_NONIO, "SES%x %s ISID[%02x%02x%02x%02x%02x%02x]", s->s_num, s->s_i_alias == NULL ? s->s_i_name : s->s_i_alias, s->s_isid[0], s->s_isid[1], s->s_isid[2], s->s_isid[3], s->s_isid[4], s->s_isid[5]); /* * SessionType=Discovery which means no target name and therefore * this is okay. */ if (s->s_t_name == NULL) return (True); (void) pthread_mutex_lock(&sess_mutex); for (check = sess_head; check; check = check->s_next) { /* * Ignore ourselves in this check. */ if (check == s) continue; if ((check->s_t_name == NULL) || (strcmp(check->s_t_name, s->s_t_name) != 0)) continue; if (strcmp(check->s_i_name, s->s_i_name) != 0) continue; if (check->s_conn_head->c_tpgt != s->s_conn_head->c_tpgt) continue; /* * Section 5.3.5 * Session reinstatement is the process of the initiator * logging in with an ISID that is possible active from * the target's perspective. Thus implicitly logging out * the session that corresponds to the ISID and * reinstating a new iSCSI session in its place (with the * same ISID). */ if (bcmp(check->s_isid, s->s_isid, 6) == 0) { queue_prt(s->s_mgmtq, Q_SESS_NONIO, "SES%x Implicit shutdown", check->s_num); if (check->s_conn_head->c_state == S5_LOGGED_IN) conn_state(check->s_conn_head, T8); else conn_state(check->s_conn_head, T7); break; } } (void) pthread_mutex_unlock(&sess_mutex); return (True); }
/* * []---- * | sbc_cmd_reserve -- Run commands when another I_T_L has a reservation * []---- */ static void sbc_cmd_reserved(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { scsi_cmd_table_t *e; switch (cdb[0]) { case SCMD_TEST_UNIT_READY: case SCMD_INQUIRY: case SCMD_REPORT_LUNS: case SCMD_LOG_SENSE_G1: case SCMD_READ_MEDIA_SERIAL: case SCMD_REPORT_TARGET_PORT_GROUPS: case SCMD_REQUEST_SENSE: /* * SPC-2, revision 20, Section 5.5.1 table 10 * The specification allows these three commands * to run even through there's a reservation in place. */ e = &cmd->c_lu->l_cmd_table[cdb[0]]; #ifdef FULL_DEBUG queue_prt(mgmtq, Q_STE_IO, "RESERVED: SBC%x LUN%d Cmd %s\n", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, e->cmd_name == NULL ? "(no name)" : e->cmd_name); #endif (*e->cmd_start)(cmd, cdb, cdb_len); break; default: trans_send_complete(cmd, STATUS_RESERVATION_CONFLICT); } }
/* * []---- * | add_targets -- add TargetName and TargetAddress to text argument * | * | Add targets which this initiator is allowed to see based on * | the access_list associated with a target. If a target doesn't * | have an access list then let everyone see it. * []---- */ static Boolean_t add_targets(iscsi_conn_t *c, char **text, int *text_length) { tgt_node_t *targ = NULL; Boolean_t rval = True; char *targ_name = NULL; while ((rval == True) && ((targ = tgt_node_next_child(targets_config, XML_ELEMENT_TARG, targ)) != NULL)) { if (check_access(targ, c->c_sess->s_i_name, False) == True) { if (tgt_find_value_str(targ, XML_ELEMENT_INAME, &targ_name) == False) { rval = False; break; } queue_prt(c->c_mgmtq, Q_CONN_LOGIN, "CON%x %24s = %s\n", c->c_num, "TargetName", targ_name); (void) add_text(text, text_length, "TargetName", targ_name); free(targ_name); add_target_address(c, text, text_length, targ); } } return (rval); }
static int do_uscsi(t10_cmd_t *cmd, raw_io_t *io, raw_direction_t dir) { struct uscsi_cmd u; uchar_t sense_buf[128]; bzero(&u, sizeof (u)); u.uscsi_cdb = (caddr_t)io->r_cdb; u.uscsi_cdblen = io->r_cdb_len; u.uscsi_bufaddr = io->r_data; u.uscsi_buflen = io->r_data_len; u.uscsi_flags = ((dir == RawDataToDevice) ? USCSI_WRITE : (dir == RawDataFromDevice) ? USCSI_READ : 0) | USCSI_RQENABLE; u.uscsi_rqbuf = (char *)sense_buf; u.uscsi_rqlen = sizeof (sense_buf); if ((ioctl(cmd->c_lu->l_common->l_fd, USCSICMD, &u) == 0) && (u.uscsi_status == 0)) { io->r_status = 0; return (0); } queue_prt(mgmtq, Q_STE_ERRS, "RAW%d LUN%d USCSICMD errno %d, cmd_status %d, rqstatus %d, " "rqresid %d", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, errno, u.uscsi_status, u.uscsi_rqstatus, u.uscsi_rqresid); if ((u.uscsi_rqlen - u.uscsi_rqresid) < sizeof (struct scsi_extended_sense)) { queue_prt(mgmtq, Q_STE_ERRS, "RAW%x LUN%d -- No sense data, got=%d, needed=%d", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, u.uscsi_rqlen - u.uscsi_rqresid, sizeof (struct scsi_extended_sense)); spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); io->r_status = STATUS_CHECK; return (STATUS_CHECK); } else { spc_sense_raw(cmd, sense_buf, u.uscsi_rqlen - u.uscsi_rqresid); io->r_status = u.uscsi_status; return (u.uscsi_status); } }
/* * SCNDereg */ static int isns_scn_dereg(int so, char *node) { isns_pdu_t *cmd = NULL; isns_rsp_t *rsp = NULL; uint32_t flags = 0; int ret = -1; queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_SCN_DEREG"); if (isns_create_pdu(ISNS_SCN_DEREG, flags, &cmd) != 0) { return (-1); } /* source attribute */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(node), node, 0) == -1) { goto error; } /* message key attribute */ /* iscsi initiator node name */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(node), node, 0) != 0) { goto error; } if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) == -1) { goto error; } if (isns_send(so, cmd) == -1) { syslog(LOG_ERR, "isns_scn_reg fails isns_send"); goto error; } if (isns_recv(so, &rsp) == -1) { syslog(LOG_ERR, "isns_scn_reg fails isns_recv "); goto error; } /* process response */ if (process_rsp(cmd, rsp) == 0) { ret = 0; } error: if (cmd) isns_free_pdu(cmd); if (rsp) isns_free_pdu(rsp); return (ret); }
/* * []---- * | sbc_cmd -- start a SCSI command * | * | This routine is called from within the SAM-3 Task router. * []---- */ static void sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { scsi_cmd_table_t *e; e = &cmd->c_lu->l_cmd_table[cdb[0]]; #ifdef FULL_DEBUG queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Cmd %s\n", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, e->cmd_name == NULL ? "(no name)" : e->cmd_name); #endif (*e->cmd_start)(cmd, cdb, cdb_len); }
/* * []------------------------------------------------------------------[] * | SCSI Object-Based Storage Device Commands | * | T10/1355-D | * | The following functions implement the emulation of OSD type | * | commands. | * []------------------------------------------------------------------[] */ static void osd_service_action(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { osd_generic_cdb_t *o; uint16_t service_action; /* * debug only -- no need to drop core if someone doesn't play right. */ assert(cdb_len == sizeof (*o)); if (cdb_len != sizeof (*o)) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, SPC_ASCQ_INVALID_CDB); trans_send_complete(cmd, STATUS_CHECK); return; } o = (osd_generic_cdb_t *)cdb; service_action = o->ocdb_basic.b_service_action[0] << 8 | o->ocdb_basic.b_service_action[1]; queue_prt(mgmtq, Q_STE_NONIO, "OSD%x LUN%d service=0x%x, options=0x%x, specific_opts=0x%x," " fmt=0x%x", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, service_action, o->ocdb_options, o->ocdb_specific_opts, o->ocdb_fmt); switch (service_action) { case OSD_APPEND: case OSD_CREATE: case OSD_CREATE_AND_WRITE: case OSD_CREATE_COLLECTION: case OSD_CREATE_PARTITION: case OSD_FLUSH: case OSD_FLUSH_COLLECTION: case OSD_FLUSH_OSD: case OSD_FLUSH_PARTITION: case OSD_FORMAT_OSD: case OSD_GET_ATTR: case OSD_LIST: osd_list(cmd, cdb, cdb_len); break; case OSD_LIST_COLLECTION: case OSD_PERFORM_SCSI: case OSD_TASK_MGMT: default: spc_unsupported(cmd, cdb, cdb_len); break; } }
/* * []---- * | sbc_data -- Data phase for command. * | * | Normally this is only called for the WRITE command. Other commands * | that have a data in phase will probably be short circuited when * | we call trans_rqst_dataout() and the data is already available. * | At least this is true for iSCSI. FC however will need a DataIn phase * | for commands like MODE SELECT and PGROUT. * []---- */ static void sbc_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, size_t data_len) { scsi_cmd_table_t *e; e = &cmd->c_lu->l_cmd_table[cmd->c_cdb[0]]; #ifdef FULL_DEBUG queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Data %s\n", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, e->cmd_name); #endif (*e->cmd_data)(cmd, id, offset, data, data_len); }
/* * []---- * | osd_list -- return a list of objects * []---- */ static void osd_list(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { osd_cmd_list_t *o = (osd_cmd_list_t *)cdb; osd_obj_id_t part; osd_list_param_t *data; uint64_t len, alloc_len; part = (uint64_t)o->ocdb_partition_id[0] << 56 | (uint64_t)o->ocdb_partition_id[1] << 48 | (uint64_t)o->ocdb_partition_id[2] << 40 | (uint64_t)o->ocdb_partition_id[3] << 32 | (uint64_t)o->ocdb_partition_id[4] << 24 | (uint64_t)o->ocdb_partition_id[5] << 16 | (uint64_t)o->ocdb_partition_id[6] << 8 | (uint64_t)o->ocdb_partition_id[7]; len = (uint64_t)o->ocdb_length[0] << 56 | (uint64_t)o->ocdb_length[1] << 48 | (uint64_t)o->ocdb_length[2] << 40 | (uint64_t)o->ocdb_length[3] << 32 | (uint64_t)o->ocdb_length[4] << 24 | (uint64_t)o->ocdb_length[5] << 16 | (uint64_t)o->ocdb_length[6] << 8 | (uint64_t)o->ocdb_length[7]; if (len == 0) { trans_send_complete(cmd, STATUS_GOOD); return; } queue_prt(mgmtq, Q_STE_NONIO, "part=0x%llx, len=0x%llx", part, len); alloc_len = MAX(sizeof (*data), len); if ((data = calloc(1, alloc_len)) == NULL) { trans_send_complete(cmd, STATUS_BUSY); return; } data->op_length[7] = sizeof (*data) - 8; if (part == OSD_PARTITION_ROOT) data->op_root = 1; (void) trans_send_datain(cmd, (char *)data, sizeof (*data), 0, free, True, (emul_handle_t)data); }
/* * []---- * | session_free -- remove connection from session * []---- */ static void session_free(iscsi_sess_t *s) { iscsi_sess_t *n; /* * Early errors in connection setup can still call this routine * which means the session hasn't been called. */ if (s == NULL) return; if (s->s_i_name) free(s->s_i_name); if (s->s_t_name) free(s->s_t_name); if (s->s_i_alias) free(s->s_i_alias); (void) pthread_mutex_lock(&sess_mutex); if (sess_head == s) sess_head = s->s_next; else { for (n = sess_head; n; n = n->s_next) { if (n->s_next == s) { n->s_next = s->s_next; break; } } if (n == NULL) { queue_prt(s->s_mgmtq, Q_SESS_ERRS, "SES%x NOT IN SESSION LIST!", s->s_num); } } (void) pthread_mutex_unlock(&sess_mutex); }
/* * Register a new node, need to find another node that is already registered * DevAttrReg * RFC 4171 Section 5.6.5.5 indicated SCN-port-tag (23) needed to be * included in the registration * Also need to register ESI-port-tag (20) see Section 6.3.5 */ static int isns_dev_attr_reg(int so, tgt_node_t *tgt, char *node, char *alias) { isns_pdu_t *cmd = NULL; isns_rsp_t *rsp = NULL; uint32_t flags = 0; int ret = 0; Boolean_t found = False; tgt_node_t *src = NULL; char *src_nm = NULL; queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_DEV_ATTR_REG"); if ((so = isns_open(isns_args.server)) == -1) { return (-1); } if (num_reg == 0) { flags |= ISNS_FLAG_REPLACE_REG; } if (isns_create_pdu(ISNS_DEV_ATTR_REG, flags, &cmd) != 0) { return (-1); } if (num_reg == 0) { /* add new node to source attribute */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(node), node, 0) != 0) { goto error; } } else { /* find a registered node to use */ do { src = find_next_tgt(src, &src_nm); if (src == NULL) { syslog(LOG_ALERT, "ISNS out of sync\n"); goto error; } if (tgt == src) { free(src_nm); src_nm = NULL; continue; } else { found = True; } } while (found == False); if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(src_nm), src_nm, 0) != 0) { goto error; } } /* add message key attribute */ if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { goto error; } /* add delimiter */ if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) != 0) { goto error; } /* add operation attributes */ /* entity id */ if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { goto error; } /* entity type */ if (isns_append_attr(cmd, ISNS_ENTITY_PROTOCOL_ATTR_ID, ISNS_ENTITY_TYP_SZ, NULL, ISNS_ENTITY_PROTOCOL_ISCSI) != 0) { goto error; } /* * Register entity portal properties the 1st time */ if (num_reg == 0) { /* portal ip-addr */ if (isns_append_attr(cmd, ISNS_PORTAL_IP_ADDR_ATTR_ID, eid_ip.ai_addrlen, (void *)&eid_ip.ip_adr, eid_ip.ip_len) != 0) { goto error; } /* portal port */ if (isns_append_attr(cmd, ISNS_PORTAL_PORT_ATTR_ID, ISNS_PORT_SZ, NULL, iscsi_port) != 0) { goto error; } /* ESI interval */ if (isns_append_attr(cmd, ISNS_ESI_INTERVAL_ATTR_ID, ISNS_ESI_TICK_SZ, NULL, 10) != 0) { goto error; } /* scn port */ if (isns_append_attr(cmd, ISNS_SCN_PORT_ATTR_ID, ISNS_PORT_SZ, NULL, scn_port) != 0) { goto error; } /* esi port */ if (isns_append_attr(cmd, ISNS_ESI_PORT_ATTR_ID, ISNS_PORT_SZ, NULL, scn_port) != 0) { goto error; } } /* iscsi node name */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(node), node, 0) != 0) { goto error; } /* iscsi node type */ if (isns_append_attr(cmd, ISNS_ISCSI_NODE_TYPE_ATTR_ID, ISNS_NODE_TYP_SZ, NULL, ISNS_TARGET_NODE_TYPE) != 0) { goto error; } /* iscsi node alias */ if (isns_append_attr(cmd, ISNS_ISCSI_ALIAS_ATTR_ID, STRLEN(alias), alias, 0) != 0) { goto error; } /* PGT */ if (append_tpgt(tgt, cmd) != 0) { goto error; } /* send pdu */ if (isns_send(so, cmd) == -1) { goto error; } /* get isns response */ if (isns_recv(so, &rsp) == -1) { goto error; } /* process response */ if ((ret = process_rsp(cmd, rsp)) == 0) { num_reg++; } error: /* Free all resouces here */ if (cmd) isns_free_pdu(cmd); if (rsp) isns_free_pdu(rsp); if (src_nm) free(src_nm); return (ret); }
/* * SCNReg * See RFC 4171 Section 5.6.5.5 */ static int isns_scn_reg(int so, char *node) { isns_pdu_t *cmd; isns_rsp_t *rsp; uint32_t flags = 0; uint32_t bitmap = 0; int ret = -1; queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_SCN_REG"); if (isns_create_pdu(ISNS_SCN_REG, flags, &cmd) != 0) { return (-1); } /* source attribute */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(node), node, 0) == -1) { goto error; } /* message key attribute */ /* iscsi initiator node name */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(node), node, 0) != 0) { goto error; } /* delimiter */ if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) == -1) { goto error; } /* SCN bitmap */ bitmap = ISNS_INIT_SELF_INFO_ONLY | ISNS_OBJ_REMOVED | ISNS_OBJ_ADDED | ISNS_OBJ_UPDATED; if (isns_append_attr(cmd, ISNS_ISCSI_SCN_BITMAP_ATTR_ID, ISNS_SCN_BITMAP_SZ, NULL, bitmap) == -1) { goto error; } if (isns_send(so, cmd) == -1) { syslog(LOG_ERR, "isns_scn_reg fails isns_send"); goto error; } if (isns_recv(so, &rsp) == -1) { syslog(LOG_ERR, "isns_scn_reg fails isns_recv "); goto error; } /* process response */ if (process_rsp(cmd, rsp) == 0) { ret = 0; } error: if (cmd) isns_free_pdu(cmd); if (rsp) isns_free_pdu(rsp); return (ret); }
/* * []---- * | sess_process -- handle messages from the connection(s) * []---- */ static void * sess_process(void *v) { iscsi_sess_t *s = (iscsi_sess_t *)v; iscsi_conn_t *c; iscsi_cmd_t *cmd; msg_t *m; Boolean_t process = True; mgmt_request_t *mgmt; name_request_t *nr; t10_cmd_t *t10_cmd; char **buf, local_buf[16]; int lun; extern void dataout_callback(t10_cmd_t *t, char *data, size_t *xfer); (void) pthread_mutex_lock(&s->s_mutex); s->s_state = SS_RUNNING; (void) pthread_mutex_unlock(&s->s_mutex); do { m = queue_message_get(s->s_sessq); switch (m->msg_type) { case msg_cmd_send: cmd = (iscsi_cmd_t *)m->msg_data; if (s->s_t10 == NULL) { /* * The value of 0x960 comes from T10. * See SPC-4, revision 1a, section 6.4.2, * table 87 * * XXX Need to rethink how I should do * the callback. */ s->s_t10 = t10_handle_create(s->s_t_name, T10_TRANS_ISCSI, s->s_conn_head->c_tpgt, s->s_conn_head->c_max_burst_len, s->s_t10q, dataout_callback); } if (t10_cmd_create(s->s_t10, cmd->c_lun, cmd->c_scb, cmd->c_scb_len, (transport_t)cmd, &t10_cmd) == False) { queue_prt(s->s_mgmtq, Q_SESS_ERRS, "SES%x FAILED to create cmd", s->s_num); /* * If the command create failed, the T10 layer * will attempt to create a sense buffer * telling the initiator what went wrong. If * that layer was unable to accomplish that * things are really bad and we need to just * close the connection. */ if (cmd->c_t10_cmd != NULL) { queue_message_set( cmd->c_allegiance->c_dataq, 0, msg_cmd_cmplt, t10_cmd); } else conn_state(cmd->c_allegiance, T11); } else { (void) pthread_mutex_lock( &cmd->c_allegiance->c_mutex); if (cmd->c_state != CmdCanceled) { cmd->c_t10_cmd = t10_cmd; (void) t10_cmd_send(s->s_t10, cmd->c_t10_cmd, cmd->c_data, cmd->c_data_len); } else { t10_cmd_state(t10_cmd, T10_Cmd_Event_Canceled); } (void) pthread_mutex_unlock( &cmd->c_allegiance->c_mutex); } break; case msg_cmd_data_out: cmd = (iscsi_cmd_t *)m->msg_data; if (s->s_t10 != NULL) (void) t10_cmd_data(s->s_t10, cmd->c_t10_cmd, cmd->c_offset_out, cmd->c_data, cmd->c_data_len); break; case msg_targ_inventory_change: if (s->s_t10 != NULL) (void) t10_task_mgmt(s->s_t10, InventoryChange, 0, 0); break; case msg_lu_capacity_change: lun = (int)(uintptr_t)m->msg_data; if (s->s_t10 != NULL) (void) t10_task_mgmt(s->s_t10, CapacityChange, lun, 0); break; case msg_reset_targ: if (s->s_t10 != NULL) (void) t10_task_mgmt(s->s_t10, ResetTarget, 0, 0); break; case msg_reset_lu: if (s->s_t10 != NULL) (void) t10_task_mgmt(s->s_t10, ResetLun, (int)(uintptr_t)m->msg_data, 0); break; case msg_shutdown: (void) pthread_mutex_lock(&s->s_mutex); s->s_state = SS_SHUTDOWN_START; (void) pthread_mutex_unlock(&s->s_mutex); /* * Shutdown rquest comming from a connection. Only * shutdown the STE if this is the last connection * for this session. */ c = (iscsi_conn_t *)m->msg_data; if (session_remove_connection(s, c) == True) { queue_prt(s->s_mgmtq, Q_SESS_NONIO, "SES%x Starting shutdown", s->s_num); /* * If this is the last connection for this * session send a message to the SAM-3 layer to * shutdown. */ if (s->s_t10 != NULL) { t10_handle_disable(s->s_t10); } queue_message_set(s->s_t10q, 0, msg_shutdown_rsp, 0); process = False; } else { /* * Since this isn't the last connection for * the session, acknowledge the connection * request now since it's references from * this session have been removed. */ queue_message_set(c->c_dataq, 0, msg_shutdown_rsp, (void *)False); } break; case msg_initiator_name: nr = (name_request_t *)m->msg_data; s->s_i_name = strdup(nr->nr_name); /* * Acknowledge the request by sending back an empty * message. */ queue_message_set(nr->nr_q, 0, msg_initiator_name, 0); break; case msg_initiator_alias: nr = (name_request_t *)m->msg_data; s->s_i_alias = strdup(nr->nr_name); /* * Acknowledge the request by sending back an empty * message. */ queue_message_set(nr->nr_q, 0, msg_initiator_alias, 0); break; case msg_target_name: nr = (name_request_t *)m->msg_data; s->s_t_name = strdup(nr->nr_name); /* * Acknowledge the request by sending back an empty * message. */ queue_message_set(nr->nr_q, 0, msg_target_name, 0); break; case msg_mgmt_rqst: mgmt = (mgmt_request_t *)m->msg_data; m->msg_data = NULL; (void) pthread_mutex_lock(&mgmt->m_resp_mutex); buf = mgmt->m_u.m_resp; if ((s->s_type == SessionNormal) && (mgmt->m_request == mgmt_full_phase_statistics) && (strcmp(s->s_t_name, mgmt->m_targ_name) == 0)) { buf_add_tag(buf, XML_ELEMENT_CONN, Tag_Start); buf_add_tag(buf, s->s_i_name, Tag_String); if (s->s_i_alias != NULL) { xml_add_tag(buf, XML_ELEMENT_ALIAS, s->s_i_alias); } /* * Need to loop through the connections * and create one time_connected tag for * each. This will be needed once MC/S support * is added. */ (void) snprintf(local_buf, sizeof (local_buf), "%d", mgmt->m_time - s->s_conn_head->c_up_at); xml_add_tag(buf, XML_ELEMENT_TIMECON, local_buf); buf_add_tag(buf, XML_ELEMENT_STATS, Tag_Start); t10_targ_stat(s->s_t10, buf); buf_add_tag(buf, XML_ELEMENT_STATS, Tag_End); buf_add_tag(buf, XML_ELEMENT_CONN, Tag_End); } (void) pthread_mutex_unlock(&mgmt->m_resp_mutex); queue_message_set(mgmt->m_q, 0, msg_mgmt_rply, 0); break; default: queue_prt(s->s_mgmtq, Q_SESS_ERRS, "SES%x Unknown msg type (%d) from Connection", s->s_num, m->msg_type); break; } queue_message_free(m); } while (process == True); return (NULL); }
/* * []---- * | sess_from_t10 -- handle messages from the T10 layer * []---- */ void * sess_from_t10(void *v) { iscsi_sess_t *s = (iscsi_sess_t *)v; msg_t *m; Boolean_t process = True; while (process == True) { m = queue_message_get(s->s_t10q); switch (m->msg_type) { case msg_cmd_data_rqst: queue_message_set(s->s_conn_head->c_dataq, 0, msg_cmd_data_rqst, m->msg_data); break; case msg_cmd_data_in: queue_message_set(s->s_conn_head->c_dataq, 0, msg_cmd_data_in, m->msg_data); break; case msg_cmd_cmplt: queue_message_set(s->s_conn_head->c_dataq, 0, msg_cmd_cmplt, m->msg_data); break; case msg_shutdown_rsp: if (s->s_t10) { t10_handle_destroy(s->s_t10); s->s_t10 = NULL; } (void) pthread_mutex_lock(&s->s_mutex); s->s_state = SS_SHUTDOWN_CMPLT; (void) pthread_mutex_unlock(&s->s_mutex); session_free(s); /* * Let the connection, which is the last, know * about our completion of the shutdown. */ queue_message_set(s->s_conn_head->c_dataq, 0, msg_shutdown_rsp, (void *)True); process = False; s->s_state = SS_FREE; break; default: queue_prt(s->s_mgmtq, Q_SESS_ERRS, "SES%x Unknown msg type (%d) from T10 ", s->s_num, m->msg_type); queue_message_set(s->s_conn_head->c_dataq, 0, m->msg_type, m->msg_data); break; } queue_message_free(m); } queue_free(s->s_t10q, NULL); util_title(s->s_mgmtq, Q_SESS_LOGIN, s->s_num, "End Session"); free(s); return (NULL); }
/* * []---- * | parse_text -- receive text information from initiator and parse * | * | Read in the current data based on the amount which the login PDU says * | should be available. Add it to the end of previous data if it exists. * | Previous data would be from a PDU which had the 'C' bit set and was * | stored in the connection. * | * | Once values for parameter name has been selected store outgoing string * | in text message for response. * | * | If errcode is non-NULL the appropriate login error code will be * | stored. * []---- */ Boolean_t parse_text(iscsi_conn_t *c, int dlen, char **text, int *text_length, int *errcode) { char *p = NULL; char *n; char *cur_pair; char param_rsp[32]; int plen; /* pair length */ Boolean_t rval = True; char *target_name = NULL; char *initiator_name = NULL; char param_buf[16]; if ((p = (char *)malloc(dlen)) == NULL) return (False); /* * Read in data to buffer. */ if (read(c->c_fd, p, dlen) != dlen) { free(p); return (False); } queue_prt(c->c_mgmtq, Q_CONN_NONIO, "CON%x Available text size %d\n", c->c_num, dlen); /* * Read in and toss any pad data */ if (dlen % ISCSI_PAD_WORD_LEN) { char junk[ISCSI_PAD_WORD_LEN]; int pad_len = ISCSI_PAD_WORD_LEN - (dlen % ISCSI_PAD_WORD_LEN); if (read(c->c_fd, junk, pad_len) != pad_len) { free(p); return (False); } } if (c->c_text_area != NULL) { if ((n = (char *)realloc(c->c_text_area, c->c_text_len + dlen)) == NULL) { free(p); return (False); } bcopy(p, n + c->c_text_len, dlen); /* * No longer need the space allocated to 'p' since it * will point to the aggregated area of all data. */ free(p); /* * Point 'p' to this new area for parsing and save the * combined length in dlen. */ p = n; dlen += c->c_text_len; /* * Clear the indication that space has been allocated */ c->c_text_area = NULL; c->c_text_len = 0; } /* * At this point 'p' points to the name/value parameters. Need * to cycle through each pair. */ n = p; while (dlen > 0) { cur_pair = n; plen = strlen(n); if ((n = strchr(cur_pair, ISCSI_TEXT_SEPARATOR)) == NULL) { if (errcode != NULL) *errcode = (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) | ISCSI_LOGIN_STATUS_INIT_ERR; rval = False; break; } else *n++ = '\0'; queue_prt(c->c_mgmtq, Q_CONN_LOGIN, "CON%x %-24s = %s\n", c->c_num, cur_pair, n); /* * At this point, 'cur_pair' points at the name and 'n' * points at the value. */ /* * []--------------------------------------------------[] * | The order of parameters processed matches the | * | the RFC in section 12. | * []--------------------------------------------------[] */ /* * 12.1 -- HeaderDigest * Negotiated */ if (strcmp("HeaderDigest", cur_pair) == 0) { rval = parse_digest_vals(&c->c_header_digest, cur_pair, n, text, text_length); /* * 12.1 -- DataDigest * Negotiated */ } else if (strcmp("DataDigest", cur_pair) == 0) { rval = parse_digest_vals(&c->c_data_digest, cur_pair, n, text, text_length); /* * 12.2 -- MaxConnections * Negotiated */ } else if (strcmp("MaxConnections", cur_pair) == 0) { /* ---- To be fixed ---- */ c->c_max_connections = 1; (void) snprintf(param_rsp, sizeof (param_rsp), "%d", c->c_max_connections); rval = add_text(text, text_length, cur_pair, param_rsp); /* * 12.3 -- SendTargets * Declarative */ } else if (strcmp("SendTargets", cur_pair) == 0) { if ((c->c_sess->s_type != SessionDiscovery) && (strcmp("All", n) == 0)) { rval = add_text(text, text_length, cur_pair, "Irrelevant"); } else { rval = add_targets(c, text, text_length); } /* * 12.4 -- TargetName * Declarative */ } else if (strcmp("TargetName", cur_pair) == 0) { send_named_msg(c, msg_target_name, n); target_name = n; /* * 12.5 -- IntiatorName * Declarative */ } else if (strcmp("InitiatorName", cur_pair) == 0) { send_named_msg(c, msg_initiator_name, n); initiator_name = n; /* ---- Section 12.6 is handled within TargetName ---- */ /* * 12.7 -- InitiatorAlias * Declarative */ } else if (strcmp("InitiatorAlias", cur_pair) == 0) { send_named_msg(c, msg_initiator_alias, n); /* * Sections 12.8 (TargetAddress) and 12.9 * (TargetPortalGroupTag) are handled during the SendTargets * processing. */ /* * 12.10 -- IntialR2T * Negotiated */ } else if (strcmp("InitialR2T", cur_pair) == 0) { c->c_initialR2T = True; rval = add_text(text, text_length, cur_pair, "Yes"); /* * 12.11 -- ImmediateData * Negotiated */ } else if (strcmp("ImmediateData", cur_pair) == 0) { /* * Since we can handle immediate data without * a problem just echo back what the initiator * sends. If the initiator decides to violate * the spec by sending immediate data even though * they've disabled it, it's their problem and * we'll deal with the data. */ c->c_immediate_data = strcmp(n, "No") ? True : False; rval = add_text(text, text_length, cur_pair, n); /* * 12.12 -- MaxRecvDataSegmentLength * Declarative */ } else if (strcmp("MaxRecvDataSegmentLength", cur_pair) == 0) { c->c_max_recv_data = strtol(n, NULL, 0); rval = add_text(text, text_length, cur_pair, n); /* * 12.13 -- MaxBurstLength * Negotiated */ } else if (strcmp("MaxBurstLength", cur_pair) == 0) { c->c_max_burst_len = strtol(n, NULL, 0); rval = add_text(text, text_length, cur_pair, n); /* * 12.14 -- FirstBurstLength * Negotiated */ } else if (strcmp("FirstBurstLength", cur_pair) == 0) { /* * We can handle anything the initiator wishes * to shove in our direction. So, store the value * in case we ever wish to validate input data, * but there's no real need to do so. */ c->c_first_burst_len = strtol(n, NULL, 0); rval = add_text(text, text_length, cur_pair, n); /* * 12.15 DefaultTime2Wait * Negotiated */ } else if (strcmp("DefaultTime2Wait", cur_pair) == 0) { c->c_default_time_2_wait = strtol(n, NULL, 0); rval = add_text(text, text_length, cur_pair, n); /* * 12.16 -- DefaultTime2Retain * Negotiated */ } else if (strcmp("DefaultTime2Retain", cur_pair) == 0) { c->c_default_time_2_retain = strtol(n, NULL, 0); rval = add_text(text, text_length, cur_pair, n); /* * 12.17 -- MaxOutstandingR2T * Negotiated */ } else if (strcmp("MaxOutstandingR2T", cur_pair) == 0) { /* * Save the value, but at most we'll toss out * one R2T packet. */ c->c_max_outstanding_r2t = strtol(n, NULL, 0); rval = add_text(text, text_length, cur_pair, n); /* * 12.18 -- DataPDUInOder * Negotiated */ } else if (strcmp("DataPDUInOrder", cur_pair) == 0) { /* * We can handle DataPDU's out of order and * currently we'll only send them in order. We're * to far removed from the hardware to see data * coming off of the platters out of order so * it's unlikely we'd ever implement this feature. * Store the parameter and echo back the initiators * request. */ c->c_data_pdu_in_order = strcmp(n, "Yes") == 0 ? True : False; rval = add_text(text, text_length, cur_pair, n); /* * 12.19 -- DataSequenceInOrder * Negotiated */ } else if (strcmp("DataSequenceInOrder", cur_pair) == 0) { /* * Currently we're set up to look at and require * PDU sequence numbers be in order. The check * now is only done as a prelude to supporting * MC/S and guaranteeing the order of incoming * packets on different connections. */ c->c_data_sequence_in_order = True; rval = add_text(text, text_length, cur_pair, "Yes"); /* * 12.20 -- ErrorRecoveryLevel * Negotiated */ } else if (strcmp("ErrorRecoveryLevel", cur_pair) == 0) { c->c_erl = 0; (void) snprintf(param_rsp, sizeof (param_rsp), "%d", c->c_erl); rval = add_text(text, text_length, cur_pair, param_rsp); /* * 12.21 -- SessionType * Declarative */ } else if (strcmp("SessionType", cur_pair) == 0) { c->c_sess->s_type = strcmp(n, "Discovery") == 0 ? SessionDiscovery : SessionNormal; /* * Appendix A 3.1 -- IFMarker * Negotiated */ } else if (strcmp("IFMarker", cur_pair) == 0) { c->c_ifmarker = False; rval = add_text(text, text_length, cur_pair, "No"); /* * Appendix A 3.1 -- OFMarker * Negotiated */ } else if (strcmp("OFMarker", cur_pair) == 0) { c->c_ofmarker = False; rval = add_text(text, text_length, cur_pair, "No"); } else if ((strcmp("AuthMethod", cur_pair) == 0) || (strcmp("CHAP_A", cur_pair) == 0) || (strcmp("CHAP_I", cur_pair) == 0) || (strcmp("CHAP_C", cur_pair) == 0) || (strcmp("CHAP_N", cur_pair) == 0) || (strcmp("CHAP_R", cur_pair) == 0)) { rval = add_text(&(c->auth_text), &c->auth_text_length, cur_pair, n); } else { /* * It's perfectly legitimate for an initiator to * send us a parameter we don't currently understand. * For example, an initiator that supports iSER will * send an RDMA options parameter. If we respond with * a valid return value it knows to switch to iSER * for future processing. */ rval = add_text(text, text_length, cur_pair, "NotUnderstood"); /* * Go ahead a log this information in case we see * something unexpected. */ queue_prt(c->c_mgmtq, Q_CONN_ERRS, "CON%x Unknown parameter %s=%s\n", c->c_num, cur_pair, n); } /* * If parsed both Initiator and Target names have been parsed, * then it is now time to load the connection parameters. * * This may fail because the target doesn't exist or the * initiator doesn't have permission to access this target. */ if ((target_name != NULL) && (initiator_name != NULL)) { if ((rval = connection_parameters_get(c, target_name)) == False) { if ((errcode != NULL) && (*errcode == 0)) *errcode = (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) | ISCSI_LOGIN_STATUS_TGT_FORBIDDEN; } else if ((rval = add_text(text, text_length, "TargetAlias", c->c_targ_alias)) == True) { /* * Add TPGT now */ (void) snprintf(param_buf, sizeof (param_buf), "%d", c->c_tpgt); rval = add_text(text, text_length, "TargetPortalGroupTag", param_buf); target_name = initiator_name = NULL; } } if (rval == False) { /* * Make sure the caller wants error status and that it * hasn't already been set. */ if ((errcode != NULL) && (*errcode == 0)) *errcode = (ISCSI_STATUS_CLASS_TARGET_ERR << 8) | ISCSI_LOGIN_STATUS_TARGET_ERROR; break; } /* * next pair of parameters. 1 is added to include the NULL * byte and the end of each string. */ n = cur_pair + plen + 1; dlen -= (plen + 1); } if (p != NULL) free(p); return (rval); }
/* * Register all iscsi target nodes from the XML database * Alway use the ISNS_FLAG_REPLACE_REG flag */ int isns_reg_all() { int so; uint32_t flags = ISNS_FLAG_REPLACE_REG; isns_pdu_t *cmd = NULL; isns_rsp_t *rsp = NULL; char *n = NULL; char *a = NULL; char alias[MAXNAMELEN]; char iname[MAXNAMELEN]; tgt_node_t *tgt = NULL; int ret = -1; int tgt_cnt = 0; if (isns_server_connection_thr_running == False) { syslog(LOG_ERR, "isns_reg_all: iSNS discovery is not running." " Check the previous iSNS initialization error."); return (-1); } /* * get the 1st target and use it for the source attribute */ if ((tgt = tgt_node_next_child(targets_config, XML_ELEMENT_TARG, tgt)) == NULL) { return (0); } if (tgt->x_value == NULL) { syslog(LOG_ALERT, "ISNS: target with NULL local name\n"); return (-1); } if (tgt_find_value_str(tgt, XML_ELEMENT_INAME, &n) == FALSE) { syslog(LOG_ALERT, "ISNS: no XML_ELEMENT_INAME found\n"); return (-1); } (void) strcpy(iname, n); free(n); if ((so = isns_open(isns_args.server)) == -1) { syslog(LOG_ALERT, "ISNS: fails to connect to %s\n", isns_args.server); return (-1); } if (isns_create_pdu(ISNS_DEV_ATTR_REG, flags, &cmd) != 0) { goto error; } /* source attribute */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(iname), iname, 0) != 0) { goto error; } /* add message key attribute */ if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { goto error; } /* add delimiter */ if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) != 0) { goto error; } /* entity id */ if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { goto error; } /* entity type */ if (isns_append_attr(cmd, ISNS_ENTITY_PROTOCOL_ATTR_ID, ISNS_ENTITY_TYP_SZ, NULL, ISNS_ENTITY_PROTOCOL_ISCSI) != 0) { goto error; } /* portal ip-addr */ if (isns_append_attr(cmd, ISNS_PORTAL_IP_ADDR_ATTR_ID, eid_ip.ai_addrlen, (void *)&eid_ip.ip_adr, eid_ip.ip_len) != 0) { goto error; } /* portal port */ if (isns_append_attr(cmd, ISNS_PORTAL_PORT_ATTR_ID, ISNS_PORT_SZ, NULL, iscsi_port) != 0) { goto error; } /* ESI interval */ if (isns_append_attr(cmd, ISNS_ESI_INTERVAL_ATTR_ID, ISNS_ESI_TICK_SZ, NULL, 10) != 0) { goto error; } /* scn port */ if (isns_append_attr(cmd, ISNS_SCN_PORT_ATTR_ID, ISNS_PORT_SZ, NULL, scn_port) != 0) { goto error; } /* esi port */ if (isns_append_attr(cmd, ISNS_ESI_PORT_ATTR_ID, ISNS_PORT_SZ, NULL, scn_port) != 0) { goto error; } /* * Open targets_config and devAttrReg all nodes */ tgt = NULL; while ((tgt = tgt_node_next_child(targets_config, XML_ELEMENT_TARG, tgt)) != NULL) { if (tgt->x_value == NULL) { syslog(LOG_ALERT, "ISNS: target with NULL name\n"); continue; } /* use this value as alias if alias is not set */ (void) strcpy(alias, tgt->x_value); if (tgt_find_value_str(tgt, XML_ELEMENT_INAME, &n) == FALSE) { continue; } (void) strcpy(iname, n); free(n); /* find alias */ if (tgt_find_value_str(tgt, XML_ELEMENT_ALIAS, &a) == TRUE) { (void) strcpy(alias, a); free(a); } tgt_cnt++; /* increment target count */ /* operation attributes */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(iname), iname, 0) != 0) { goto error; } if (isns_append_attr(cmd, ISNS_ISCSI_NODE_TYPE_ATTR_ID, 4, NULL, ISNS_TARGET_NODE_TYPE) != 0) { goto error; } if (isns_append_attr(cmd, ISNS_ISCSI_ALIAS_ATTR_ID, STRLEN(alias), alias, 0) != 0) { goto error; } if (append_tpgt(tgt, cmd) != 0) { goto error; } } /* send pdu */ if (isns_send(so, cmd) == -1) { goto error; } /* get isns response */ if (isns_recv(so, &rsp) == -1) { goto error; } /* process response */ if (process_rsp(cmd, rsp) == 0) { ret = 0; num_reg = tgt_cnt; queue_prt(mgmtq, Q_ISNS_DBG, "DevAttrRegAll successful"); } else { syslog(LOG_ALERT, "DevAttrReg failed"); } error: if (cmd) isns_free_pdu(cmd); if (rsp) isns_free_pdu(rsp); isns_close(so); return (ret); }
void * port_watcher(void *v) { int s, fd, on = 1; char debug[80]; struct sockaddr_in sin_ip; struct sockaddr_in6 sin6_ip; struct sockaddr_storage st; socklen_t socklen; iscsi_conn_t *conn; port_args_t *p = (port_args_t *)v; target_queue_t *q = p->port_mgmtq; int l, accept_err_sleep = 1; pthread_t junk; struct in_addr addr; struct in6_addr addr6; /* * Try creating an IPv6 socket first * If failed, try creating an IPv4 socket */ if ((s = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { queue_str(q, Q_GEN_ERRS, msg_log, "Opening IPv4 socket"); if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { queue_str(q, Q_GEN_ERRS, msg_log, "Can't open socket"); return (NULL); } else { bzero(&sin_ip, sizeof (sin_ip)); sin_ip.sin_family = AF_INET; sin_ip.sin_port = htons(p->port_num); sin_ip.sin_addr.s_addr = INADDR_ANY; (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)); if ((bind(s, (struct sockaddr *)&sin_ip, sizeof (sin_ip))) < 0) { (void) snprintf(debug, sizeof (debug), "bind on port %d failed, errno %d", p->port_num, errno); queue_str(q, Q_GEN_ERRS, msg_status, debug); return (NULL); } } } else { queue_str(q, Q_GEN_DETAILS, msg_log, "Got IPv6 socket"); bzero(&sin6_ip, sizeof (sin6_ip)); sin6_ip.sin6_family = AF_INET6; sin6_ip.sin6_port = htons(p->port_num); sin6_ip.sin6_addr = in6addr_any; (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)); if ((bind(s, (struct sockaddr *)&sin6_ip, sizeof (sin6_ip))) < 0) { (void) snprintf(debug, sizeof (debug), "bind on port %d failed, errno %d", p->port_num, errno); queue_str(q, Q_GEN_ERRS, msg_status, debug); return (NULL); } } if (listen(s, 5) < 0) { queue_str(q, Q_GEN_ERRS, msg_status, "listen failed"); return (NULL); } /*CONSTANTCONDITION*/ while (1) { socklen = sizeof (st); if ((fd = accept(s, (struct sockaddr *)&st, &socklen)) < 0) { accept_err_sleep *= 2; (void) sleep(accept_err_sleep); if (accept_err_sleep > 60) { accept_err_sleep = 1; queue_prt(q, Q_GEN_ERRS, "accept failed, errno %d", errno); } continue; } l = 128 * 1024; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&l, sizeof (l)) < 0) queue_str(q, Q_GEN_ERRS, msg_status, "setsockopt failed"); if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&l, sizeof (l)) < 0) queue_str(q, Q_GEN_ERRS, msg_status, "setsockopt failed"); l = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&l, sizeof (l)) < 0) queue_str(q, Q_GEN_ERRS, msg_status, "setsockopt keepalive failed"); if ((conn = (iscsi_conn_t *)calloc(sizeof (iscsi_conn_t), 1)) == NULL) { /* * If we fail to get memory this is all rather * pointless, since it's unlikely that queue_str * could malloc memory to send a message. */ queue_str(q, Q_GEN_ERRS, msg_status, "connection malloc failed"); return (NULL); } /* * Save initiator sockaddr for future use */ conn->c_initiator_sockaddr = st; socklen = sizeof (st); if (getsockname(fd, (struct sockaddr *)&st, &socklen) == 0) { /* * Save target sockaddr for future use */ addr6 = ((struct sockaddr_in6 *)&st)->sin6_addr; if (st.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&addr6)) { /* * If target address is IPv4 mapped IPv6 address * convert it to IPv4 address */ IN6_V4MAPPED_TO_INADDR(&addr6, &addr); ((struct sockaddr_in *)&st)->sin_addr = addr; st.ss_family = AF_INET; } conn->c_target_sockaddr = st; } conn->c_fd = fd; conn->c_mgmtq = q; conn->c_up_at = time(NULL); conn->c_state = S1_FREE; (void) pthread_mutex_init(&conn->c_mutex, NULL); (void) pthread_mutex_init(&conn->c_state_mutex, NULL); (void) pthread_mutex_lock(&port_mutex); conn->c_num = port_conn_num++; if (conn_head == NULL) { conn_head = conn; assert(conn_tail == NULL); conn_tail = conn; } else { conn_tail->c_next = conn; conn->c_prev = conn_tail; conn_tail = conn; } (void) pthread_mutex_unlock(&port_mutex); (void) pthread_create(&junk, NULL, conn_process, conn); } return (NULL); }
/*ARGSUSED*/ void sbc_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { struct mode_header *mode_hdr; char *np; disk_params_t *d; disk_io_t *io; if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL) return; /* * SPC-3 Revision 21c section 6.8 * Reserve bit checks */ if ((cdb[1] & ~SPC_MODE_SENSE_DBD) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } /* * Zero length causes a simple ack to occur. */ if (cdb[4] == 0) { trans_send_complete(cmd, STATUS_GOOD); return; } io = sbc_io_alloc(cmd); /* * Make sure that we have enough room in the data buffer. We'll * only send back the amount requested though */ io->da_data_len = MAX(cdb[4], sizeof (struct mode_format) + sizeof (struct mode_geometry) + sizeof (struct mode_control_scsi3) + sizeof (struct mode_cache_scsi3) + sizeof (struct mode_info_ctrl) + (MODE_BLK_DESC_LENGTH * 5)); if ((io->da_data = (char *)calloc(1, io->da_data_len)) == NULL) { sbc_io_free(io); trans_send_complete(cmd, STATUS_BUSY); return; } io->da_clear_overlap = False; io->da_data_alloc = True; mode_hdr = (struct mode_header *)io->da_data; switch (cdb[2]) { case MODE_SENSE_PAGE3_CODE: if ((d->d_heads == 0) && (d->d_cyl == 0) && (d->d_spt == 0)) { sbc_io_free(io); spc_unsupported(cmd, cdb, cdb_len); return; } mode_hdr->length = sizeof (struct mode_format); mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH; (void) sense_page3(d, io->da_data + sizeof (*mode_hdr) + mode_hdr->bdesc_length); break; case MODE_SENSE_PAGE4_CODE: if ((d->d_heads == 0) && (d->d_cyl == 0) && (d->d_spt == 0)) { sbc_io_free(io); spc_unsupported(cmd, cdb, cdb_len); return; } mode_hdr->length = sizeof (struct mode_geometry); mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH; (void) sense_page4(d, io->da_data + sizeof (*mode_hdr) + mode_hdr->bdesc_length); break; case MODE_SENSE_CACHE: mode_hdr->length = sizeof (struct mode_cache_scsi3); mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH; (void) sense_cache(d, io->da_data + sizeof (*mode_hdr) + mode_hdr->bdesc_length); break; case MODE_SENSE_CONTROL: mode_hdr->length = sizeof (struct mode_control_scsi3); mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH; (void) sense_mode_control(cmd->c_lu, io->da_data + sizeof (*mode_hdr) + mode_hdr->bdesc_length); break; case MODE_SENSE_INFO_CTRL: (void) sense_info_ctrl(io->da_data); break; case MODE_SENSE_SEND_ALL: /* * SPC-3 revision 21c * Section 6.9.1 Table 97 * "Return all subpage 00h mode pages in page_0 format" */ if (io->da_data_len < (sizeof (struct mode_format) + sizeof (struct mode_geometry) + sizeof (struct mode_control_scsi3) + sizeof (struct mode_info_ctrl))) { /* * Believe it or not, there's an initiator out * there which sends a mode sense request for all * of the pages, without always sending a data-in * size which is large enough. * NOTE: Need to check the error key returned * here and see if something else should be used. */ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); trans_send_complete(cmd, STATUS_CHECK); } else { /* * If we don't have geometry then don't attempt * report that information. */ if (d->d_heads && d->d_cyl && d->d_spt) { np = sense_page3(d, io->da_data); np = sense_page4(d, np); } np = sense_cache(d, np); np = sense_mode_control(cmd->c_lu, np); (void) sense_info_ctrl(np); } break; case 0x00: /* * SPC-3 Revision 21c, section 6.9.1 * Table 97 -- Mode page code usage for all devices * Page Code 00 == Vendor specific. We are going to return * zeros. */ break; default: queue_prt(mgmtq, Q_STE_ERRS, "SBC%x LUN%d Unsupported mode_sense request 0x%x", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, cdb[2]); spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; break; } if (trans_send_datain(cmd, io->da_data, cdb[4], 0, sbc_io_free, True, io) == False) { trans_send_complete(cmd, STATUS_BUSY); } }
/*ARGSUSED*/ static void sbc_write(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { union scsi_cdb *u; diskaddr_t addr; uint64_t err_blkno; uint32_t cnt; uchar_t addl_sense_len; disk_params_t *d; disk_io_t *io; size_t max_out; void *mmap_area; if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL) return; /*LINTED*/ u = (union scsi_cdb *)cdb; switch (u->scc_cmd) { case SCMD_WRITE: /* * SBC-2 revision 16, section 5.24 * Reserve bit checks. */ if ((cdb[1] & 0xe0) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG0ADDR(u); cnt = GETG0COUNT(u); /* * SBC-2 Revision 16/Section 5.24 WRITE(6) * A TRANSFER LENGHT of 0 indicates that 256 logical blocks * shall be written. */ if (cnt == 0) cnt = 256; break; case SCMD_WRITE_G1: /* * SBC-2 revision 16, section 5.25 * Reserve bit checks. */ if ((cdb[1] & 0x6) || cdb[6] || SAM_CONTROL_BYTE_RESERVED(cdb[9])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG1ADDR(u); cnt = GETG1COUNT(u); break; case SCMD_WRITE_G4: /* * SBC-2 revision 16, section 5.27 * Reserve bit checks. */ if ((cdb[1] & 0x6) || cdb[14] || SAM_CONTROL_BYTE_RESERVED(cdb[15])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)GETG4LONGADDR(u); cnt = GETG4COUNT(u); break; default: queue_prt(mgmtq, Q_STE_ERRS, "Unprocessed WRITE type"); spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } if ((addr + cnt) > d->d_size) { if (addr > d->d_size) err_blkno = addr; else err_blkno = d->d_size; /* * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris * doesn't care about these values when key is set * to KEY_ILLEGAL_REQUEST. */ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN) addl_sense_len = INFORMATION_SENSE_DESCR; else addl_sense_len = 0; spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len); spc_sense_info(cmd, err_blkno); spc_sense_ascq(cmd, 0x21, 0x00); trans_send_complete(cmd, STATUS_CHECK); queue_prt(mgmtq, Q_STE_ERRS, "SBC%x LUN%d WRITE Illegal sector " "(0x%llx + 0x%x) > 0x%ullx", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr, cnt, d->d_size); return; } if (cnt == 0) { queue_prt(mgmtq, Q_STE_NONIO, "SBC%x LUN%d WRITE zero block count for addr 0x%x", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr); trans_send_complete(cmd, STATUS_GOOD); return; } io = (disk_io_t *)cmd->c_emul_id; if (io == NULL) { io = sbc_io_alloc(cmd); io->da_lba = addr; io->da_lba_cnt = cnt; io->da_clear_overlap = False; io->da_aio.a_aio_cmplt = sbc_write_cmplt; io->da_aio.a_id = io; /* * Only update the statistics the first time through * for this particular command. If the requested transfer * is larger than the transport can handle this routine * will be called many times. */ cmd->c_lu->l_cmds_write++; cmd->c_lu->l_sects_write += cnt; } #ifdef FULL_DEBUG queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d blk 0x%llx, cnt %d, offset 0x%llx, size %d", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr, cnt, io->da_offset, io->da_data_len); #endif /* * If a transport sets the maximum output value to zero we'll * just request the entire amount. Otherwise, transfer no more * than the maximum output or the reminder, whichever is less. */ max_out = cmd->c_lu->l_targ->s_maxout; io->da_data_len = max_out ? MIN(max_out, (cnt * 512) - io->da_offset) : (cnt * 512); mmap_area = T10_MMAP_AREA(cmd); if (mmap_area != MAP_FAILED) { io->da_data_alloc = False; io->da_data = (char *)mmap_area + (addr * 512LL) + io->da_offset; sbc_overlap_check(io); } else if ((io->da_data = (char *)malloc(io->da_data_len)) == NULL) { trans_send_complete(cmd, STATUS_BUSY); return; } else { io->da_data_alloc = True; } if (trans_rqst_dataout(cmd, io->da_data, io->da_data_len, io->da_offset, io) == False) { trans_send_complete(cmd, STATUS_BUSY); } }
/*ARGSUSED*/ static void sbc_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { /*LINTED*/ union scsi_cdb *u = (union scsi_cdb *)cdb; diskaddr_t addr; off_t offset = 0; uint32_t cnt, min; disk_io_t *io; void *mmap_data = T10_MMAP_AREA(cmd); uint64_t err_blkno; disk_params_t *d; uchar_t addl_sense_len; if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL) return; switch (u->scc_cmd) { case SCMD_READ: /* * SBC-2 Revision 16, section 5.5 * Reserve bit checks */ if ((cdb[1] & 0xe0) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG0ADDR(u); cnt = GETG0COUNT(u); /* * SBC-2 Revision 16 * Section: 5.5 READ(6) command * A TRANSFER LENGTH field set to zero specifies * that 256 logical blocks shall be read. */ if (cnt == 0) cnt = 256; break; case SCMD_READ_G1: /* * SBC-2 Revision 16, section 5.6 * Reserve bit checks. */ if ((cdb[1] & 6) || cdb[6] || SAM_CONTROL_BYTE_RESERVED(cdb[9])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG1ADDR(u); cnt = GETG1COUNT(u); break; case SCMD_READ_G4: /* * SBC-2 Revision 16, section 5.8 * Reserve bit checks */ if ((cdb[1] & 0x6) || cdb[14] || SAM_CONTROL_BYTE_RESERVED(cdb[15])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = GETG4LONGADDR(u); cnt = GETG4COUNT(u); break; default: spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); trans_send_complete(cmd, STATUS_CHECK); return; } if ((addr + cnt) > d->d_size) { if (addr > d->d_size) err_blkno = addr; else err_blkno = d->d_size; /* * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris * doesn't care about these values when key is set * to KEY_ILLEGAL_REQUEST. */ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN) addl_sense_len = INFORMATION_SENSE_DESCR; else addl_sense_len = 0; spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len); spc_sense_info(cmd, err_blkno); spc_sense_ascq(cmd, 0x21, 0x00); trans_send_complete(cmd, STATUS_CHECK); queue_prt(mgmtq, Q_STE_ERRS, "SBC%x LUN%d READ Illegal sector " "(0x%llx + 0x%x) > 0x%ullx", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr, cnt, d->d_size); return; } cmd->c_lu->l_cmds_read++; cmd->c_lu->l_sects_read += cnt; if (cnt == 0) { trans_send_complete(cmd, STATUS_GOOD); return; } do { io = sbc_io_alloc(cmd); min = MIN((cnt * 512) - offset, T10_MAX_OUT(cmd)); io->da_lba = addr; io->da_lba_cnt = cnt; io->da_offset = offset; io->da_data_len = min; #ifdef FULL_DEBUG queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d blk 0x%llx, cnt %d, offset 0x%llx, size %d", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr, cnt, io->da_offset, min); #endif if (mmap_data != MAP_FAILED) { io->da_clear_overlap = True; io->da_data_alloc = False; io->da_aio.a_aio.aio_return = min; io->da_data = (char *)mmap_data + (addr * 512LL) + io->da_offset; sbc_overlap_store(io); sbc_read_cmplt((emul_handle_t)io); } else { if ((io->da_data = (char *)malloc(min)) == NULL) { trans_send_complete(cmd, STATUS_BUSY); return; } io->da_clear_overlap = False; io->da_data_alloc = True; io->da_aio.a_aio_cmplt = sbc_read_cmplt; io->da_aio.a_id = io; trans_aioread(cmd, io->da_data, min, (addr * 512LL) + (off_t)io->da_offset, (aio_result_t *)io); } offset += min; } while (offset < (off_t)(cnt * 512)); }
/* * DevAttrQry for iscsi initiator * See RFC 4171 Sect. 5.6.5.2 for query detail */ static int isns_dev_attr_qry(int so, char *target, char *initiator) { isns_pdu_t *cmd; isns_rsp_t *rsp; uint32_t flags = 0; int ret = -1; size_t remain; isns_tlv_t *tlv; uint8_t *ptr; queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_DEV_ATTR_QRY"); if (isns_create_pdu(ISNS_DEV_ATTR_QRY, flags, &cmd) != 0) { return (-1); } /* source attribute */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(target), target, 0) == -1) { goto error; } /* message key attribute */ /* iscsi initiator node type */ if (isns_append_attr(cmd, ISNS_ISCSI_NODE_TYPE_ATTR_ID, ISNS_NODE_TYP_SZ, NULL, ISNS_INITIATOR_NODE_TYPE) == -1) { goto error; } /* delimiter */ if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) == -1) { goto error; } /* * operating attributes * Query Iscsi initiator with zero length TLV operating * attribute */ /* iscsi name */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, 0, NULL, 0) != 0) { goto error; } if (isns_send(so, cmd) == -1) { syslog(LOG_ERR, "isns_dev_attr_qry fails isns_send"); goto error; } /* recv response */ if (isns_recv(so, &rsp) == -1) { syslog(LOG_ERR, "isns_dev_attr_qry fails isns_recv "); goto error; } /* process response */ if ((ret = process_rsp(cmd, rsp)) == 0) { /* compare initiator name to the response, success if found */ /* subtract out status word */ remain = rsp->pdu_len - ISNS_STATUS_SZ; ptr = rsp->data; while (remain > 0) { /* LINTED */ tlv = (isns_tlv_t *)ptr; /* debug only */ print_ntoh_tlv(tlv); /* process tag-len-value */ ntoh_tlv(tlv); /* * let's process the data, only interested * in iscsi name, skip everything else for * now. */ if (tlv->attr_id == ISNS_ISCSI_NAME_ATTR_ID) { if (strncmp((char *)tlv->attr_value, initiator, tlv->attr_len) == 0) { break; } } /* next tlv */ remain -= ISNS_ATTR_SZ(tlv->attr_len); ptr += ISNS_ATTR_SZ(tlv->attr_len); } ret = (remain > 0) ? 1 : 0; } error: if (cmd) isns_free_pdu(cmd); if (rsp) isns_free_pdu(rsp); return (ret); }
/* * process_scn() * -Added/Updated object: nop, initiator is verified during connect * * -Removed object: logout_targ if still connected * * RFC 4171 section 5.6.5.9 * destination attribute is always the 1st attribute in the SCN message, * then follows by SCN_BITMAP(35) & Source_Attribute(32) */ static void process_scn(int so, isns_pdu_t *scn) { uint8_t *ptr = scn->payload; isns_tlv_t *tlv; uint16_t cnt = 0; uint32_t got_dest = 0; uint32_t got_source = 0; uint32_t bitmap = 0; uint32_t got_bitmap = 0; char dest[MAXNAMELEN]; char source[MAXNAMELEN]; queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN %u\n", scn->payload_len); if (scn->payload_len < TAG_LEN_SZ) { syslog(LOG_ALERT, "ISNS SCN message error\n"); return; } while (cnt < scn->payload_len) { /* LINTED */ tlv = (isns_tlv_t *)ptr; tlv->attr_id = ntohl(tlv->attr_id); tlv->attr_len = ntohl(tlv->attr_len); queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN %u %u\n", tlv->attr_id, tlv->attr_len); /* * devAttrQry the source attribute, process if node_type * is initiator */ switch (tlv->attr_id) { case ISNS_ISCSI_NAME_ATTR_ID: if (got_dest == 0) { bcopy(tlv->attr_value, dest, tlv->attr_len); queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN dest %s\n", dest); got_dest = 1; } else { bcopy(tlv->attr_value, source, tlv->attr_len); queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN source %s\n", source); got_source = 1; } break; case ISNS_ISCSI_SCN_BITMAP_ATTR_ID: bcopy(tlv->attr_value, &bitmap, tlv->attr_len); bitmap = ntohl(bitmap); queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN bitmap %u\n", bitmap); got_bitmap = 1; break; default: queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN DEFAULT\n"); break; } if (got_source && !got_bitmap) { queue_prt(mgmtq, Q_ISNS_DBG, "process_scn: message out-of-order\n"); return; } if (got_source && got_bitmap) { switch (bitmap) { case ISNS_OBJ_ADDED: case ISNS_OBJ_UPDATED: queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN OBJ ADDED"); (void) isns_update(); break; case ISNS_OBJ_REMOVED: queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN OBJ REMOVED"); /* logout target */ if (got_dest == 0) { syslog(LOG_ALERT, "ISNS protocol error\n"); continue; } logout_targ(dest); break; default: break; } /* clear got_xxx */ got_source = 0; got_bitmap = 1; } /* next attribute */ cnt += ISNS_ATTR_SZ(tlv->attr_len); ptr += ISNS_ATTR_SZ(tlv->attr_len); } queue_prt(mgmtq, Q_ISNS_DBG, "DONE PROCESS_SCN\n"); }
/* * DevAttrDereg */ static int isns_dev_attr_dereg(int so, char *node) { isns_pdu_t *cmd = NULL; isns_rsp_t *rsp = NULL; uint32_t flags = 0; int ret = -1; queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_DEV_ATTR_DEREG"); if (isns_create_pdu(ISNS_DEV_DEREG, flags, &cmd) != 0) { return (-1); } /* add source attribute */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(node), node, 0) != 0) { goto error; } /* add delimiter */ if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) != 0) { goto error; } /* add operation attributes */ if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(node), node, 0) != 0) { goto error; } /* send pdu */ if (isns_send(so, cmd) == -1) { syslog(LOG_ERR, "isns_dev_attr_dereg fails isns_send"); goto error; } /* get isns response */ if (isns_recv(so, &rsp) == -1) { syslog(LOG_ERR, "isns_dev_attr_dereg fails isns_recv "); goto error; } /* process response */ if (process_rsp(cmd, rsp) == 0) { /* * Keep the num_reg to a non-negative number. * num_reg is used to keep track of whether there was * any registration occurred or not. Deregstration should * be followed by registration but in case dereg occurs * and somehow it is succeeded keeping num_reg to 0 prevent * any negative effect on subsequent registration. */ if (num_reg > 0) num_reg--; ret = 0; } error: /* Free all resouces here */ if (cmd) isns_free_pdu(cmd); if (rsp) isns_free_pdu(rsp); return (ret); }