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