2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2012 The FreeBSD Foundation
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/types.h>
32 #include <netinet/in.h>
37 #include <iscsi_proto.h>
38 #include "libiscsiutil.h"
40 /* Construct a new TextRequest PDU. */
42 text_new_request(struct connection *conn, uint32_t ttt)
45 struct iscsi_bhs_text_request *bhstr;
47 request = pdu_new(conn);
48 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
49 bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST |
50 ISCSI_BHS_OPCODE_IMMEDIATE;
51 bhstr->bhstr_flags = BHSTR_FLAGS_FINAL;
52 bhstr->bhstr_initiator_task_tag = 0;
53 bhstr->bhstr_target_transfer_tag = ttt;
55 bhstr->bhstr_cmdsn = conn->conn_cmdsn;
56 bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1);
61 /* Receive a TextRequest PDU from a connection. */
63 text_receive_request(struct connection *conn)
66 struct iscsi_bhs_text_request *bhstr;
68 request = pdu_new(conn);
70 if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
71 ISCSI_BHS_OPCODE_TEXT_REQUEST)
72 log_errx(1, "protocol error: received invalid opcode 0x%x",
73 request->pdu_bhs->bhs_opcode);
74 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
77 * XXX: Implement the C flag some day.
79 if ((bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE)) !=
81 log_errx(1, "received TextRequest PDU with invalid "
82 "flags: %u", bhstr->bhstr_flags);
83 if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) {
84 log_errx(1, "received TextRequest PDU with decreasing CmdSN: "
85 "was %u, is %u", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn));
87 conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn);
88 if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0)
94 /* Construct a new TextResponse PDU in reply to a request. */
96 text_new_response(struct pdu *request, uint32_t ttt, bool final)
99 struct connection *conn;
100 struct iscsi_bhs_text_request *bhstr;
101 struct iscsi_bhs_text_response *bhstr2;
103 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
104 conn = request->pdu_connection;
106 response = pdu_new_response(request);
107 bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs;
108 bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE;
110 bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL;
112 bhstr2->bhstr_flags = BHSTR_FLAGS_CONTINUE;
113 bhstr2->bhstr_lun = bhstr->bhstr_lun;
114 bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag;
115 bhstr2->bhstr_target_transfer_tag = ttt;
116 bhstr2->bhstr_statsn = htonl(conn->conn_statsn++);
117 bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn);
118 bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn);
123 /* Receive a TextResponse PDU from a connection. */
125 text_receive_response(struct connection *conn)
127 struct pdu *response;
128 struct iscsi_bhs_text_response *bhstr;
131 response = pdu_new(conn);
132 pdu_receive(response);
133 if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE)
134 log_errx(1, "protocol error: received invalid opcode 0x%x",
135 response->pdu_bhs->bhs_opcode);
136 bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs;
137 flags = bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE);
139 case BHSTR_FLAGS_CONTINUE:
140 if (bhstr->bhstr_target_transfer_tag == 0xffffffff)
141 log_errx(1, "received continue TextResponse PDU with "
143 bhstr->bhstr_target_transfer_tag);
145 case BHSTR_FLAGS_FINAL:
146 if (bhstr->bhstr_target_transfer_tag != 0xffffffff)
147 log_errx(1, "received final TextResponse PDU with "
149 bhstr->bhstr_target_transfer_tag);
152 log_errx(1, "received TextResponse PDU with invalid "
153 "flags: %u", bhstr->bhstr_flags);
155 if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) {
156 log_errx(1, "received TextResponse PDU with wrong StatSN: "
157 "is %u, should be %u", ntohl(bhstr->bhstr_statsn),
158 conn->conn_statsn + 1);
160 conn->conn_statsn = ntohl(bhstr->bhstr_statsn);
166 * Send a list of keys from the initiator to the target in a
170 text_send_request(struct connection *conn, struct keys *request_keys)
174 request = text_new_request(conn, 0xffffffff);
175 keys_save_pdu(request_keys, request);
176 if (request->pdu_data_len == 0)
177 log_errx(1, "No keys to send in a TextRequest");
178 if (request->pdu_data_len >
179 (size_t)conn->conn_max_send_data_segment_length)
180 log_errx(1, "Keys to send in TextRequest are too long");
187 * Read a list of keys from the target in a series of TextResponse
191 text_read_response(struct connection *conn)
193 struct keys *response_keys;
202 struct pdu *request, *response;
203 struct iscsi_bhs_text_response *bhstr;
205 response = text_receive_response(conn);
206 bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs;
207 if (keys_data == NULL) {
208 ttt = bhstr->bhstr_target_transfer_tag;
209 keys_data = response->pdu_data;
210 keys_len = response->pdu_data_len;
211 response->pdu_data = NULL;
213 keys_data = realloc(keys_data,
214 keys_len + response->pdu_data_len);
215 if (keys_data == NULL)
216 log_err(1, "failed to grow keys block");
217 memcpy(keys_data + keys_len, response->pdu_data,
218 response->pdu_data_len);
219 keys_len += response->pdu_data_len;
221 if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) != 0) {
222 pdu_delete(response);
225 if (bhstr->bhstr_target_transfer_tag != ttt)
226 log_errx(1, "received non-final TextRequest PDU with "
228 bhstr->bhstr_target_transfer_tag);
229 pdu_delete(response);
231 /* Send an empty request. */
232 request = text_new_request(conn, ttt);
237 response_keys = keys_new();
238 keys_load(response_keys, keys_data, keys_len);
240 return (response_keys);
244 * Read a list of keys from the initiator in a TextRequest PDU.
247 text_read_request(struct connection *conn, struct pdu **requestp)
249 struct iscsi_bhs_text_request *bhstr;
251 struct keys *request_keys;
253 request = text_receive_request(conn);
254 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
255 if (bhstr->bhstr_target_transfer_tag != 0xffffffff)
256 log_errx(1, "received TextRequest PDU with invalid TTT 0x%x",
257 bhstr->bhstr_target_transfer_tag);
258 if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) {
259 log_errx(1, "received TextRequest PDU with wrong ExpStatSN: "
260 "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn),
264 request_keys = keys_new();
265 keys_load_pdu(request_keys, request);
267 return (request_keys);
271 * Send a response back to the initiator as a series of TextResponse
275 text_send_response(struct pdu *request, struct keys *response_keys)
277 struct connection *conn = request->pdu_connection;
283 keys_save(response_keys, &keys_data, &keys_len);
287 struct pdu *request2, *response;
288 struct iscsi_bhs_text_request *bhstr;
292 todo = keys_len - keys_offset;
293 if (todo > (size_t)conn->conn_max_send_data_segment_length) {
295 todo = conn->conn_max_send_data_segment_length;
301 response = text_new_response(request, ttt, final);
302 response->pdu_data = keys_data + keys_offset;
303 response->pdu_data_len = todo;
307 response->pdu_data = NULL;
308 pdu_delete(response);
314 * Wait for an empty request.
316 * XXX: Linux's Open-iSCSI initiator doesn't update
317 * ExpStatSN when receiving a TextResponse PDU.
319 request2 = text_receive_request(conn);
320 bhstr = (struct iscsi_bhs_text_request *)request2->pdu_bhs;
321 if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0)
322 log_errx(1, "received continuation TextRequest PDU "
324 if (pdu_data_segment_length(request2) != 0)
325 log_errx(1, "received non-empty continuation "
327 if (bhstr->bhstr_target_transfer_tag != ttt)
328 log_errx(1, "received TextRequest PDU with invalid "
329 "TTT 0x%x", bhstr->bhstr_target_transfer_tag);
330 pdu_delete(request2);