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