static int iscsi_login_add_authalgorithm(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM) { return 0; } strncpy(str,"CHAP_A=5",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; }
static int iscsi_login_add_ofmarker(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send OFMarker during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"OFMarker=No",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; }
static int iscsi_login_add_authmethod(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { return 0; } strncpy(str,"AuthMethod=CHAP,None",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; }
int iscsi_disconnect(struct iscsi_context *iscsi) { if (iscsi->fd == -1) { iscsi_set_error(iscsi, "Trying to disconnect " "but not connected"); return -1; } close(iscsi->fd); if (iscsi->connected_portal) DPRINTF(iscsi,2,"disconnected from portal %s",iscsi->connected_portal); iscsi->fd = -1; iscsi->is_connected = 0; return 0; }
int iscsi_init_transport(struct iscsi_context *iscsi, enum iscsi_transport_type transport) { iscsi->transport = transport; switch (iscsi->transport) { case TCP_TRANSPORT: iscsi_init_tcp_transport(iscsi); break; #ifdef HAVE_LINUX_ISER case ISER_TRANSPORT: iscsi_init_iser_transport(iscsi); break; #endif default: iscsi_set_error(iscsi, "Unfamiliar transport type"); return -1; } return 0; }
void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { if (pdu == NULL) { iscsi_set_error(iscsi, "trying to free NULL pdu"); return; } free(pdu->outdata.data); pdu->outdata.data = NULL; free(pdu->indata.data); pdu->indata.data = NULL; if (pdu->scsi_cbdata) { iscsi_free_scsi_cbdata(pdu->scsi_cbdata); pdu->scsi_cbdata = NULL; } free(pdu); }
static int iscsi_tcp_disconnect(struct iscsi_context *iscsi) { if (iscsi->fd == -1) { iscsi_set_error(iscsi, "Trying to disconnect " "but not connected"); return -1; } close(iscsi->fd); if (!(iscsi->pending_reconnect && iscsi->old_iscsi) && iscsi->connected_portal[0]) { ISCSI_LOG(iscsi, 2, "disconnected from portal %s",iscsi->connected_portal); } iscsi->fd = -1; iscsi->is_connected = 0; iscsi->is_corked = 0; return 0; }
void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { if (pdu == NULL) { iscsi_set_error(iscsi, "trying to free NULL pdu"); return; } if (pdu->outdata.size <= iscsi->smalloc_size) { iscsi_sfree(iscsi, pdu->outdata.data); } else { iscsi_free(iscsi, pdu->outdata.data); } pdu->outdata.data = NULL; if (pdu->indata.size <= iscsi->smalloc_size) { iscsi_sfree(iscsi, pdu->indata.data); } else { iscsi_free(iscsi, pdu->indata.data); } pdu->indata.data = NULL; iscsi_sfree(iscsi, pdu); }
int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { struct iscsi_discovery_address *targets = NULL; unsigned char *ptr = in->data; int size = in->data_pos; /* verify the response looks sane */ if (in->hdr[1] != ISCSI_PDU_TEXT_FINAL) { iscsi_set_error(iscsi, "unsupported flags in text " "reply %02x", in->hdr[1]); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return -1; } while (size > 0) { unsigned char *end; int len; end = memchr(ptr, 0, size); if (end == NULL) { iscsi_set_error(iscsi, "NUL not found after offset %ld " "when parsing discovery data", (long)(ptr - in->data)); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } len = end - ptr; if (len == 0) { break; } /* parse the strings */ if (!strncmp((char *)ptr, "TargetName=", 11)) { struct iscsi_discovery_address *target; target = iscsi_zmalloc(iscsi, sizeof(struct iscsi_discovery_address)); if (target == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } target->target_name = iscsi_strdup(iscsi,(char *)ptr+11); if (target->target_name == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target name"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free(iscsi, target); target = NULL; iscsi_free_discovery_addresses(iscsi, targets); return -1; } target->next = targets; targets = target; } else if (!strncmp((char *)ptr, "TargetAddress=", 14)) { struct iscsi_target_portal *portal; if (targets == NULL) { iscsi_set_error(iscsi, "Invalid discovery " "reply"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } portal = iscsi_zmalloc(iscsi, sizeof(struct iscsi_target_portal)); if (portal == NULL) { iscsi_set_error(iscsi, "Failed to malloc " "portal structure"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } portal->next = targets->portals; targets->portals = portal; portal->portal = iscsi_strdup(iscsi, (char *)ptr+14); if (portal->portal == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target address"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } } else { iscsi_set_error(iscsi, "Don't know how to handle " "discovery string : %s", ptr); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } ptr += len + 1; size -= len + 1; } if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_GOOD, targets, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return 0; }
int iscsi_tcp_service(struct iscsi_context *iscsi, int revents) { if (iscsi->fd < 0) { return 0; } if (iscsi->pending_reconnect) { if (time(NULL) >= iscsi->next_reconnect) { return iscsi_reconnect(iscsi); } else { if (iscsi->old_iscsi) { return 0; } } } if (revents & POLLERR) { int err = 0; socklen_t err_size = sizeof(err); if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, (char *)&err, &err_size) != 0 || err != 0) { if (err == 0) { err = errno; } iscsi_set_error(iscsi, "iscsi_service: socket error " "%s(%d).", strerror(err), err); } else { iscsi_set_error(iscsi, "iscsi_service: POLLERR, " "Unknown socket error."); } if (iscsi->socket_status_cb) { iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); iscsi->socket_status_cb = NULL; } return iscsi_service_reconnect_if_loggedin(iscsi); } if (revents & POLLHUP) { iscsi_set_error(iscsi, "iscsi_service: POLLHUP, " "socket error."); if (iscsi->socket_status_cb) { iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); iscsi->socket_status_cb = NULL; } return iscsi_service_reconnect_if_loggedin(iscsi); } if (iscsi->is_connected == 0 && revents&POLLOUT) { int err = 0; socklen_t err_size = sizeof(err); struct sockaddr_in local; socklen_t local_l = sizeof(local); if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, (char *)&err, &err_size) != 0 || err != 0) { if (err == 0) { err = errno; } iscsi_set_error(iscsi, "iscsi_service: socket error " "%s(%d) while connecting.", strerror(err), err); if (iscsi->socket_status_cb) { iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); iscsi->socket_status_cb = NULL; } return iscsi_service_reconnect_if_loggedin(iscsi); } if (getsockname(iscsi->fd, (struct sockaddr *) &local, &local_l) == 0) { ISCSI_LOG(iscsi, 2, "connection established (%s:%u -> %s)", inet_ntoa(local.sin_addr), (unsigned)ntohs(local.sin_port),iscsi->connected_portal); } iscsi->is_connected = 1; if (iscsi->socket_status_cb) { iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL, iscsi->connect_data); iscsi->socket_status_cb = NULL; } return 0; } if (revents & POLLIN) { if (iscsi_read_from_socket(iscsi) != 0) { return iscsi_service_reconnect_if_loggedin(iscsi); } } if (revents & POLLOUT) { if (iscsi_write_to_socket(iscsi) != 0) { return iscsi_service_reconnect_if_loggedin(iscsi); } } iscsi_timeout_scan(iscsi); return 0; }
static int iscsi_write_to_socket(struct iscsi_context *iscsi) { ssize_t count; size_t total; struct iscsi_pdu *pdu; static char padding_buf[3]; int socket_flags = 0; #ifdef MSG_NOSIGNAL socket_flags |= MSG_NOSIGNAL; #elif SO_NOSIGPIPE socket_flags |= SO_NOSIGPIPE; #endif if (iscsi->fd == -1) { iscsi_set_error(iscsi, "trying to write but not connected"); return -1; } while (iscsi->outqueue != NULL || iscsi->outqueue_current != NULL) { if (iscsi->outqueue_current == NULL) { if (iscsi->is_corked) { /* connection is corked we are not allowed to send * additional PDUs */ ISCSI_LOG(iscsi, 6, "iscsi_write_to_socket: socket is corked"); return 0; } if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) > 0 && !(iscsi->outqueue->outdata.data[0] & ISCSI_PDU_IMMEDIATE)) { /* stop sending for non-immediate PDUs. maxcmdsn is reached */ ISCSI_LOG(iscsi, 6, "iscsi_write_to_socket: maxcmdsn reached (outqueue[0]->cmdsnd %08x > maxcmdsn %08x)", iscsi->outqueue->cmdsn, iscsi->maxcmdsn); return 0; } /* pop first element of the outqueue */ if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->expcmdsn) < 0 && (iscsi->outqueue->outdata.data[0] & 0x3f) != ISCSI_PDU_DATA_OUT) { iscsi_set_error(iscsi, "iscsi_write_to_socket: outqueue[0]->cmdsn < expcmdsn (%08x < %08x) opcode %02x", iscsi->outqueue->cmdsn, iscsi->expcmdsn, iscsi->outqueue->outdata.data[0] & 0x3f); return -1; } iscsi->outqueue_current = iscsi->outqueue; /* set exp statsn */ iscsi_pdu_set_expstatsn(iscsi->outqueue_current, iscsi->statsn + 1); ISCSI_LIST_REMOVE(&iscsi->outqueue, iscsi->outqueue_current); if (!(iscsi->outqueue_current->flags & ISCSI_PDU_DELETE_WHEN_SENT)) { /* we have to add the pdu to the waitqueue already here since the storage might sent a R2T as soon as it has received the header. if we sent immediate data in a cmd PDU the R2T might get lost otherwise. */ ISCSI_LIST_ADD_END(&iscsi->waitpdu, iscsi->outqueue_current); } } pdu = iscsi->outqueue_current; pdu->outdata.size = (pdu->outdata.size + 3) & 0xfffffffc; /* Write header and any immediate data */ if (pdu->outdata_written < pdu->outdata.size) { count = send(iscsi->fd, pdu->outdata.data + pdu->outdata_written, pdu->outdata.size - pdu->outdata_written, socket_flags); if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } iscsi_set_error(iscsi, "Error when writing to " "socket :%d", errno); return -1; } pdu->outdata_written += count; } /* if we havent written the full header yet. */ if (pdu->outdata_written != pdu->outdata.size) { return 0; } /* Write any iovectors that might have been passed to us */ while (pdu->payload_written < pdu->payload_len) { struct scsi_iovector* iovector_out; iovector_out = iscsi_get_scsi_task_iovector_out(iscsi, pdu); if (iovector_out == NULL) { iscsi_set_error(iscsi, "Can't find iovector data for DATA-OUT"); return -1; } count = iscsi_iovector_readv_writev(iscsi, iovector_out, pdu->payload_offset + pdu->payload_written, pdu->payload_len - pdu->payload_written, 1); if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } iscsi_set_error(iscsi, "Error when writing to " "socket :%d %s", errno, iscsi_get_error(iscsi)); return -1; } pdu->payload_written += count; } total = pdu->payload_len; total = (total + 3) & 0xfffffffc; /* Write padding */ if (pdu->payload_written < total) { count = send(iscsi->fd, padding_buf, total - pdu->payload_written, socket_flags); if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } iscsi_set_error(iscsi, "Error when writing to " "socket :%d", errno); return -1; } pdu->payload_written += count; } /* if we havent written the full padding yet. */ if (pdu->payload_written != total) { return 0; } if (pdu->flags & ISCSI_PDU_CORK_WHEN_SENT) { iscsi->is_corked = 1; } if (pdu->flags & ISCSI_PDU_DELETE_WHEN_SENT) { iscsi->t->free_pdu(iscsi, pdu); } iscsi->outqueue_current = NULL; } return 0; }
static int iscsi_read_from_socket(struct iscsi_context *iscsi) { struct iscsi_in_pdu *in; ssize_t data_size, count, padding_size; if (iscsi->incoming == NULL) { iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu)); iscsi->incoming->hdr = iscsi_szmalloc(iscsi, ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE); if (iscsi->incoming == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu"); return -1; } } in = iscsi->incoming; /* first we must read the header, including any digests */ if (in->hdr_pos < ISCSI_HEADER_SIZE) { /* try to only read the header, the socket is nonblocking, so * no need to limit the read to what is available in the socket */ count = ISCSI_HEADER_SIZE - in->hdr_pos; count = recv(iscsi->fd, &in->hdr[in->hdr_pos], count, 0); if (count == 0) { return -1; } if (count < 0) { if (errno == EINTR || errno == EAGAIN) { return 0; } iscsi_set_error(iscsi, "read from socket failed, " "errno:%d", errno); return -1; } in->hdr_pos += count; } if (in->hdr_pos < ISCSI_HEADER_SIZE) { /* we don't have the full header yet, so return */ return 0; } padding_size = iscsi_get_pdu_padding_size(&in->hdr[0]); data_size = iscsi_get_pdu_data_size(&in->hdr[0]) + padding_size; if (data_size < 0 || data_size > (ssize_t)iscsi->initiator_max_recv_data_segment_length) { iscsi_set_error(iscsi, "Invalid data size received from target (%d)", (int)data_size); return -1; } if (data_size != 0) { unsigned char padding_buf[3]; unsigned char *buf = padding_buf; struct scsi_iovector * iovector_in; count = data_size - in->data_pos; /* first try to see if we already have a user buffer */ iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in); if (iovector_in != NULL && count > padding_size) { uint32_t offset = scsi_get_uint32(&in->hdr[40]); count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, 0); } else { if (iovector_in == NULL) { if (in->data == NULL) { in->data = iscsi_malloc(iscsi, data_size); if (in->data == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size); return -1; } } buf = &in->data[in->data_pos]; } count = recv(iscsi->fd, buf, count, 0); } if (count == 0) { return -1; } if (count < 0) { if (errno == EINTR || errno == EAGAIN) { return 0; } iscsi_set_error(iscsi, "read from socket failed, " "errno:%d %s", errno, iscsi_get_error(iscsi)); return -1; } in->data_pos += count; } if (in->data_pos < data_size) { return 0; } ISCSI_LIST_ADD_END(&iscsi->inqueue, in); iscsi->incoming = NULL; while (iscsi->inqueue != NULL) { struct iscsi_in_pdu *current = iscsi->inqueue; if (iscsi_process_pdu(iscsi, current) != 0) { return -1; } ISCSI_LIST_REMOVE(&iscsi->inqueue, current); iscsi_free_iscsi_in_pdu(iscsi, current); } return 0; }
ssize_t iscsi_iovector_readv_writev(struct iscsi_context *iscsi, struct scsi_iovector *iovector, uint32_t pos, ssize_t count, int do_write) { struct scsi_iovec *iov, *iov2; int niov; uint32_t len2; size_t _len2; ssize_t n; if (iovector->iov == NULL) { errno = EINVAL; return -1; } if (pos < iovector->offset) { iscsi_set_error(iscsi, "iovector reset. pos is smaller than" "current offset"); errno = EINVAL; return -1; } if (iovector->niov <= iovector->consumed) { /* someone issued a read/write but did not provide enough user buffers for all the data. * maybe someone tried to read just 512 bytes off a MMC device? */ errno = EINVAL; return -1; } /* iov is a pointer to the first iovec to pass */ iov = &iovector->iov[iovector->consumed]; pos -= iovector->offset; /* forward until iov points to the first iov to pass */ while (pos >= iov->iov_len) { iovector->offset += iov->iov_len; iovector->consumed++; pos -= iov->iov_len; if (iovector->niov <= iovector->consumed) { errno = EINVAL; return -1; } iov = &iovector->iov[iovector->consumed]; } iov2 = iov; /* iov2 is a pointer to the last iovec to pass */ niov = 1; /* number of iovectors to pass */ len2 = pos + count; /* adjust length of iov2 */ /* forward until iov2 points to the last iovec we pass later. it might happen that we have a lot of iovectors but are limited by count */ while (len2 > iov2->iov_len) { niov++; if (iovector->niov < iovector->consumed + niov) { errno = EINVAL; return -1; } len2 -= iov2->iov_len; iov2 = &iovector->iov[iovector->consumed + niov - 1]; } /* we might limit the length of the last iovec we pass to readv/writev store its orignal length to restore it later */ _len2 = iov2->iov_len; /* adjust base+len of start iovec and len of last iovec */ iov2->iov_len = len2; iov->iov_base = (void*) ((uintptr_t)iov->iov_base + pos); iov->iov_len -= pos; if (do_write) { n = writev(iscsi->fd, (struct iovec*) iov, niov); } else { n = readv(iscsi->fd, (struct iovec*) iov, niov); } /* restore original values */ iov->iov_base = (void*) ((uintptr_t)iov->iov_base - pos); iov->iov_len += pos; iov2->iov_len = _len2; if (n > count) { /* we read/write more bytes than expected, this MUST not happen */ errno = EINVAL; return -1; } return n; }
int iscsi_service(struct iscsi_context *iscsi, int revents) { if (revents & POLLERR) { int err = 0; socklen_t err_size = sizeof(err); if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, &err, &err_size) != 0 || err != 0) { if (err == 0) { err = errno; } iscsi_set_error(iscsi, "iscsi_service: socket error " "%s(%d).", strerror(err), err); } else { iscsi_set_error(iscsi, "iscsi_service: POLLERR, " "Unknown socket error."); } iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); return iscsi_service_reconnect_if_loggedin(iscsi); } if (revents & POLLHUP) { iscsi_set_error(iscsi, "iscsi_service: POLLHUP, " "socket error."); iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); return iscsi_service_reconnect_if_loggedin(iscsi); } if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) { int err = 0; socklen_t err_size = sizeof(err); if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, &err, &err_size) != 0 || err != 0) { if (err == 0) { err = errno; } iscsi_set_error(iscsi, "iscsi_service: socket error " "%s(%d) while connecting.", strerror(err), err); iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); return iscsi_service_reconnect_if_loggedin(iscsi); } DPRINTF(iscsi,2,"connection to %s established",iscsi->connected_portal); iscsi->is_connected = 1; iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL, iscsi->connect_data); return 0; } if (revents & POLLOUT && iscsi->outqueue != NULL) { if (iscsi_write_to_socket(iscsi) != 0) { return iscsi_service_reconnect_if_loggedin(iscsi); } } if (revents & POLLIN) { if (iscsi_read_from_socket(iscsi) != 0) { return iscsi_service_reconnect_if_loggedin(iscsi); } } return 0; }
int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi_command_cb cb, void *private_data) { int port = 3260; char *str; char *addr, *host; struct addrinfo *ai = NULL; union socket_address sa; int socksize; ISCSI_LOG(iscsi, 2, "connecting to portal %s",portal); if (iscsi->fd != -1) { iscsi_set_error(iscsi, "Trying to connect but already connected."); return -1; } addr = iscsi_strdup(iscsi, portal); if (addr == NULL) { iscsi_set_error(iscsi, "Out-of-memory: " "Failed to strdup portal address."); return -1; } host = addr; /* check if we have a target portal group tag */ str = strrchr(host, ','); if (str != NULL) { str[0] = 0; } str = strrchr(host, ':'); if (str != NULL) { if (strchr(str, ']') == NULL) { if (str != NULL) { port = atoi(str+1); str[0] = 0; } } } /* ipv6 in [...] form ? */ if (host[0] == '[') { host ++; str = strchr(host, ']'); if (str == NULL) { iscsi_free(iscsi, addr); iscsi_set_error(iscsi, "Invalid target:%s " "Missing ']' in IPv6 address", portal); return -1; } *str = 0; } /* is it a hostname ? */ if (getaddrinfo(host, NULL, NULL, &ai) != 0) { iscsi_free(iscsi, addr); iscsi_set_error(iscsi, "Invalid target:%s " "Can not resolv into IPv4/v6.", portal); return -1; } iscsi_free(iscsi, addr); memset(&sa, 0, sizeof(sa)); switch (ai->ai_family) { case AF_INET: socksize = sizeof(struct sockaddr_in); memcpy(&sa.sin, ai->ai_addr, socksize); sa.sin.sin_port = htons(port); #ifdef HAVE_SOCK_SIN_LEN sa.sin.sin_len = socksize; #endif break; #ifdef HAVE_SOCKADDR_IN6 case AF_INET6: socksize = sizeof(struct sockaddr_in6); memcpy(&sa.sin6, ai->ai_addr, socksize); sa.sin6.sin6_port = htons(port); #ifdef HAVE_SOCK_SIN_LEN sa.sin6.sin6_len = socksize; #endif break; #endif default: iscsi_set_error(iscsi, "Unknown address family :%d. " "Only IPv4/IPv6 supported so far.", ai->ai_family); freeaddrinfo(ai); return -1; } iscsi->socket_status_cb = cb; iscsi->connect_data = private_data; if (iscsi->t->connect(iscsi, &sa, ai->ai_family) < 0) { iscsi_set_error(iscsi, "Couldn't connect transport"); freeaddrinfo(ai); return -1; } freeaddrinfo(ai); return 0; }
static int iscsi_read_from_socket(struct iscsi_context *iscsi) { struct iscsi_in_pdu *in; ssize_t data_size, count; if (iscsi->incoming == NULL) { iscsi->incoming = malloc(sizeof(struct iscsi_in_pdu)); if (iscsi->incoming == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu"); return -1; } memset(iscsi->incoming, 0, sizeof(struct iscsi_in_pdu)); } in = iscsi->incoming; /* first we must read the header, including any digests */ if (in->hdr_pos < ISCSI_HEADER_SIZE) { /* try to only read the header, the socket is nonblocking, so * no need to limit the read to what is available in the socket */ count = ISCSI_HEADER_SIZE - in->hdr_pos; count = recv(iscsi->fd, &in->hdr[in->hdr_pos], count, 0); if (count == 0) { return -1; } if (count < 0) { if (errno == EINTR || errno == EAGAIN) { return 0; } iscsi_set_error(iscsi, "read from socket failed, " "errno:%d", errno); return -1; } in->hdr_pos += count; } if (in->hdr_pos < ISCSI_HEADER_SIZE) { /* we dont have the full header yet, so return */ return 0; } data_size = iscsi_get_pdu_data_size(&in->hdr[0]); if (data_size < 0 || data_size > iscsi->initiator_max_recv_data_segment_length) { iscsi_set_error(iscsi, "Invalid data size received from target (%d)", (int)data_size); return -1; } if (data_size != 0) { unsigned char *buf = NULL; count = data_size - in->data_pos; /* first try to see if we already have a user buffer */ buf = iscsi_get_user_in_buffer(iscsi, in, in->data_pos, &count); /* if not, allocate one */ if (buf == NULL) { if (in->data == NULL) { in->data = malloc(data_size); if (in->data == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size); return -1; } } buf = &in->data[in->data_pos]; } count = recv(iscsi->fd, buf, count, 0); if (count == 0) { return -1; } if (count < 0) { if (errno == EINTR || errno == EAGAIN) { return 0; } iscsi_set_error(iscsi, "read from socket failed, " "errno:%d", errno); return -1; } in->data_pos += count; } if (in->data_pos < data_size) { return 0; } SLIST_ADD_END(&iscsi->inqueue, in); iscsi->incoming = NULL; while (iscsi->inqueue != NULL) { struct iscsi_in_pdu *current = iscsi->inqueue; if (iscsi_process_pdu(iscsi, current) != 0) { return -1; } SLIST_REMOVE(&iscsi->inqueue, current); iscsi_free_iscsi_in_pdu(current); } return 0; }
int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t itt; enum iscsi_opcode opcode; struct iscsi_pdu *pdu; uint8_t ahslen; opcode = in->hdr[0] & 0x3f; ahslen = in->hdr[4]; itt = ntohl(*(uint32_t *)&in->hdr[16]); if (ahslen != 0) { iscsi_set_error(iscsi, "cant handle expanded headers yet"); return -1; } if (opcode == ISCSI_PDU_REJECT) { iscsi_set_error(iscsi, "Request was rejected with reason: 0x%02x (%s)", in->hdr[2], iscsi_reject_reason_str(in->hdr[2])); if (iscsi_process_reject(iscsi, in) != 0) { return -1; } return 0; } if (opcode == ISCSI_PDU_NOP_IN && itt == 0xffffffff) { if (iscsi_process_target_nop_in(iscsi, in) != 0) { return -1; } return 0; } for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { enum iscsi_opcode expected_response = pdu->response_opcode; int is_finished = 1; if (pdu->itt != itt) { continue; } /* we have a special case with scsi-command opcodes, * they are replied to by either a scsi-response * or a data-in, or a combination of both. */ if (opcode == ISCSI_PDU_DATA_IN && expected_response == ISCSI_PDU_SCSI_RESPONSE) { expected_response = ISCSI_PDU_DATA_IN; } /* Another special case is if we get a R2T. * In this case we should find the original request and just send an additional * DATAOUT segment for this task. */ if (opcode == ISCSI_PDU_R2T) { expected_response = ISCSI_PDU_R2T; } if (opcode != expected_response) { iscsi_set_error(iscsi, "Got wrong opcode back for " "itt:%d got:%d expected %d", itt, opcode, pdu->response_opcode); return -1; } switch (opcode) { case ISCSI_PDU_LOGIN_RESPONSE: if (iscsi_process_login_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi login reply " "failed"); return -1; } break; case ISCSI_PDU_TEXT_RESPONSE: if (iscsi_process_text_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi text reply " "failed"); return -1; } break; case ISCSI_PDU_LOGOUT_RESPONSE: if (iscsi_process_logout_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi logout reply " "failed"); return -1; } break; case ISCSI_PDU_SCSI_RESPONSE: if (iscsi_process_scsi_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi response reply " "failed"); return -1; } break; case ISCSI_PDU_DATA_IN: if (iscsi_process_scsi_data_in(iscsi, pdu, in, &is_finished) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi data in " "failed"); return -1; } break; case ISCSI_PDU_NOP_IN: if (iscsi_process_nop_out_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi nop-in failed"); return -1; } break; case ISCSI_PDU_SCSI_TASK_MANAGEMENT_RESPONSE: if (iscsi_process_task_mgmt_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi task-mgmt failed"); return -1; } break; case ISCSI_PDU_R2T: if (iscsi_process_r2t(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi r2t " "failed"); return -1; } is_finished = 0; break; default: iscsi_set_error(iscsi, "Dont know how to handle " "opcode 0x%02x", opcode); return -1; } if (is_finished) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); } return 0; } return 0; }
int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { uint32_t status, maxcmdsn, expcmdsn; char *ptr = (char *)in->data; int size = in->data_pos; status = scsi_get_uint16(&in->hdr[36]); iscsi->statsn = scsi_get_uint16(&in->hdr[24]); maxcmdsn = scsi_get_uint32(&in->hdr[32]); if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { iscsi->maxcmdsn = maxcmdsn; } expcmdsn = scsi_get_uint32(&in->hdr[28]); if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { iscsi->expcmdsn = expcmdsn; } /* XXX here we should parse the data returned in case the target * renegotiated some some parameters. * we should also do proper handshaking if the target is not yet * prepared to transition to the next stage */ while (size > 0) { char *end; int len; end = memchr(ptr, 0, size); if (end == NULL) { iscsi_set_error(iscsi, "NUL not found after offset %ld " "when parsing login data", (long)((unsigned char *)ptr - in->data)); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); return -1; } len = end - ptr; if (len == 0) { break; } /* parse the strings */ if (!strncmp(ptr, "TargetAddress=", 14)) { strncpy(iscsi->target_address,ptr+14,MAX_STRING_SIZE); } if (!strncmp(ptr, "HeaderDigest=", 13)) { if (!strcmp(ptr + 13, "CRC32C")) { iscsi->want_header_digest = ISCSI_HEADER_DIGEST_CRC32C; } else { iscsi->want_header_digest = ISCSI_HEADER_DIGEST_NONE; } } if (!strncmp(ptr, "FirstBurstLength=", 17)) { iscsi->first_burst_length = strtol(ptr + 17, NULL, 10); } if (!strncmp(ptr, "InitialR2T=", 11)) { if (!strcmp(ptr + 11, "No")) { iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_NO; } else { iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_YES; } } if (!strncmp(ptr, "ImmediateData=", 14)) { if (!strcmp(ptr + 14, "No")) { iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; } else if (iscsi->want_immediate_data == ISCSI_IMMEDIATE_DATA_NO) { /* If we negotiated NO, it doesnt matter what * the target said. ImmediateData is NO. */ iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; } else { iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES; } } if (!strncmp(ptr, "MaxBurstLength=", 15)) { iscsi->max_burst_length = strtol(ptr + 15, NULL, 10); } if (!strncmp(ptr, "MaxRecvDataSegmentLength=", 25)) { iscsi->target_max_recv_data_segment_length = strtol(ptr + 25, NULL, 10); } if (!strncmp(ptr, "AuthMethod=", 11)) { if (!strcmp(ptr + 11, "CHAP")) { iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM; } } if (!strncmp(ptr, "CHAP_A=", 7)) { iscsi->chap_a = atoi(ptr+7); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } if (!strncmp(ptr, "CHAP_I=", 7)) { iscsi->chap_i = atoi(ptr+7); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } if (!strncmp(ptr, "CHAP_C=0x", 9)) { strncpy(iscsi->chap_c,ptr+9,MAX_STRING_SIZE); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } ptr += len + 1; size -= len + 1; } if (status == SCSI_STATUS_REDIRECT && iscsi->target_address[0]) { ISCSI_LOG(iscsi, 2, "target requests redirect to %s",iscsi->target_address); pdu->callback(iscsi, SCSI_STATUS_REDIRECT, NULL, pdu->private_data); return 0; } if (status != 0) { iscsi_set_error(iscsi, "Failed to log in to target. Status: %s(%d)", login_error_str(status), status); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); return 0; } if (in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) { iscsi->current_phase = (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) << 2; } if ((in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) && (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) == ISCSI_PDU_LOGIN_NSG_FF) { iscsi->is_loggedin = 1; iscsi_itt_post_increment(iscsi); iscsi->header_digest = iscsi->want_header_digest; ISCSI_LOG(iscsi, 2, "login successful"); pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); } else { if (iscsi_login_async(iscsi, pdu->callback, pdu->private_data) != 0) { iscsi_set_error(iscsi, "Failed to send continuation login pdu"); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } } return 0; }
int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data) { struct iscsi_pdu *pdu; int transit; if (iscsi->login_attempts++ > 10) { iscsi_set_error(iscsi, "login took too many tries." " giving up."); return -1; } if (iscsi->is_loggedin != 0) { iscsi_set_error(iscsi, "Trying to login while already logged " "in."); return -1; } switch (iscsi->session_type) { case ISCSI_SESSION_DISCOVERY: case ISCSI_SESSION_NORMAL: break; default: iscsi_set_error(iscsi, "trying to login without setting " "session type."); return -1; } pdu = iscsi_allocate_pdu_with_itt_flags(iscsi, ISCSI_PDU_LOGIN_REQUEST, ISCSI_PDU_LOGIN_RESPONSE, iscsi->itt, 0); if (pdu == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate " "login pdu."); return -1; } /* login request */ iscsi_pdu_set_immediate(pdu); if (!iscsi->user[0]) { iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_OPNEG; } if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) { iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG; } if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) { iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_FF; } transit = 0; if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) { transit = ISCSI_PDU_LOGIN_TRANSIT; } if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) { if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { transit = ISCSI_PDU_LOGIN_TRANSIT; } if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { transit = ISCSI_PDU_LOGIN_TRANSIT; } } /* flags */ iscsi_pdu_set_pduflags(pdu, transit | iscsi->current_phase | iscsi->next_phase); /* initiator name */ if (iscsi_login_add_initiatorname(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* optional alias */ if (iscsi->alias[0]) { if (iscsi_login_add_alias(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } } /* target name */ if (iscsi->session_type == ISCSI_SESSION_NORMAL) { if (iscsi_login_add_targetname(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } } /* session type */ if (iscsi_login_add_sessiontype(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* header digest */ if (iscsi_login_add_headerdigest(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* auth method */ if (iscsi_login_add_authmethod(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* auth algorithm */ if (iscsi_login_add_authalgorithm(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* chap username */ if (iscsi_login_add_chap_username(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* chap response */ if (iscsi_login_add_chap_response(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* data digest */ if (iscsi_login_add_datadigest(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* initial r2t */ if (iscsi_login_add_initialr2t(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* immediate data */ if (iscsi_login_add_immediatedata(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* max burst length */ if (iscsi_login_add_maxburstlength(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* first burst length */ if (iscsi_login_add_firstburstlength(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* default time 2 wait */ if (iscsi_login_add_defaulttime2wait(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* default time 2 retain */ if (iscsi_login_add_defaulttime2retain(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* max outstanding r2t */ if (iscsi_login_add_maxoutstandingr2t(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* errorrecoverylevel */ if (iscsi_login_add_errorrecoverylevel(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* ifmarker */ if (iscsi_login_add_ifmarker(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* ofmarker */ if (iscsi_login_add_ofmarker(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* maxconnections */ if (iscsi_login_add_maxconnections(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* max recv data segment length */ if (iscsi_login_add_maxrecvdatasegmentlength(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* data pdu in order */ if (iscsi_login_add_datapduinorder(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* data sequence in order */ if (iscsi_login_add_datasequenceinorder(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } pdu->callback = cb; pdu->private_data = private_data; if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " "pdu."); iscsi_free_pdu(iscsi, pdu); return -1; } return 0; }
static int iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; char * strp; unsigned char c, cc[2]; unsigned char digest[16]; gcry_md_hd_t ctx; int i; if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { return 0; } gcry_md_open(&ctx, GCRY_MD_MD5, 0); if (!ctx) { iscsi_set_error(iscsi, "Cannot create MD5 algorithm"); return -1; } if (!iscsi->chap_c[0]) { iscsi_set_error(iscsi, "No CHAP challenge found"); return -1; } gcry_md_putc(ctx, iscsi->chap_i); gcry_md_write(ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd)); strp = iscsi->chap_c; while (*strp != 0) { c = (h2i(strp[0]) << 4) | h2i(strp[1]); strp += 2; gcry_md_putc(ctx, c); } memcpy(digest, gcry_md_read(ctx, 0), sizeof(digest)); gcry_md_close(ctx); strncpy(str,"CHAP_R=0x",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } for (i=0; i<16; i++) { c = digest[i]; cc[0] = i2h((c >> 4)&0x0f); cc[1] = i2h((c )&0x0f); if (iscsi_pdu_add_data(iscsi, pdu, &cc[0], 2) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data " "failed."); return -1; } } c = 0; if (iscsi_pdu_add_data(iscsi, pdu, &c, 1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data " "failed."); return -1; } return 0; }
struct iscsi_context * iscsi_create_context(const char *initiator_name) { struct iscsi_context *iscsi; size_t required = ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE; char *ca; if (!initiator_name[0]) { return NULL; } iscsi = malloc(sizeof(struct iscsi_context)); if (iscsi == NULL) { return NULL; } memset(iscsi, 0, sizeof(struct iscsi_context)); /* initalize transport of context */ if (iscsi_init_transport(iscsi, TCP_TRANSPORT)) { iscsi_set_error(iscsi, "Failed allocating transport"); return NULL; } strncpy(iscsi->initiator_name,initiator_name,MAX_STRING_SIZE); iscsi->fd = -1; srand(time(NULL) ^ getpid() ^ (uint32_t) ((uintptr_t) iscsi)); /* initialize to a "random" isid */ iscsi_set_isid_random(iscsi, rand(), 0); /* assume we start in security negotiation phase */ iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_SECNEG; iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG; iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP; iscsi->max_burst_length = 262144; iscsi->first_burst_length = 262144; iscsi->initiator_max_recv_data_segment_length = 262144; iscsi->target_max_recv_data_segment_length = 8192; iscsi->want_initial_r2t = ISCSI_INITIAL_R2T_NO; iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_YES; iscsi->want_immediate_data = ISCSI_IMMEDIATE_DATA_YES; iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES; iscsi->want_header_digest = ISCSI_HEADER_DIGEST_NONE_CRC32C; iscsi->tcp_keepcnt=3; iscsi->tcp_keepintvl=30; iscsi->tcp_keepidle=30; iscsi->reconnect_max_retries = -1; if (getenv("LIBISCSI_DEBUG") != NULL) { iscsi_set_log_level(iscsi, atoi(getenv("LIBISCSI_DEBUG"))); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } if (getenv("LIBISCSI_TCP_USER_TIMEOUT") != NULL) { iscsi_set_tcp_user_timeout(iscsi,atoi(getenv("LIBISCSI_TCP_USER_TIMEOUT"))); } if (getenv("LIBISCSI_TCP_KEEPCNT") != NULL) { iscsi_set_tcp_keepcnt(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPCNT"))); } if (getenv("LIBISCSI_TCP_KEEPINTVL") != NULL) { iscsi_set_tcp_keepintvl(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPINTVL"))); } if (getenv("LIBISCSI_TCP_KEEPIDLE") != NULL) { iscsi_set_tcp_keepidle(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPIDLE"))); } if (getenv("LIBISCSI_TCP_SYNCNT") != NULL) { iscsi_set_tcp_syncnt(iscsi,atoi(getenv("LIBISCSI_TCP_SYNCNT"))); } if (getenv("LIBISCSI_BIND_INTERFACES") != NULL) { iscsi_set_bind_interfaces(iscsi,getenv("LIBISCSI_BIND_INTERFACES")); } /* iscsi->smalloc_size is the size for small allocations. this should be max(ISCSI_HEADER_SIZE, sizeof(struct iscsi_pdu), sizeof(struct iscsi_in_pdu)) rounded up to the next power of 2. */ required = MAX(required, sizeof(struct iscsi_pdu)); required = MAX(required, sizeof(struct iscsi_in_pdu)); iscsi->smalloc_size = 1; while (iscsi->smalloc_size < required) { iscsi->smalloc_size <<= 1; } ISCSI_LOG(iscsi,5,"small allocation size is %d byte", iscsi->smalloc_size); ca = getenv("LIBISCSI_CACHE_ALLOCATIONS"); if (!ca || atoi(ca) != 0) { iscsi->cache_allocations = 1; } return iscsi; }
int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data) { struct iscsi_pdu *pdu; if (iscsi->old_iscsi || iscsi->pending_reconnect) { ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6, "NOP Out Send NOT SEND while reconnecting (nops_in_flight: %d, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x)", iscsi->nops_in_flight, iscsi->maxcmdsn, iscsi->expcmdsn); return 0; } if (iscsi->is_loggedin == 0) { iscsi_set_error(iscsi, "trying to send nop-out while not " "logged in"); return -1; } pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NOP_IN, iscsi_itt_post_increment(iscsi), ISCSI_PDU_DROP_ON_RECONNECT); if (pdu == NULL) { iscsi_set_error(iscsi, "Failed to allocate nop-out pdu"); return -1; } /* flags */ iscsi_pdu_set_pduflags(pdu, 0x80); /* ttt */ iscsi_pdu_set_ttt(pdu, 0xffffffff); /* lun */ iscsi_pdu_set_lun(pdu, 0); /* cmdsn */ iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn++); pdu->callback = cb; pdu->private_data = private_data; if (data != NULL && len > 0) { if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) { iscsi_set_error(iscsi, "Failed to add outdata to nop-out"); iscsi->drv->free_pdu(iscsi, pdu); return -1; } } if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "failed to queue iscsi nop-out pdu"); iscsi->drv->free_pdu(iscsi, pdu); return -1; } iscsi->nops_in_flight++; ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6, "NOP Out Send (nops_in_flight: %d, pdu->cmdsn %08x, pdu->itt %08x, pdu->ttt %08x, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x)", iscsi->nops_in_flight, pdu->cmdsn, pdu->itt, 0xffffffff, iscsi->maxcmdsn, iscsi->expcmdsn); return 0; }
int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { struct iscsi_discovery_address *targets = NULL; unsigned char *ptr = in->data; int size = in->data_pos; /* verify the response looks sane */ if (in->hdr[1] != ISCSI_PDU_TEXT_FINAL) { iscsi_set_error(iscsi, "unsupported flags in text " "reply %02x", in->hdr[1]); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); return -1; } while (size > 0) { int len; len = strlen((char *)ptr); if (len == 0) { break; } if (len > size) { iscsi_set_error(iscsi, "len > size when parsing " "discovery data %d>%d", len, size); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); iscsi_free_discovery_addresses(targets); return -1; } /* parse the strings */ if (!strncmp((char *)ptr, "TargetName=", 11)) { struct iscsi_discovery_address *target; target = malloc(sizeof(struct iscsi_discovery_address)); if (target == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target"); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); iscsi_free_discovery_addresses(targets); return -1; } bzero(target, sizeof(struct iscsi_discovery_address)); target->target_name = strdup((char *)ptr+11); if (target->target_name == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target name"); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); free(target); target = NULL; iscsi_free_discovery_addresses(targets); return -1; } target->next = targets; targets = target; } else if (!strncmp((char *)ptr, "TargetAddress=", 14)) { targets->target_address = strdup((char *)ptr+14); if (targets->target_address == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target address"); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); iscsi_free_discovery_addresses(targets); return -1; } } else { iscsi_set_error(iscsi, "Dont know how to handle " "discovery string : %s", ptr); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); iscsi_free_discovery_addresses(targets); return -1; } ptr += len + 1; size -= len + 1; } pdu->callback(iscsi, SCSI_STATUS_GOOD, targets, pdu->private_data); iscsi_free_discovery_addresses(targets); return 0; }
static int iscsi_tcp_connect(struct iscsi_context *iscsi, union socket_address *sa, int ai_family) { int socksize; iscsi->fd = socket(ai_family, SOCK_STREAM, 0); if (iscsi->fd == -1) { iscsi_set_error(iscsi, "Failed to open iscsi socket. " "Errno:%s(%d).", strerror(errno), errno); return -1; } if (iscsi->old_iscsi && iscsi->fd != iscsi->old_iscsi->fd) { if (dup2(iscsi->fd, iscsi->old_iscsi->fd) == -1) { return -1; } close(iscsi->fd); iscsi->fd = iscsi->old_iscsi->fd; } set_nonblocking(iscsi->fd); iscsi_set_tcp_keepalive(iscsi, iscsi->tcp_keepidle, iscsi->tcp_keepcnt, iscsi->tcp_keepintvl); if (iscsi->tcp_user_timeout > 0) { set_tcp_user_timeout(iscsi); } if (iscsi->tcp_syncnt > 0) { set_tcp_syncnt(iscsi); } #if __linux if (iscsi->bind_interfaces[0]) { char *pchr = iscsi->bind_interfaces, *pchr2; int iface_n = iface_rr++%iscsi->bind_interfaces_cnt; int iface_c = 0; do { pchr2 = strchr(pchr,','); if (iface_c == iface_n) { if (pchr2) pchr2[0]=0x00; break; } if (pchr2) {pchr=pchr2+1;} iface_c++; } while (pchr2); int res = setsockopt(iscsi->fd, SOL_SOCKET, SO_BINDTODEVICE, pchr, strlen(pchr)); if (res < 0) { ISCSI_LOG(iscsi,1,"failed to bind to interface '%s': %s",pchr,strerror(errno)); } else { ISCSI_LOG(iscsi,3,"successfully bound to interface '%s'",pchr); } if (pchr2) pchr2[0]=','; } #endif if (set_tcp_sockopt(iscsi->fd, TCP_NODELAY, 1) != 0) { ISCSI_LOG(iscsi,1,"failed to set TCP_NODELAY sockopt: %s",strerror(errno)); } else { ISCSI_LOG(iscsi,3,"TCP_NODELAY set to 1"); } socksize = sizeof(struct sockaddr_in); // Work-around for now, need to fix it if (connect(iscsi->fd, &sa->sa, socksize) != 0 && errno != EINPROGRESS) { iscsi_set_error(iscsi, "Connect failed with errno : " "%s(%d)", strerror(errno), errno); close(iscsi->fd); iscsi->fd = -1; return -1; } return 0; }
int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi_command_cb cb, void *private_data) { int port = 3260; char *str; char *addr, *host; struct addrinfo *ai = NULL; int socksize; DPRINTF(iscsi,2,"connecting to portal %s",portal); if (iscsi->fd != -1) { iscsi_set_error(iscsi, "Trying to connect but already connected."); return -1; } addr = strdup(portal); if (addr == NULL) { iscsi_set_error(iscsi, "Out-of-memory: " "Failed to strdup portal address."); return -1; } host = addr; /* check if we have a target portal group tag */ str = strrchr(host, ','); if (str != NULL) { str[0] = 0; } str = strrchr(host, ':'); if (str != NULL) { if (strchr(str, ']') == NULL) { if (str != NULL) { port = atoi(str+1); str[0] = 0; } } } /* ipv6 in [...] form ? */ if (host[0] == '[') { host ++; str = strchr(host, ']'); if (str == NULL) { free(addr); iscsi_set_error(iscsi, "Invalid target:%s " "Missing ']' in IPv6 address", portal); return -1; } *str = 0; } /* is it a hostname ? */ if (getaddrinfo(host, NULL, NULL, &ai) != 0) { free(addr); iscsi_set_error(iscsi, "Invalid target:%s " "Can not resolv into IPv4/v6.", portal); return -1; } free(addr); switch (ai->ai_family) { case AF_INET: socksize = sizeof(struct sockaddr_in); ((struct sockaddr_in *)(ai->ai_addr))->sin_port = htons(port); #ifdef HAVE_SOCK_SIN_LEN ((struct sockaddr_in *)(ai->ai_addr))->sin_len = socksize; #endif break; case AF_INET6: socksize = sizeof(struct sockaddr_in6); ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_port = htons(port); #ifdef HAVE_SOCK_SIN_LEN ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_len = socksize; #endif break; default: iscsi_set_error(iscsi, "Unknown address family :%d. " "Only IPv4/IPv6 supported so far.", ai->ai_family); freeaddrinfo(ai); return -1; } iscsi->fd = socket(ai->ai_family, SOCK_STREAM, 0); if (iscsi->fd == -1) { freeaddrinfo(ai); iscsi_set_error(iscsi, "Failed to open iscsi socket. " "Errno:%s(%d).", strerror(errno), errno); return -1; } iscsi->socket_status_cb = cb; iscsi->connect_data = private_data; set_nonblocking(iscsi->fd); iscsi_set_tcp_keepalive(iscsi, iscsi->tcp_keepidle, iscsi->tcp_keepcnt, iscsi->tcp_keepintvl); if (iscsi->tcp_user_timeout > 0) { set_tcp_user_timeout(iscsi); } if (iscsi->tcp_syncnt > 0) { set_tcp_syncnt(iscsi); } if (connect(iscsi->fd, ai->ai_addr, socksize) != 0 && errno != EINPROGRESS) { iscsi_set_error(iscsi, "Connect failed with errno : " "%s(%d)", strerror(errno), errno); close(iscsi->fd); iscsi->fd = -1; freeaddrinfo(ai); return -1; } freeaddrinfo(ai); if (iscsi->connected_portal) free(discard_const(iscsi->connected_portal)); iscsi->connected_portal=strdup(portal); return 0; }