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$"); 33 34#include <sys/types.h> 35#include <sys/uio.h> 36#include <assert.h> 37#include <stdint.h> 38#include <stdio.h> 39#include <stdlib.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(int fd, char *data, size_t len) 183{ 184 ssize_t ret; 185 186 while (len > 0) { 187 ret = read(fd, data, len); 188 if (ret < 0) { 189 if (timed_out()) 190 log_errx(1, "exiting due to timeout"); 191 log_err(1, "read"); 192 } else if (ret == 0) 193 log_errx(1, "read: connection lost"); 194 len -= ret; 195 data += ret; 196 } 197} 198 199void 200pdu_receive(struct pdu *pdu) 201{ 202 size_t len, padding; 203 char dummy[4]; 204 205#ifdef ICL_KERNEL_PROXY 206 if (pdu->pdu_connection->conn_conf.isc_iser != 0) 207 return (pdu_receive_proxy(pdu)); 208#endif 209 210 assert(pdu->pdu_connection->conn_conf.isc_iser == 0); 211 212 pdu_read(pdu->pdu_connection->conn_socket, 213 (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); 214 215 len = pdu_ahs_length(pdu); 216 if (len > 0) 217 log_errx(1, "protocol error: non-empty AHS"); 218 219 len = pdu_data_segment_length(pdu); 220 if (len > 0) { 221 if (len > ISCSI_MAX_DATA_SEGMENT_LENGTH) { 222 log_errx(1, "protocol error: received PDU " 223 "with DataSegmentLength exceeding %d", 224 ISCSI_MAX_DATA_SEGMENT_LENGTH); 225 } 226 227 pdu->pdu_data_len = len; 228 pdu->pdu_data = malloc(len); 229 if (pdu->pdu_data == NULL) 230 log_err(1, "malloc"); 231 232 pdu_read(pdu->pdu_connection->conn_socket, 233 (char *)pdu->pdu_data, pdu->pdu_data_len); 234 235 padding = pdu_padding(pdu); 236 if (padding != 0) { 237 assert(padding < sizeof(dummy)); 238 pdu_read(pdu->pdu_connection->conn_socket, 239 (char *)dummy, padding); 240 } 241 } 242} 243 244void 245pdu_send(struct pdu *pdu) 246{ 247 ssize_t ret, total_len; 248 size_t padding; 249 uint32_t zero = 0; 250 struct iovec iov[3]; 251 int iovcnt; 252 253#ifdef ICL_KERNEL_PROXY 254 if (pdu->pdu_connection->conn_conf.isc_iser != 0) 255 return (pdu_send_proxy(pdu)); 256#endif 257 258 assert(pdu->pdu_connection->conn_conf.isc_iser == 0); 259 260 pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 261 iov[0].iov_base = pdu->pdu_bhs; 262 iov[0].iov_len = sizeof(*pdu->pdu_bhs); 263 total_len = iov[0].iov_len; 264 iovcnt = 1; 265 266 if (pdu->pdu_data_len > 0) { 267 iov[1].iov_base = pdu->pdu_data; 268 iov[1].iov_len = pdu->pdu_data_len; 269 total_len += iov[1].iov_len; 270 iovcnt = 2; 271 272 padding = pdu_padding(pdu); 273 if (padding > 0) { 274 assert(padding < sizeof(zero)); 275 iov[2].iov_base = &zero; 276 iov[2].iov_len = padding; 277 total_len += iov[2].iov_len; 278 iovcnt = 3; 279 } 280 } 281 282 ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); 283 if (ret < 0) { 284 if (timed_out()) 285 log_errx(1, "exiting due to timeout"); 286 log_err(1, "writev"); 287 } 288 if (ret != total_len) 289 log_errx(1, "short write"); 290} 291 292void 293pdu_delete(struct pdu *pdu) 294{ 295 296 free(pdu->pdu_data); 297 free(pdu->pdu_bhs); 298 free(pdu); 299} 300