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 <stdlib.h> 39#include <unistd.h> 40 41#include "iscsid.h" 42#include "iscsi_proto.h" 43 44#ifdef ICL_KERNEL_PROXY 45#include <sys/ioctl.h> 46#endif 47 48static int 49pdu_ahs_length(const struct pdu *pdu) 50{ 51 52 return (pdu->pdu_bhs->bhs_total_ahs_len * 4); 53} 54 55static int 56pdu_data_segment_length(const struct pdu *pdu) 57{ 58 uint32_t len = 0; 59 60 len += pdu->pdu_bhs->bhs_data_segment_len[0]; 61 len <<= 8; 62 len += pdu->pdu_bhs->bhs_data_segment_len[1]; 63 len <<= 8; 64 len += pdu->pdu_bhs->bhs_data_segment_len[2]; 65 66 return (len); 67} 68 69static void 70pdu_set_data_segment_length(struct pdu *pdu, uint32_t len) 71{ 72 73 pdu->pdu_bhs->bhs_data_segment_len[2] = len; 74 pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; 75 pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; 76} 77 78struct pdu * 79pdu_new(struct connection *conn) 80{ 81 struct pdu *pdu; 82 83 pdu = calloc(sizeof(*pdu), 1); 84 if (pdu == NULL) 85 log_err(1, "calloc"); 86 87 pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1); 88 if (pdu->pdu_bhs == NULL) 89 log_err(1, "calloc"); 90 91 pdu->pdu_connection = conn; 92 93 return (pdu); 94} 95 96struct pdu * 97pdu_new_response(struct pdu *request) 98{ 99 100 return (pdu_new(request->pdu_connection)); 101} 102 103#ifdef ICL_KERNEL_PROXY 104 105static void 106pdu_receive_proxy(struct pdu *pdu) 107{ 108 struct iscsi_daemon_receive *idr; 109 size_t len; 110 int error; 111 112 assert(pdu->pdu_connection->conn_conf.isc_iser != 0); 113 114 pdu->pdu_data = malloc(ISCSI_MAX_DATA_SEGMENT_LENGTH); 115 if (pdu->pdu_data == NULL) 116 log_err(1, "malloc"); 117 118 idr = calloc(1, sizeof(*idr)); 119 if (idr == NULL) 120 log_err(1, "calloc"); 121 122 idr->idr_session_id = pdu->pdu_connection->conn_session_id; 123 idr->idr_bhs = pdu->pdu_bhs; 124 idr->idr_data_segment_len = ISCSI_MAX_DATA_SEGMENT_LENGTH; 125 idr->idr_data_segment = pdu->pdu_data; 126 127 error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDRECEIVE, idr); 128 if (error != 0) 129 log_err(1, "ISCSIDRECEIVE"); 130 131 len = pdu_ahs_length(pdu); 132 if (len > 0) 133 log_errx(1, "protocol error: non-empty AHS"); 134 135 len = pdu_data_segment_length(pdu); 136 assert(len <= ISCSI_MAX_DATA_SEGMENT_LENGTH); 137 pdu->pdu_data_len = len; 138 139 free(idr); 140} 141 142static void 143pdu_send_proxy(struct pdu *pdu) 144{ 145 struct iscsi_daemon_send *ids; 146 int error; 147 148 assert(pdu->pdu_connection->conn_conf.isc_iser != 0); 149 150 pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 151 152 ids = calloc(1, sizeof(*ids)); 153 if (ids == NULL) 154 log_err(1, "calloc"); 155 156 ids->ids_session_id = pdu->pdu_connection->conn_session_id; 157 ids->ids_bhs = pdu->pdu_bhs; 158 ids->ids_data_segment_len = pdu->pdu_data_len; 159 ids->ids_data_segment = pdu->pdu_data; 160 161 error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDSEND, ids); 162 if (error != 0) 163 log_err(1, "ISCSIDSEND"); 164 165 free(ids); 166} 167 168#endif /* ICL_KERNEL_PROXY */ 169 170static size_t 171pdu_padding(const struct pdu *pdu) 172{ 173 174 if ((pdu->pdu_data_len % 4) != 0) 175 return (4 - (pdu->pdu_data_len % 4)); 176 177 return (0); 178} 179 180static void 181pdu_read(int fd, char *data, size_t len) 182{ 183 ssize_t ret; 184 185 while (len > 0) { 186 ret = read(fd, data, len); 187 if (ret < 0) { 188 if (timed_out()) 189 log_errx(1, "exiting due to timeout"); 190 log_err(1, "read"); 191 } else if (ret == 0) 192 log_errx(1, "read: connection lost"); 193 len -= ret; 194 data += ret; 195 } 196} 197 198void 199pdu_receive(struct pdu *pdu) 200{ 201 size_t len, padding; 202 char dummy[4]; 203 204#ifdef ICL_KERNEL_PROXY 205 if (pdu->pdu_connection->conn_conf.isc_iser != 0) 206 return (pdu_receive_proxy(pdu)); 207#endif 208 209 assert(pdu->pdu_connection->conn_conf.isc_iser == 0); 210 211 pdu_read(pdu->pdu_connection->conn_socket, 212 (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); 213 214 len = pdu_ahs_length(pdu); 215 if (len > 0) 216 log_errx(1, "protocol error: non-empty AHS"); 217 218 len = pdu_data_segment_length(pdu); 219 if (len > 0) { 220 if (len > ISCSI_MAX_DATA_SEGMENT_LENGTH) { 221 log_errx(1, "protocol error: received PDU " 222 "with DataSegmentLength exceeding %d", 223 ISCSI_MAX_DATA_SEGMENT_LENGTH); 224 } 225 226 pdu->pdu_data_len = len; 227 pdu->pdu_data = malloc(len); 228 if (pdu->pdu_data == NULL) 229 log_err(1, "malloc"); 230 231 pdu_read(pdu->pdu_connection->conn_socket, 232 (char *)pdu->pdu_data, pdu->pdu_data_len); 233 234 padding = pdu_padding(pdu); 235 if (padding != 0) { 236 assert(padding < sizeof(dummy)); 237 pdu_read(pdu->pdu_connection->conn_socket, 238 (char *)dummy, padding); 239 } 240 } 241} 242 243void 244pdu_send(struct pdu *pdu) 245{ 246 ssize_t ret, total_len; 247 size_t padding; 248 uint32_t zero = 0; 249 struct iovec iov[3]; 250 int iovcnt; 251 252#ifdef ICL_KERNEL_PROXY 253 if (pdu->pdu_connection->conn_conf.isc_iser != 0) 254 return (pdu_send_proxy(pdu)); 255#endif 256 257 assert(pdu->pdu_connection->conn_conf.isc_iser == 0); 258 259 pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 260 iov[0].iov_base = pdu->pdu_bhs; 261 iov[0].iov_len = sizeof(*pdu->pdu_bhs); 262 total_len = iov[0].iov_len; 263 iovcnt = 1; 264 265 if (pdu->pdu_data_len > 0) { 266 iov[1].iov_base = pdu->pdu_data; 267 iov[1].iov_len = pdu->pdu_data_len; 268 total_len += iov[1].iov_len; 269 iovcnt = 2; 270 271 padding = pdu_padding(pdu); 272 if (padding > 0) { 273 assert(padding < sizeof(zero)); 274 iov[2].iov_base = &zero; 275 iov[2].iov_len = padding; 276 total_len += iov[2].iov_len; 277 iovcnt = 3; 278 } 279 } 280 281 ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); 282 if (ret < 0) { 283 if (timed_out()) 284 log_errx(1, "exiting due to timeout"); 285 log_err(1, "writev"); 286 } 287 if (ret != total_len) 288 log_errx(1, "short write"); 289} 290 291void 292pdu_delete(struct pdu *pdu) 293{ 294 295 free(pdu->pdu_data); 296 free(pdu->pdu_bhs); 297 free(pdu); 298} 299