1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Edward Tomasz Napierala under sponsorship 8 * from the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: stable/11/usr.sbin/iscsid/pdu.c 330449 2018-03-05 07:26:05Z eadler $"); 35 36#include <sys/types.h> 37#include <sys/uio.h> 38#include <assert.h> 39#include <errno.h> 40#include <stdlib.h> 41#include <string.h> 42#include <unistd.h> 43 44#include "iscsid.h" 45#include "iscsi_proto.h" 46 47#ifdef ICL_KERNEL_PROXY 48#include <sys/ioctl.h> 49#endif 50 51static int 52pdu_ahs_length(const struct pdu *pdu) 53{ 54 55 return (pdu->pdu_bhs->bhs_total_ahs_len * 4); 56} 57 58static int 59pdu_data_segment_length(const struct pdu *pdu) 60{ 61 uint32_t len = 0; 62 63 len += pdu->pdu_bhs->bhs_data_segment_len[0]; 64 len <<= 8; 65 len += pdu->pdu_bhs->bhs_data_segment_len[1]; 66 len <<= 8; 67 len += pdu->pdu_bhs->bhs_data_segment_len[2]; 68 69 return (len); 70} 71 72static void 73pdu_set_data_segment_length(struct pdu *pdu, uint32_t len) 74{ 75 76 pdu->pdu_bhs->bhs_data_segment_len[2] = len; 77 pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; 78 pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; 79} 80 81struct pdu * 82pdu_new(struct connection *conn) 83{ 84 struct pdu *pdu; 85 86 pdu = calloc(1, sizeof(*pdu)); 87 if (pdu == NULL) 88 log_err(1, "calloc"); 89 90 pdu->pdu_bhs = calloc(1, sizeof(*pdu->pdu_bhs)); 91 if (pdu->pdu_bhs == NULL) 92 log_err(1, "calloc"); 93 94 pdu->pdu_connection = conn; 95 96 return (pdu); 97} 98 99struct pdu * 100pdu_new_response(struct pdu *request) 101{ 102 103 return (pdu_new(request->pdu_connection)); 104} 105 106#ifdef ICL_KERNEL_PROXY 107 108static void 109pdu_receive_proxy(struct pdu *pdu) 110{ 111 struct iscsi_daemon_receive *idr; 112 size_t len; 113 int error; 114 115 assert(pdu->pdu_connection->conn_conf.isc_iser != 0); 116 117 pdu->pdu_data = malloc(ISCSI_MAX_DATA_SEGMENT_LENGTH); 118 if (pdu->pdu_data == NULL) 119 log_err(1, "malloc"); 120 121 idr = calloc(1, sizeof(*idr)); 122 if (idr == NULL) 123 log_err(1, "calloc"); 124 125 idr->idr_session_id = pdu->pdu_connection->conn_session_id; 126 idr->idr_bhs = pdu->pdu_bhs; 127 idr->idr_data_segment_len = ISCSI_MAX_DATA_SEGMENT_LENGTH; 128 idr->idr_data_segment = pdu->pdu_data; 129 130 error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDRECEIVE, idr); 131 if (error != 0) 132 log_err(1, "ISCSIDRECEIVE"); 133 134 len = pdu_ahs_length(pdu); 135 if (len > 0) 136 log_errx(1, "protocol error: non-empty AHS"); 137 138 len = pdu_data_segment_length(pdu); 139 assert(len <= ISCSI_MAX_DATA_SEGMENT_LENGTH); 140 pdu->pdu_data_len = len; 141 142 free(idr); 143} 144 145static void 146pdu_send_proxy(struct pdu *pdu) 147{ 148 struct iscsi_daemon_send *ids; 149 int error; 150 151 assert(pdu->pdu_connection->conn_conf.isc_iser != 0); 152 153 pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 154 155 ids = calloc(1, sizeof(*ids)); 156 if (ids == NULL) 157 log_err(1, "calloc"); 158 159 ids->ids_session_id = pdu->pdu_connection->conn_session_id; 160 ids->ids_bhs = pdu->pdu_bhs; 161 ids->ids_data_segment_len = pdu->pdu_data_len; 162 ids->ids_data_segment = pdu->pdu_data; 163 164 error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDSEND, ids); 165 if (error != 0) 166 log_err(1, "ISCSIDSEND"); 167 168 free(ids); 169} 170 171#endif /* ICL_KERNEL_PROXY */ 172 173static size_t 174pdu_padding(const struct pdu *pdu) 175{ 176 177 if ((pdu->pdu_data_len % 4) != 0) 178 return (4 - (pdu->pdu_data_len % 4)); 179 180 return (0); 181} 182 183static void 184pdu_read(const struct connection *conn, char *data, size_t len) 185{ 186 ssize_t ret; 187 188 while (len > 0) { 189 ret = read(conn->conn_socket, data, len); 190 if (ret < 0) { 191 if (timed_out()) { 192 fail(conn, "Login Phase timeout"); 193 log_errx(1, "exiting due to timeout"); 194 } 195 fail(conn, strerror(errno)); 196 log_err(1, "read"); 197 } else if (ret == 0) { 198 fail(conn, "connection lost"); 199 log_errx(1, "read: connection lost"); 200 } 201 len -= ret; 202 data += ret; 203 } 204} 205 206void 207pdu_receive(struct pdu *pdu) 208{ 209 size_t len, padding; 210 char dummy[4]; 211 212#ifdef ICL_KERNEL_PROXY 213 if (pdu->pdu_connection->conn_conf.isc_iser != 0) 214 return (pdu_receive_proxy(pdu)); 215#endif 216 217 assert(pdu->pdu_connection->conn_conf.isc_iser == 0); 218 219 pdu_read(pdu->pdu_connection, 220 (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); 221 222 len = pdu_ahs_length(pdu); 223 if (len > 0) 224 log_errx(1, "protocol error: non-empty AHS"); 225 226 len = pdu_data_segment_length(pdu); 227 if (len > 0) { 228 if (len > ISCSI_MAX_DATA_SEGMENT_LENGTH) { 229 log_errx(1, "protocol error: received PDU " 230 "with DataSegmentLength exceeding %d", 231 ISCSI_MAX_DATA_SEGMENT_LENGTH); 232 } 233 234 pdu->pdu_data_len = len; 235 pdu->pdu_data = malloc(len); 236 if (pdu->pdu_data == NULL) 237 log_err(1, "malloc"); 238 239 pdu_read(pdu->pdu_connection, 240 (char *)pdu->pdu_data, pdu->pdu_data_len); 241 242 padding = pdu_padding(pdu); 243 if (padding != 0) { 244 assert(padding < sizeof(dummy)); 245 pdu_read(pdu->pdu_connection, 246 (char *)dummy, padding); 247 } 248 } 249} 250 251void 252pdu_send(struct pdu *pdu) 253{ 254 ssize_t ret, total_len; 255 size_t padding; 256 uint32_t zero = 0; 257 struct iovec iov[3]; 258 int iovcnt; 259 260#ifdef ICL_KERNEL_PROXY 261 if (pdu->pdu_connection->conn_conf.isc_iser != 0) 262 return (pdu_send_proxy(pdu)); 263#endif 264 265 assert(pdu->pdu_connection->conn_conf.isc_iser == 0); 266 267 pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 268 iov[0].iov_base = pdu->pdu_bhs; 269 iov[0].iov_len = sizeof(*pdu->pdu_bhs); 270 total_len = iov[0].iov_len; 271 iovcnt = 1; 272 273 if (pdu->pdu_data_len > 0) { 274 iov[1].iov_base = pdu->pdu_data; 275 iov[1].iov_len = pdu->pdu_data_len; 276 total_len += iov[1].iov_len; 277 iovcnt = 2; 278 279 padding = pdu_padding(pdu); 280 if (padding > 0) { 281 assert(padding < sizeof(zero)); 282 iov[2].iov_base = &zero; 283 iov[2].iov_len = padding; 284 total_len += iov[2].iov_len; 285 iovcnt = 3; 286 } 287 } 288 289 ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); 290 if (ret < 0) { 291 if (timed_out()) 292 log_errx(1, "exiting due to timeout"); 293 log_err(1, "writev"); 294 } 295 if (ret != total_len) 296 log_errx(1, "short write"); 297} 298 299void 300pdu_delete(struct pdu *pdu) 301{ 302 303 free(pdu->pdu_data); 304 free(pdu->pdu_bhs); 305 free(pdu); 306} 307