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