1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 } 334