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