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