]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libiscsiutil/text.c
Merge bmake-20230622
[FreeBSD/FreeBSD.git] / lib / libiscsiutil / text.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2012 The FreeBSD Foundation
5  *
6  * This software was developed by Edward Tomasz Napierala under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
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.
17  *
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
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/types.h>
32 #include <netinet/in.h>
33
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <iscsi_proto.h>
38 #include "libiscsiutil.h"
39
40 /* Construct a new TextRequest PDU. */
41 static struct pdu *
42 text_new_request(struct connection *conn, uint32_t ttt)
43 {
44         struct pdu *request;
45         struct iscsi_bhs_text_request *bhstr;
46
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;
54
55         bhstr->bhstr_cmdsn = conn->conn_cmdsn;
56         bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1);
57
58         return (request);
59 }
60
61 /* Receive a TextRequest PDU from a connection. */
62 static struct pdu *
63 text_receive_request(struct connection *conn)
64 {
65         struct pdu *request;
66         struct iscsi_bhs_text_request *bhstr;
67
68         request = pdu_new(conn);
69         pdu_receive(request);
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;
75
76         /*
77          * XXX: Implement the C flag some day.
78          */
79         if ((bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE)) !=
80             BHSTR_FLAGS_FINAL)
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));
86         }
87         conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn);
88         if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0)
89                 conn->conn_cmdsn++;
90
91         return (request);
92 }
93
94 /* Construct a new TextResponse PDU in reply to a request. */
95 static struct pdu *
96 text_new_response(struct pdu *request, uint32_t ttt, bool final)
97 {
98         struct pdu *response;
99         struct connection *conn;
100         struct iscsi_bhs_text_request *bhstr;
101         struct iscsi_bhs_text_response *bhstr2;
102
103         bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
104         conn = request->pdu_connection;
105
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;
109         if (final)
110                 bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL;
111         else
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);
119
120         return (response);
121 }
122
123 /* Receive a TextResponse PDU from a connection. */
124 static struct pdu *
125 text_receive_response(struct connection *conn)
126 {
127         struct pdu *response;
128         struct iscsi_bhs_text_response *bhstr;
129         uint8_t flags;
130
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);
138         switch (flags) {
139         case BHSTR_FLAGS_CONTINUE:
140                 if (bhstr->bhstr_target_transfer_tag == 0xffffffff)
141                         log_errx(1, "received continue TextResponse PDU with "
142                             "invalid TTT 0x%x",
143                             bhstr->bhstr_target_transfer_tag);
144                 break;
145         case BHSTR_FLAGS_FINAL:
146                 if (bhstr->bhstr_target_transfer_tag != 0xffffffff)
147                         log_errx(1, "received final TextResponse PDU with "
148                             "invalid TTT 0x%x",
149                             bhstr->bhstr_target_transfer_tag);
150                 break;
151         default:
152                 log_errx(1, "received TextResponse PDU with invalid "
153                     "flags: %u", bhstr->bhstr_flags);
154         }
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);
159         }
160         conn->conn_statsn = ntohl(bhstr->bhstr_statsn);
161
162         return (response);
163 }
164
165 /*
166  * Send a list of keys from the initiator to the target in a
167  * TextRequest PDU.
168  */
169 void
170 text_send_request(struct connection *conn, struct keys *request_keys)
171 {
172         struct pdu *request;
173
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");
181
182         pdu_send(request);
183         pdu_delete(request);
184 }
185
186 /*
187  * Read a list of keys from the target in a series of TextResponse
188  * PDUs.
189  */
190 struct keys *
191 text_read_response(struct connection *conn)
192 {
193         struct keys *response_keys;
194         char *keys_data;
195         size_t keys_len;
196         uint32_t ttt;
197
198         keys_data = NULL;
199         keys_len = 0;
200         ttt = 0xffffffff;
201         for (;;) {
202                 struct pdu *request, *response;
203                 struct iscsi_bhs_text_response *bhstr;
204
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;
212                 } else {
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;
220                 }
221                 if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) != 0) {
222                         pdu_delete(response);
223                         break;
224                 }
225                 if (bhstr->bhstr_target_transfer_tag != ttt)
226                         log_errx(1, "received non-final TextRequest PDU with "
227                             "invalid TTT 0x%x",
228                             bhstr->bhstr_target_transfer_tag);
229                 pdu_delete(response);
230
231                 /* Send an empty request. */
232                 request = text_new_request(conn, ttt);
233                 pdu_send(request);
234                 pdu_delete(request);
235         }
236
237         response_keys = keys_new();
238         keys_load(response_keys, keys_data, keys_len);
239         free(keys_data);
240         return (response_keys);
241 }
242
243 /*
244  * Read a list of keys from the initiator in a TextRequest PDU.
245  */
246 struct keys *
247 text_read_request(struct connection *conn, struct pdu **requestp)
248 {
249         struct iscsi_bhs_text_request *bhstr;
250         struct pdu *request;
251         struct keys *request_keys;
252
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),
261                     conn->conn_statsn);
262         }
263
264         request_keys = keys_new();
265         keys_load_pdu(request_keys, request);
266         *requestp = request;
267         return (request_keys);
268 }
269
270 /*
271  * Send a response back to the initiator as a series of TextResponse
272  * PDUs.
273  */
274 void
275 text_send_response(struct pdu *request, struct keys *response_keys)
276 {
277         struct connection *conn = request->pdu_connection;
278         char *keys_data;
279         size_t keys_len;
280         size_t keys_offset;
281         uint32_t ttt;
282
283         keys_save(response_keys, &keys_data, &keys_len);
284         keys_offset = 0;
285         ttt = keys_len;
286         for (;;) {
287                 struct pdu *request2, *response;
288                 struct iscsi_bhs_text_request *bhstr;
289                 size_t todo;
290                 bool final;
291
292                 todo = keys_len - keys_offset;
293                 if (todo > (size_t)conn->conn_max_send_data_segment_length) {
294                         final = false;
295                         todo = conn->conn_max_send_data_segment_length;
296                 } else {
297                         final = true;
298                         ttt = 0xffffffff;
299                 }
300
301                 response = text_new_response(request, ttt, final);
302                 response->pdu_data = keys_data + keys_offset;
303                 response->pdu_data_len = todo;
304                 keys_offset += todo;
305
306                 pdu_send(response);
307                 response->pdu_data = NULL;
308                 pdu_delete(response);
309
310                 if (final)
311                         break;
312
313                 /*
314                  * Wait for an empty request.
315                  *
316                  * XXX: Linux's Open-iSCSI initiator doesn't update
317                  * ExpStatSN when receiving a TextResponse PDU.
318                  */
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 "
323                             "without F set");
324                 if (pdu_data_segment_length(request2) != 0)
325                         log_errx(1, "received non-empty continuation "
326                             "TextRequest PDU");
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);
331         }
332         free(keys_data);
333 }