1b4068979SJohn Baldwin /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3b4068979SJohn Baldwin *
4b4068979SJohn Baldwin * Copyright (c) 2012 The FreeBSD Foundation
5b4068979SJohn Baldwin *
6b4068979SJohn Baldwin * This software was developed by Edward Tomasz Napierala under sponsorship
7b4068979SJohn Baldwin * from the FreeBSD Foundation.
8b4068979SJohn Baldwin *
9b4068979SJohn Baldwin * Redistribution and use in source and binary forms, with or without
10b4068979SJohn Baldwin * modification, are permitted provided that the following conditions
11b4068979SJohn Baldwin * are met:
12b4068979SJohn Baldwin * 1. Redistributions of source code must retain the above copyright
13b4068979SJohn Baldwin * notice, this list of conditions and the following disclaimer.
14b4068979SJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright
15b4068979SJohn Baldwin * notice, this list of conditions and the following disclaimer in the
16b4068979SJohn Baldwin * documentation and/or other materials provided with the distribution.
17b4068979SJohn Baldwin *
18b4068979SJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19b4068979SJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20b4068979SJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21b4068979SJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22b4068979SJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23b4068979SJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24b4068979SJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25b4068979SJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26b4068979SJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27b4068979SJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28b4068979SJohn Baldwin * SUCH DAMAGE.
29b4068979SJohn Baldwin */
30b4068979SJohn Baldwin
31b4068979SJohn Baldwin #include <sys/types.h>
32b4068979SJohn Baldwin #include <netinet/in.h>
33b4068979SJohn Baldwin
34b4068979SJohn Baldwin #include <stdlib.h>
35b4068979SJohn Baldwin #include <string.h>
36b4068979SJohn Baldwin
37b4068979SJohn Baldwin #include <iscsi_proto.h>
38b4068979SJohn Baldwin #include "libiscsiutil.h"
39b4068979SJohn Baldwin
40b4068979SJohn Baldwin /* Construct a new TextRequest PDU. */
41b4068979SJohn Baldwin static struct pdu *
text_new_request(struct connection * conn,uint32_t ttt)42b4068979SJohn Baldwin text_new_request(struct connection *conn, uint32_t ttt)
43b4068979SJohn Baldwin {
44b4068979SJohn Baldwin struct pdu *request;
45b4068979SJohn Baldwin struct iscsi_bhs_text_request *bhstr;
46b4068979SJohn Baldwin
47b4068979SJohn Baldwin request = pdu_new(conn);
48b4068979SJohn Baldwin bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
49b4068979SJohn Baldwin bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST |
50b4068979SJohn Baldwin ISCSI_BHS_OPCODE_IMMEDIATE;
51b4068979SJohn Baldwin bhstr->bhstr_flags = BHSTR_FLAGS_FINAL;
52b4068979SJohn Baldwin bhstr->bhstr_initiator_task_tag = 0;
53b4068979SJohn Baldwin bhstr->bhstr_target_transfer_tag = ttt;
54b4068979SJohn Baldwin
55b4068979SJohn Baldwin bhstr->bhstr_cmdsn = conn->conn_cmdsn;
56b4068979SJohn Baldwin bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1);
57b4068979SJohn Baldwin
58b4068979SJohn Baldwin return (request);
59b4068979SJohn Baldwin }
60b4068979SJohn Baldwin
61b4068979SJohn Baldwin /* Receive a TextRequest PDU from a connection. */
62b4068979SJohn Baldwin static struct pdu *
text_receive_request(struct connection * conn)63b4068979SJohn Baldwin text_receive_request(struct connection *conn)
64b4068979SJohn Baldwin {
65b4068979SJohn Baldwin struct pdu *request;
66b4068979SJohn Baldwin struct iscsi_bhs_text_request *bhstr;
67b4068979SJohn Baldwin
68b4068979SJohn Baldwin request = pdu_new(conn);
69b4068979SJohn Baldwin pdu_receive(request);
70b4068979SJohn Baldwin if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
71b4068979SJohn Baldwin ISCSI_BHS_OPCODE_TEXT_REQUEST)
72b4068979SJohn Baldwin log_errx(1, "protocol error: received invalid opcode 0x%x",
73b4068979SJohn Baldwin request->pdu_bhs->bhs_opcode);
74b4068979SJohn Baldwin bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
75b4068979SJohn Baldwin
76b4068979SJohn Baldwin /*
77b4068979SJohn Baldwin * XXX: Implement the C flag some day.
78b4068979SJohn Baldwin */
79b4068979SJohn Baldwin if ((bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE)) !=
80b4068979SJohn Baldwin BHSTR_FLAGS_FINAL)
81b4068979SJohn Baldwin log_errx(1, "received TextRequest PDU with invalid "
82b4068979SJohn Baldwin "flags: %u", bhstr->bhstr_flags);
83b4068979SJohn Baldwin if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) {
84b4068979SJohn Baldwin log_errx(1, "received TextRequest PDU with decreasing CmdSN: "
85b4068979SJohn Baldwin "was %u, is %u", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn));
86b4068979SJohn Baldwin }
87b4068979SJohn Baldwin conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn);
88b4068979SJohn Baldwin if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0)
89b4068979SJohn Baldwin conn->conn_cmdsn++;
90b4068979SJohn Baldwin
91b4068979SJohn Baldwin return (request);
92b4068979SJohn Baldwin }
93b4068979SJohn Baldwin
94b4068979SJohn Baldwin /* Construct a new TextResponse PDU in reply to a request. */
95b4068979SJohn Baldwin static struct pdu *
text_new_response(struct pdu * request,uint32_t ttt,bool final)96b4068979SJohn Baldwin text_new_response(struct pdu *request, uint32_t ttt, bool final)
97b4068979SJohn Baldwin {
98b4068979SJohn Baldwin struct pdu *response;
99b4068979SJohn Baldwin struct connection *conn;
100b4068979SJohn Baldwin struct iscsi_bhs_text_request *bhstr;
101b4068979SJohn Baldwin struct iscsi_bhs_text_response *bhstr2;
102b4068979SJohn Baldwin
103b4068979SJohn Baldwin bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
104b4068979SJohn Baldwin conn = request->pdu_connection;
105b4068979SJohn Baldwin
106b4068979SJohn Baldwin response = pdu_new_response(request);
107b4068979SJohn Baldwin bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs;
108b4068979SJohn Baldwin bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE;
109b4068979SJohn Baldwin if (final)
110b4068979SJohn Baldwin bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL;
111b4068979SJohn Baldwin else
112b4068979SJohn Baldwin bhstr2->bhstr_flags = BHSTR_FLAGS_CONTINUE;
113b4068979SJohn Baldwin bhstr2->bhstr_lun = bhstr->bhstr_lun;
114b4068979SJohn Baldwin bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag;
115b4068979SJohn Baldwin bhstr2->bhstr_target_transfer_tag = ttt;
116b4068979SJohn Baldwin bhstr2->bhstr_statsn = htonl(conn->conn_statsn++);
117b4068979SJohn Baldwin bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn);
118b4068979SJohn Baldwin bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn);
119b4068979SJohn Baldwin
120b4068979SJohn Baldwin return (response);
121b4068979SJohn Baldwin }
122b4068979SJohn Baldwin
123b4068979SJohn Baldwin /* Receive a TextResponse PDU from a connection. */
124b4068979SJohn Baldwin static struct pdu *
text_receive_response(struct connection * conn)125b4068979SJohn Baldwin text_receive_response(struct connection *conn)
126b4068979SJohn Baldwin {
127b4068979SJohn Baldwin struct pdu *response;
128b4068979SJohn Baldwin struct iscsi_bhs_text_response *bhstr;
129b4068979SJohn Baldwin uint8_t flags;
130b4068979SJohn Baldwin
131b4068979SJohn Baldwin response = pdu_new(conn);
132b4068979SJohn Baldwin pdu_receive(response);
133b4068979SJohn Baldwin if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE)
134b4068979SJohn Baldwin log_errx(1, "protocol error: received invalid opcode 0x%x",
135b4068979SJohn Baldwin response->pdu_bhs->bhs_opcode);
136b4068979SJohn Baldwin bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs;
137b4068979SJohn Baldwin flags = bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE);
138b4068979SJohn Baldwin switch (flags) {
139b4068979SJohn Baldwin case BHSTR_FLAGS_CONTINUE:
140b4068979SJohn Baldwin if (bhstr->bhstr_target_transfer_tag == 0xffffffff)
141b4068979SJohn Baldwin log_errx(1, "received continue TextResponse PDU with "
142b4068979SJohn Baldwin "invalid TTT 0x%x",
143b4068979SJohn Baldwin bhstr->bhstr_target_transfer_tag);
144b4068979SJohn Baldwin break;
145b4068979SJohn Baldwin case BHSTR_FLAGS_FINAL:
146b4068979SJohn Baldwin if (bhstr->bhstr_target_transfer_tag != 0xffffffff)
147b4068979SJohn Baldwin log_errx(1, "received final TextResponse PDU with "
148b4068979SJohn Baldwin "invalid TTT 0x%x",
149b4068979SJohn Baldwin bhstr->bhstr_target_transfer_tag);
150b4068979SJohn Baldwin break;
151b4068979SJohn Baldwin default:
152b4068979SJohn Baldwin log_errx(1, "received TextResponse PDU with invalid "
153b4068979SJohn Baldwin "flags: %u", bhstr->bhstr_flags);
154b4068979SJohn Baldwin }
155b4068979SJohn Baldwin if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) {
156b4068979SJohn Baldwin log_errx(1, "received TextResponse PDU with wrong StatSN: "
157b4068979SJohn Baldwin "is %u, should be %u", ntohl(bhstr->bhstr_statsn),
158b4068979SJohn Baldwin conn->conn_statsn + 1);
159b4068979SJohn Baldwin }
160b4068979SJohn Baldwin conn->conn_statsn = ntohl(bhstr->bhstr_statsn);
161b4068979SJohn Baldwin
162b4068979SJohn Baldwin return (response);
163b4068979SJohn Baldwin }
164b4068979SJohn Baldwin
165b4068979SJohn Baldwin /*
166b4068979SJohn Baldwin * Send a list of keys from the initiator to the target in a
167b4068979SJohn Baldwin * TextRequest PDU.
168b4068979SJohn Baldwin */
169b4068979SJohn Baldwin void
text_send_request(struct connection * conn,struct keys * request_keys)170b4068979SJohn Baldwin text_send_request(struct connection *conn, struct keys *request_keys)
171b4068979SJohn Baldwin {
172b4068979SJohn Baldwin struct pdu *request;
173b4068979SJohn Baldwin
174b4068979SJohn Baldwin request = text_new_request(conn, 0xffffffff);
175b4068979SJohn Baldwin keys_save_pdu(request_keys, request);
176b4068979SJohn Baldwin if (request->pdu_data_len == 0)
177b4068979SJohn Baldwin log_errx(1, "No keys to send in a TextRequest");
178b4068979SJohn Baldwin if (request->pdu_data_len >
179b4068979SJohn Baldwin (size_t)conn->conn_max_send_data_segment_length)
180b4068979SJohn Baldwin log_errx(1, "Keys to send in TextRequest are too long");
181b4068979SJohn Baldwin
182b4068979SJohn Baldwin pdu_send(request);
183b4068979SJohn Baldwin pdu_delete(request);
184b4068979SJohn Baldwin }
185b4068979SJohn Baldwin
186b4068979SJohn Baldwin /*
187b4068979SJohn Baldwin * Read a list of keys from the target in a series of TextResponse
188b4068979SJohn Baldwin * PDUs.
189b4068979SJohn Baldwin */
190b4068979SJohn Baldwin struct keys *
text_read_response(struct connection * conn)191b4068979SJohn Baldwin text_read_response(struct connection *conn)
192b4068979SJohn Baldwin {
193b4068979SJohn Baldwin struct keys *response_keys;
194b4068979SJohn Baldwin char *keys_data;
195b4068979SJohn Baldwin size_t keys_len;
196b4068979SJohn Baldwin uint32_t ttt;
197b4068979SJohn Baldwin
198b4068979SJohn Baldwin keys_data = NULL;
199b4068979SJohn Baldwin keys_len = 0;
200b4068979SJohn Baldwin ttt = 0xffffffff;
201b4068979SJohn Baldwin for (;;) {
202b4068979SJohn Baldwin struct pdu *request, *response;
203b4068979SJohn Baldwin struct iscsi_bhs_text_response *bhstr;
204b4068979SJohn Baldwin
205b4068979SJohn Baldwin response = text_receive_response(conn);
206b4068979SJohn Baldwin bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs;
207b4068979SJohn Baldwin if (keys_data == NULL) {
208b4068979SJohn Baldwin ttt = bhstr->bhstr_target_transfer_tag;
209b4068979SJohn Baldwin keys_data = response->pdu_data;
210b4068979SJohn Baldwin keys_len = response->pdu_data_len;
211b4068979SJohn Baldwin response->pdu_data = NULL;
212b4068979SJohn Baldwin } else {
213b4068979SJohn Baldwin keys_data = realloc(keys_data,
214b4068979SJohn Baldwin keys_len + response->pdu_data_len);
215b4068979SJohn Baldwin if (keys_data == NULL)
216b4068979SJohn Baldwin log_err(1, "failed to grow keys block");
217b4068979SJohn Baldwin memcpy(keys_data + keys_len, response->pdu_data,
218b4068979SJohn Baldwin response->pdu_data_len);
219b4068979SJohn Baldwin keys_len += response->pdu_data_len;
220b4068979SJohn Baldwin }
221b4068979SJohn Baldwin if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) != 0) {
222b4068979SJohn Baldwin pdu_delete(response);
223b4068979SJohn Baldwin break;
224b4068979SJohn Baldwin }
225b4068979SJohn Baldwin if (bhstr->bhstr_target_transfer_tag != ttt)
226b4068979SJohn Baldwin log_errx(1, "received non-final TextRequest PDU with "
227b4068979SJohn Baldwin "invalid TTT 0x%x",
228b4068979SJohn Baldwin bhstr->bhstr_target_transfer_tag);
229b4068979SJohn Baldwin pdu_delete(response);
230b4068979SJohn Baldwin
231b4068979SJohn Baldwin /* Send an empty request. */
232b4068979SJohn Baldwin request = text_new_request(conn, ttt);
233b4068979SJohn Baldwin pdu_send(request);
234b4068979SJohn Baldwin pdu_delete(request);
235b4068979SJohn Baldwin }
236b4068979SJohn Baldwin
237b4068979SJohn Baldwin response_keys = keys_new();
238b4068979SJohn Baldwin keys_load(response_keys, keys_data, keys_len);
239b4068979SJohn Baldwin free(keys_data);
240b4068979SJohn Baldwin return (response_keys);
241b4068979SJohn Baldwin }
242b4068979SJohn Baldwin
243b4068979SJohn Baldwin /*
244b4068979SJohn Baldwin * Read a list of keys from the initiator in a TextRequest PDU.
245b4068979SJohn Baldwin */
246b4068979SJohn Baldwin struct keys *
text_read_request(struct connection * conn,struct pdu ** requestp)247b4068979SJohn Baldwin text_read_request(struct connection *conn, struct pdu **requestp)
248b4068979SJohn Baldwin {
249b4068979SJohn Baldwin struct iscsi_bhs_text_request *bhstr;
250b4068979SJohn Baldwin struct pdu *request;
251b4068979SJohn Baldwin struct keys *request_keys;
252b4068979SJohn Baldwin
253b4068979SJohn Baldwin request = text_receive_request(conn);
254b4068979SJohn Baldwin bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
255b4068979SJohn Baldwin if (bhstr->bhstr_target_transfer_tag != 0xffffffff)
256b4068979SJohn Baldwin log_errx(1, "received TextRequest PDU with invalid TTT 0x%x",
257b4068979SJohn Baldwin bhstr->bhstr_target_transfer_tag);
258b4068979SJohn Baldwin if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) {
259b4068979SJohn Baldwin log_errx(1, "received TextRequest PDU with wrong ExpStatSN: "
260b4068979SJohn Baldwin "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn),
261b4068979SJohn Baldwin conn->conn_statsn);
262b4068979SJohn Baldwin }
263b4068979SJohn Baldwin
264b4068979SJohn Baldwin request_keys = keys_new();
265b4068979SJohn Baldwin keys_load_pdu(request_keys, request);
266b4068979SJohn Baldwin *requestp = request;
267b4068979SJohn Baldwin return (request_keys);
268b4068979SJohn Baldwin }
269b4068979SJohn Baldwin
270b4068979SJohn Baldwin /*
271b4068979SJohn Baldwin * Send a response back to the initiator as a series of TextResponse
272b4068979SJohn Baldwin * PDUs.
273b4068979SJohn Baldwin */
274b4068979SJohn Baldwin void
text_send_response(struct pdu * request,struct keys * response_keys)275b4068979SJohn Baldwin text_send_response(struct pdu *request, struct keys *response_keys)
276b4068979SJohn Baldwin {
277b4068979SJohn Baldwin struct connection *conn = request->pdu_connection;
278b4068979SJohn Baldwin char *keys_data;
279b4068979SJohn Baldwin size_t keys_len;
280b4068979SJohn Baldwin size_t keys_offset;
281b4068979SJohn Baldwin uint32_t ttt;
282b4068979SJohn Baldwin
283b4068979SJohn Baldwin keys_save(response_keys, &keys_data, &keys_len);
284b4068979SJohn Baldwin keys_offset = 0;
285b4068979SJohn Baldwin ttt = keys_len;
286b4068979SJohn Baldwin for (;;) {
287b4068979SJohn Baldwin struct pdu *request2, *response;
288b4068979SJohn Baldwin struct iscsi_bhs_text_request *bhstr;
289b4068979SJohn Baldwin size_t todo;
290b4068979SJohn Baldwin bool final;
291b4068979SJohn Baldwin
292b4068979SJohn Baldwin todo = keys_len - keys_offset;
293b4068979SJohn Baldwin if (todo > (size_t)conn->conn_max_send_data_segment_length) {
294b4068979SJohn Baldwin final = false;
295b4068979SJohn Baldwin todo = conn->conn_max_send_data_segment_length;
296b4068979SJohn Baldwin } else {
297b4068979SJohn Baldwin final = true;
298b4068979SJohn Baldwin ttt = 0xffffffff;
299b4068979SJohn Baldwin }
300b4068979SJohn Baldwin
301b4068979SJohn Baldwin response = text_new_response(request, ttt, final);
302b4068979SJohn Baldwin response->pdu_data = keys_data + keys_offset;
303b4068979SJohn Baldwin response->pdu_data_len = todo;
304b4068979SJohn Baldwin keys_offset += todo;
305b4068979SJohn Baldwin
306b4068979SJohn Baldwin pdu_send(response);
307b4068979SJohn Baldwin response->pdu_data = NULL;
308b4068979SJohn Baldwin pdu_delete(response);
309b4068979SJohn Baldwin
310b4068979SJohn Baldwin if (final)
311b4068979SJohn Baldwin break;
312b4068979SJohn Baldwin
313b4068979SJohn Baldwin /*
314b4068979SJohn Baldwin * Wait for an empty request.
315b4068979SJohn Baldwin *
316b4068979SJohn Baldwin * XXX: Linux's Open-iSCSI initiator doesn't update
317b4068979SJohn Baldwin * ExpStatSN when receiving a TextResponse PDU.
318b4068979SJohn Baldwin */
319b4068979SJohn Baldwin request2 = text_receive_request(conn);
320b4068979SJohn Baldwin bhstr = (struct iscsi_bhs_text_request *)request2->pdu_bhs;
321b4068979SJohn Baldwin if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0)
322b4068979SJohn Baldwin log_errx(1, "received continuation TextRequest PDU "
323b4068979SJohn Baldwin "without F set");
324b4068979SJohn Baldwin if (pdu_data_segment_length(request2) != 0)
325b4068979SJohn Baldwin log_errx(1, "received non-empty continuation "
326b4068979SJohn Baldwin "TextRequest PDU");
327b4068979SJohn Baldwin if (bhstr->bhstr_target_transfer_tag != ttt)
328b4068979SJohn Baldwin log_errx(1, "received TextRequest PDU with invalid "
329b4068979SJohn Baldwin "TTT 0x%x", bhstr->bhstr_target_transfer_tag);
330b4068979SJohn Baldwin pdu_delete(request2);
331b4068979SJohn Baldwin }
332b4068979SJohn Baldwin free(keys_data);
333b4068979SJohn Baldwin }
334