1255570Strasz/*- 2255570Strasz * Copyright (c) 2012 The FreeBSD Foundation 3255570Strasz * All rights reserved. 4255570Strasz * 5255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6255570Strasz * from the FreeBSD Foundation. 7255570Strasz * 8255570Strasz * Redistribution and use in source and binary forms, with or without 9255570Strasz * modification, are permitted provided that the following conditions 10255570Strasz * are met: 11255570Strasz * 1. Redistributions of source code must retain the above copyright 12255570Strasz * notice, this list of conditions and the following disclaimer. 13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright 14255570Strasz * notice, this list of conditions and the following disclaimer in the 15255570Strasz * documentation and/or other materials provided with the distribution. 16255570Strasz * 17255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20255570Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27255570Strasz * SUCH DAMAGE. 28255570Strasz * 29255570Strasz * $FreeBSD$ 30255570Strasz */ 31255570Strasz 32255570Strasz#include <sys/types.h> 33255570Strasz#include <sys/uio.h> 34255570Strasz#include <assert.h> 35255570Strasz#include <stdint.h> 36255570Strasz#include <stdio.h> 37255570Strasz#include <stdlib.h> 38255570Strasz#include <unistd.h> 39255570Strasz 40255570Strasz#include "ctld.h" 41255570Strasz#include "iscsi_proto.h" 42255570Strasz 43255570Strasz#ifdef ICL_KERNEL_PROXY 44255570Strasz#include <sys/ioctl.h> 45255570Strasz#endif 46255570Strasz 47255570Straszstatic int 48255570Straszpdu_ahs_length(const struct pdu *pdu) 49255570Strasz{ 50255570Strasz 51255570Strasz return (pdu->pdu_bhs->bhs_total_ahs_len * 4); 52255570Strasz} 53255570Strasz 54255570Straszstatic int 55255570Straszpdu_data_segment_length(const struct pdu *pdu) 56255570Strasz{ 57255570Strasz uint32_t len = 0; 58255570Strasz 59255570Strasz len += pdu->pdu_bhs->bhs_data_segment_len[0]; 60255570Strasz len <<= 8; 61255570Strasz len += pdu->pdu_bhs->bhs_data_segment_len[1]; 62255570Strasz len <<= 8; 63255570Strasz len += pdu->pdu_bhs->bhs_data_segment_len[2]; 64255570Strasz 65255570Strasz return (len); 66255570Strasz} 67255570Strasz 68255570Straszstatic void 69255570Straszpdu_set_data_segment_length(struct pdu *pdu, uint32_t len) 70255570Strasz{ 71255570Strasz 72255570Strasz pdu->pdu_bhs->bhs_data_segment_len[2] = len; 73255570Strasz pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; 74255570Strasz pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; 75255570Strasz} 76255570Strasz 77255570Straszstruct pdu * 78255570Straszpdu_new(struct connection *conn) 79255570Strasz{ 80255570Strasz struct pdu *pdu; 81255570Strasz 82255570Strasz pdu = calloc(sizeof(*pdu), 1); 83255570Strasz if (pdu == NULL) 84255570Strasz log_err(1, "calloc"); 85255570Strasz 86255570Strasz pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1); 87255570Strasz if (pdu->pdu_bhs == NULL) 88255570Strasz log_err(1, "calloc"); 89255570Strasz 90255570Strasz pdu->pdu_connection = conn; 91255570Strasz 92255570Strasz return (pdu); 93255570Strasz} 94255570Strasz 95255570Straszstruct pdu * 96255570Straszpdu_new_response(struct pdu *request) 97255570Strasz{ 98255570Strasz 99255570Strasz return (pdu_new(request->pdu_connection)); 100255570Strasz} 101255570Strasz 102255570Strasz#ifdef ICL_KERNEL_PROXY 103255570Strasz 104255570Straszvoid 105255570Straszpdu_receive(struct pdu *pdu) 106255570Strasz{ 107255570Strasz size_t len; 108255570Strasz 109255570Strasz kernel_receive(pdu); 110255570Strasz 111255570Strasz len = pdu_ahs_length(pdu); 112255570Strasz if (len > 0) 113255570Strasz log_errx(1, "protocol error: non-empty AHS"); 114255570Strasz 115255570Strasz len = pdu_data_segment_length(pdu); 116255570Strasz assert(len <= MAX_DATA_SEGMENT_LENGTH); 117255570Strasz pdu->pdu_data_len = len; 118255570Strasz} 119255570Strasz 120255570Straszvoid 121255570Straszpdu_send(struct pdu *pdu) 122255570Strasz{ 123255570Strasz 124255570Strasz pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 125255570Strasz kernel_send(pdu); 126255570Strasz} 127255570Strasz 128255570Strasz#else /* !ICL_KERNEL_PROXY */ 129255570Strasz 130255570Straszstatic size_t 131255570Straszpdu_padding(const struct pdu *pdu) 132255570Strasz{ 133255570Strasz 134255570Strasz if ((pdu->pdu_data_len % 4) != 0) 135255570Strasz return (4 - (pdu->pdu_data_len % 4)); 136255570Strasz 137255570Strasz return (0); 138255570Strasz} 139255570Strasz 140255570Straszstatic void 141255570Straszpdu_read(int fd, char *data, size_t len) 142255570Strasz{ 143255570Strasz ssize_t ret; 144255570Strasz 145255570Strasz while (len > 0) { 146255570Strasz ret = read(fd, data, len); 147255570Strasz if (ret < 0) { 148255570Strasz if (timed_out()) 149255570Strasz log_errx(1, "exiting due to timeout"); 150255570Strasz log_err(1, "read"); 151255570Strasz } else if (ret == 0) 152255570Strasz log_errx(1, "read: connection lost"); 153255570Strasz len -= ret; 154255570Strasz data += ret; 155255570Strasz } 156255570Strasz} 157255570Strasz 158255570Straszvoid 159255570Straszpdu_receive(struct pdu *pdu) 160255570Strasz{ 161255570Strasz size_t len, padding; 162255570Strasz char dummy[4]; 163255570Strasz 164255570Strasz pdu_read(pdu->pdu_connection->conn_socket, 165255570Strasz (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); 166255570Strasz 167255570Strasz len = pdu_ahs_length(pdu); 168255570Strasz if (len > 0) 169255570Strasz log_errx(1, "protocol error: non-empty AHS"); 170255570Strasz 171255570Strasz len = pdu_data_segment_length(pdu); 172255570Strasz if (len > 0) { 173255570Strasz if (len > MAX_DATA_SEGMENT_LENGTH) { 174255570Strasz log_errx(1, "protocol error: received PDU " 175255570Strasz "with DataSegmentLength exceeding %d", 176255570Strasz MAX_DATA_SEGMENT_LENGTH); 177255570Strasz } 178255570Strasz 179255570Strasz pdu->pdu_data_len = len; 180255570Strasz pdu->pdu_data = malloc(len); 181255570Strasz if (pdu->pdu_data == NULL) 182255570Strasz log_err(1, "malloc"); 183255570Strasz 184255570Strasz pdu_read(pdu->pdu_connection->conn_socket, 185255570Strasz (char *)pdu->pdu_data, pdu->pdu_data_len); 186255570Strasz 187255570Strasz padding = pdu_padding(pdu); 188255570Strasz if (padding != 0) { 189255570Strasz assert(padding < sizeof(dummy)); 190255570Strasz pdu_read(pdu->pdu_connection->conn_socket, 191255570Strasz (char *)dummy, padding); 192255570Strasz } 193255570Strasz } 194255570Strasz} 195255570Strasz 196255570Straszvoid 197255570Straszpdu_send(struct pdu *pdu) 198255570Strasz{ 199255570Strasz ssize_t ret, total_len; 200255570Strasz size_t padding; 201255570Strasz uint32_t zero = 0; 202255570Strasz struct iovec iov[3]; 203255570Strasz int iovcnt; 204255570Strasz 205255570Strasz pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 206255570Strasz iov[0].iov_base = pdu->pdu_bhs; 207255570Strasz iov[0].iov_len = sizeof(*pdu->pdu_bhs); 208255570Strasz total_len = iov[0].iov_len; 209255570Strasz iovcnt = 1; 210255570Strasz 211255570Strasz if (pdu->pdu_data_len > 0) { 212255570Strasz iov[1].iov_base = pdu->pdu_data; 213255570Strasz iov[1].iov_len = pdu->pdu_data_len; 214255570Strasz total_len += iov[1].iov_len; 215255570Strasz iovcnt = 2; 216255570Strasz 217255570Strasz padding = pdu_padding(pdu); 218255570Strasz if (padding > 0) { 219255570Strasz assert(padding < sizeof(zero)); 220255570Strasz iov[2].iov_base = &zero; 221255570Strasz iov[2].iov_len = padding; 222255570Strasz total_len += iov[2].iov_len; 223255570Strasz iovcnt = 3; 224255570Strasz } 225255570Strasz } 226255570Strasz 227255570Strasz ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); 228255570Strasz if (ret < 0) { 229255570Strasz if (timed_out()) 230255570Strasz log_errx(1, "exiting due to timeout"); 231255570Strasz log_err(1, "writev"); 232255570Strasz } 233255570Strasz if (ret != total_len) 234255570Strasz log_errx(1, "short write"); 235255570Strasz} 236255570Strasz 237255570Strasz#endif /* !ICL_KERNEL_PROXY */ 238255570Strasz 239255570Straszvoid 240255570Straszpdu_delete(struct pdu *pdu) 241255570Strasz{ 242255570Strasz 243255570Strasz free(pdu->pdu_data); 244255570Strasz free(pdu->pdu_bhs); 245255570Strasz free(pdu); 246255570Strasz} 247