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