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$ 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 "ctld.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 104void 105pdu_receive(struct pdu *pdu) 106{ 107 size_t len; 108 109 kernel_receive(pdu); 110 111 len = pdu_ahs_length(pdu); 112 if (len > 0) 113 log_errx(1, "protocol error: non-empty AHS"); 114 115 len = pdu_data_segment_length(pdu); 116 assert(len <= MAX_DATA_SEGMENT_LENGTH); 117 pdu->pdu_data_len = len; 118} 119 120void 121pdu_send(struct pdu *pdu) 122{ 123 124 pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 125 kernel_send(pdu); 126} 127 128#else /* !ICL_KERNEL_PROXY */ 129 130static size_t 131pdu_padding(const struct pdu *pdu) 132{ 133 134 if ((pdu->pdu_data_len % 4) != 0) 135 return (4 - (pdu->pdu_data_len % 4)); 136 137 return (0); 138} 139 140static void 141pdu_read(int fd, char *data, size_t len) 142{ 143 ssize_t ret; 144 145 while (len > 0) { 146 ret = read(fd, data, len); 147 if (ret < 0) { 148 if (timed_out()) 149 log_errx(1, "exiting due to timeout"); 150 log_err(1, "read"); 151 } else if (ret == 0) 152 log_errx(1, "read: connection lost"); 153 len -= ret; 154 data += ret; 155 } 156} 157 158void 159pdu_receive(struct pdu *pdu) 160{ 161 size_t len, padding; 162 char dummy[4]; 163 164 pdu_read(pdu->pdu_connection->conn_socket, 165 (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); 166 167 len = pdu_ahs_length(pdu); 168 if (len > 0) 169 log_errx(1, "protocol error: non-empty AHS"); 170 171 len = pdu_data_segment_length(pdu); 172 if (len > 0) { 173 if (len > MAX_DATA_SEGMENT_LENGTH) { 174 log_errx(1, "protocol error: received PDU " 175 "with DataSegmentLength exceeding %d", 176 MAX_DATA_SEGMENT_LENGTH); 177 } 178 179 pdu->pdu_data_len = len; 180 pdu->pdu_data = malloc(len); 181 if (pdu->pdu_data == NULL) 182 log_err(1, "malloc"); 183 184 pdu_read(pdu->pdu_connection->conn_socket, 185 (char *)pdu->pdu_data, pdu->pdu_data_len); 186 187 padding = pdu_padding(pdu); 188 if (padding != 0) { 189 assert(padding < sizeof(dummy)); 190 pdu_read(pdu->pdu_connection->conn_socket, 191 (char *)dummy, padding); 192 } 193 } 194} 195 196void 197pdu_send(struct pdu *pdu) 198{ 199 ssize_t ret, total_len; 200 size_t padding; 201 uint32_t zero = 0; 202 struct iovec iov[3]; 203 int iovcnt; 204 205 pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 206 iov[0].iov_base = pdu->pdu_bhs; 207 iov[0].iov_len = sizeof(*pdu->pdu_bhs); 208 total_len = iov[0].iov_len; 209 iovcnt = 1; 210 211 if (pdu->pdu_data_len > 0) { 212 iov[1].iov_base = pdu->pdu_data; 213 iov[1].iov_len = pdu->pdu_data_len; 214 total_len += iov[1].iov_len; 215 iovcnt = 2; 216 217 padding = pdu_padding(pdu); 218 if (padding > 0) { 219 assert(padding < sizeof(zero)); 220 iov[2].iov_base = &zero; 221 iov[2].iov_len = padding; 222 total_len += iov[2].iov_len; 223 iovcnt = 3; 224 } 225 } 226 227 ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); 228 if (ret < 0) { 229 if (timed_out()) 230 log_errx(1, "exiting due to timeout"); 231 log_err(1, "writev"); 232 } 233 if (ret != total_len) 234 log_errx(1, "short write"); 235} 236 237#endif /* !ICL_KERNEL_PROXY */ 238 239void 240pdu_delete(struct pdu *pdu) 241{ 242 243 free(pdu->pdu_data); 244 free(pdu->pdu_bhs); 245 free(pdu); 246} 247