diff --git a/iscsi-scst/usr/iscsi_hdr.h b/iscsi-scst/usr/iscsi_hdr.h index 64b559d60..329c85db9 100644 --- a/iscsi-scst/usr/iscsi_hdr.h +++ b/iscsi-scst/usr/iscsi_hdr.h @@ -223,4 +223,34 @@ struct iscsi_logout_rsp_hdr { u32 rsvd5; } __packed; +#define ISCSI_REASON_NO_FULL_FEATURE_PHASE 0x01 +#define ISCSI_REASON_DATA_DIGEST_ERROR 0x02 +#define ISCSI_REASON_DATA_SNACK_REJECT 0x03 +#define ISCSI_REASON_PROTOCOL_ERROR 0x04 +#define ISCSI_REASON_UNSUPPORTED_COMMAND 0x05 +#define ISCSI_REASON_IMMEDIATE_COMMAND_REJECT 0x06 +#define ISCSI_REASON_TASK_IN_PROGRESS 0x07 +#define ISCSI_REASON_INVALID_SNACK 0x08 +#define ISCSI_REASON_INVALID_PDU_FIELD 0x09 +#define ISCSI_REASON_BOOKMARK_REJECT 0x0a +#define ISCSI_REASON_NEGOTIATION_RESET 0x0b +#define ISCSI_REASON_WAITING_LOGOUT 0x0c + +struct iscsi_reject_hdr { + u8 opcode; + u8 flags; + u8 reason; + u8 rsvd1; + u8 ahslength; + u8 datalength[3]; + u32 rsvd2[2]; + u32 ffffffff; + u32 rsvd3; + u32 stat_sn; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 data_sn; + u32 rsvd4[2]; +} __packed; + #endif /* ISCSI_HDR_H */ diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index a60071811..97852034d 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -479,6 +479,35 @@ static void login_finish(struct connection *conn) } } +static void cmnd_reject(struct connection *conn, u8 reason) +{ + struct iscsi_reject_hdr *rej = + (struct iscsi_reject_hdr *)&conn->rsp.bhs; + size_t data_sz = sizeof(struct iscsi_hdr); + struct buf_segment *seg = conn_alloc_buf_segment(conn, data_sz); + + conn_free_rsp_buf_list(conn); + + memset(rej, 0x0, sizeof *rej); + rej->opcode = ISCSI_OP_REJECT_MSG; + rej->reason = ISCSI_REASON_INVALID_PDU_FIELD; + rej->ffffffff = ISCSI_RESERVED_TAG; + rej->flags |= ISCSI_FLG_FINAL; + + rej->stat_sn = cpu_to_be32(conn->stat_sn++); + rej->exp_cmd_sn = cpu_to_be32(conn->exp_cmd_sn); + rej->max_cmd_sn = cpu_to_be32(conn->exp_cmd_sn + 1); + + if (!seg) { + log_error("Failed to alloc data segment for Reject PDU\n"); + return; + } + + memcpy(seg->data, &conn->req.bhs, data_sz); + seg->len = data_sz; + list_add_tail(&seg->entry, &conn->rsp_buf_list); +} + static int cmnd_exec_auth(struct connection *conn) { int res; @@ -507,7 +536,8 @@ static void cmnd_exec_login(struct connection *conn) memset(rsp, 0, BHS_SIZE); if ((req->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_LOGIN_CMD || !(req->opcode & ISCSI_OP_IMMEDIATE)) { - //reject + cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); + return; } rsp->opcode = ISCSI_OP_LOGIN_RSP; @@ -773,13 +803,8 @@ static void cmnd_exec_text(struct connection *conn) "expected %#x; %stext segments queued\n", req->ttt, conn->ttt, list_empty(&conn->rsp_buf_list) ? "no " : ""); - /* - * Return a malformed text rsp and close the conn for now. - * The proper response would be a Reject instead. - */ - conn->ttt = rsp->ttt = ISCSI_RESERVED_TAG; - conn_free_rsp_buf_list(conn); - conn->state = STATE_CLOSE; + cmnd_reject(conn, ISCSI_REASON_INVALID_PDU_FIELD); + return; } if (list_length_is_one(&conn->rsp_buf_list)) @@ -817,22 +842,30 @@ int cmnd_execute(struct connection *conn) switch (conn->req.bhs.opcode & ISCSI_OPCODE_MASK) { case ISCSI_OP_LOGIN_CMD: - //if conn->state == STATE_FULL -> reject + if (conn->state == STATE_FULL) { + cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); + break; + } cmnd_exec_login(conn); login_rsp = (struct iscsi_login_rsp_hdr *) &conn->rsp.bhs; if (login_rsp->status_class) conn_free_rsp_buf_list(conn); break; case ISCSI_OP_TEXT_CMD: - //if conn->state != STATE_FULL -> reject - cmnd_exec_text(conn); + if (conn->state != STATE_FULL) + cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); + else + cmnd_exec_text(conn); break; case ISCSI_OP_LOGOUT_CMD: - //if conn->state != STATE_FULL -> reject + if (conn->state != STATE_FULL) + cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); + else + cmnd_exec_logout(conn); cmnd_exec_logout(conn); break; default: - //reject + cmnd_reject(conn, ISCSI_REASON_UNSUPPORTED_COMMAND); res = 0; goto out; }