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 */
30255570Strasz
31270279Strasz#include <sys/cdefs.h>
32270279Strasz__FBSDID("$FreeBSD: releng/11.0/usr.sbin/ctld/pdu.c 281532 2015-04-14 18:13:55Z delphij $");
33270279Strasz
34255570Strasz#include <sys/types.h>
35255570Strasz#include <sys/uio.h>
36255570Strasz#include <assert.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
47264524Straszextern bool proxy_mode;
48264524Strasz
49255570Straszstatic int
50255570Straszpdu_ahs_length(const struct pdu *pdu)
51255570Strasz{
52255570Strasz
53255570Strasz	return (pdu->pdu_bhs->bhs_total_ahs_len * 4);
54255570Strasz}
55255570Strasz
56255570Straszstatic int
57255570Straszpdu_data_segment_length(const struct pdu *pdu)
58255570Strasz{
59255570Strasz	uint32_t len = 0;
60255570Strasz
61255570Strasz	len += pdu->pdu_bhs->bhs_data_segment_len[0];
62255570Strasz	len <<= 8;
63255570Strasz	len += pdu->pdu_bhs->bhs_data_segment_len[1];
64255570Strasz	len <<= 8;
65255570Strasz	len += pdu->pdu_bhs->bhs_data_segment_len[2];
66255570Strasz
67255570Strasz	return (len);
68255570Strasz}
69255570Strasz
70255570Straszstatic void
71255570Straszpdu_set_data_segment_length(struct pdu *pdu, uint32_t len)
72255570Strasz{
73255570Strasz
74255570Strasz	pdu->pdu_bhs->bhs_data_segment_len[2] = len;
75255570Strasz	pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8;
76255570Strasz	pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16;
77255570Strasz}
78255570Strasz
79255570Straszstruct pdu *
80255570Straszpdu_new(struct connection *conn)
81255570Strasz{
82255570Strasz	struct pdu *pdu;
83255570Strasz
84255570Strasz	pdu = calloc(sizeof(*pdu), 1);
85255570Strasz	if (pdu == NULL)
86255570Strasz		log_err(1, "calloc");
87255570Strasz
88255570Strasz	pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1);
89255570Strasz	if (pdu->pdu_bhs == NULL)
90255570Strasz		log_err(1, "calloc");
91255570Strasz
92255570Strasz	pdu->pdu_connection = conn;
93255570Strasz
94255570Strasz	return (pdu);
95255570Strasz}
96255570Strasz
97255570Straszstruct pdu *
98255570Straszpdu_new_response(struct pdu *request)
99255570Strasz{
100255570Strasz
101255570Strasz	return (pdu_new(request->pdu_connection));
102255570Strasz}
103255570Strasz
104255570Strasz#ifdef ICL_KERNEL_PROXY
105255570Strasz
106264524Straszstatic void
107264524Straszpdu_receive_proxy(struct pdu *pdu)
108255570Strasz{
109255570Strasz	size_t len;
110255570Strasz
111264524Strasz	assert(proxy_mode);
112264524Strasz
113255570Strasz	kernel_receive(pdu);
114255570Strasz
115255570Strasz	len = pdu_ahs_length(pdu);
116255570Strasz	if (len > 0)
117255570Strasz		log_errx(1, "protocol error: non-empty AHS");
118255570Strasz
119255570Strasz	len = pdu_data_segment_length(pdu);
120255570Strasz	assert(len <= MAX_DATA_SEGMENT_LENGTH);
121255570Strasz	pdu->pdu_data_len = len;
122255570Strasz}
123255570Strasz
124264524Straszstatic void
125264524Straszpdu_send_proxy(struct pdu *pdu)
126255570Strasz{
127255570Strasz
128264524Strasz	assert(proxy_mode);
129264524Strasz
130255570Strasz	pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
131255570Strasz	kernel_send(pdu);
132255570Strasz}
133255570Strasz
134264524Strasz#endif /* ICL_KERNEL_PROXY */
135255570Strasz
136255570Straszstatic size_t
137255570Straszpdu_padding(const struct pdu *pdu)
138255570Strasz{
139255570Strasz
140255570Strasz	if ((pdu->pdu_data_len % 4) != 0)
141255570Strasz		return (4 - (pdu->pdu_data_len % 4));
142255570Strasz
143255570Strasz	return (0);
144255570Strasz}
145255570Strasz
146255570Straszstatic void
147255570Straszpdu_read(int fd, char *data, size_t len)
148255570Strasz{
149255570Strasz	ssize_t ret;
150255570Strasz
151255570Strasz	while (len > 0) {
152255570Strasz		ret = read(fd, data, len);
153255570Strasz		if (ret < 0) {
154255570Strasz			if (timed_out())
155255570Strasz				log_errx(1, "exiting due to timeout");
156255570Strasz			log_err(1, "read");
157255570Strasz		} else if (ret == 0)
158255570Strasz			log_errx(1, "read: connection lost");
159255570Strasz		len -= ret;
160255570Strasz		data += ret;
161255570Strasz	}
162255570Strasz}
163255570Strasz
164255570Straszvoid
165255570Straszpdu_receive(struct pdu *pdu)
166255570Strasz{
167255570Strasz	size_t len, padding;
168255570Strasz	char dummy[4];
169255570Strasz
170264524Strasz#ifdef ICL_KERNEL_PROXY
171264524Strasz	if (proxy_mode)
172264524Strasz		return (pdu_receive_proxy(pdu));
173264524Strasz#endif
174264524Strasz
175264524Strasz	assert(proxy_mode == false);
176264524Strasz
177255570Strasz	pdu_read(pdu->pdu_connection->conn_socket,
178255570Strasz	    (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs));
179255570Strasz
180255570Strasz	len = pdu_ahs_length(pdu);
181255570Strasz	if (len > 0)
182255570Strasz		log_errx(1, "protocol error: non-empty AHS");
183255570Strasz
184255570Strasz	len = pdu_data_segment_length(pdu);
185255570Strasz	if (len > 0) {
186255570Strasz		if (len > MAX_DATA_SEGMENT_LENGTH) {
187255570Strasz			log_errx(1, "protocol error: received PDU "
188255570Strasz			    "with DataSegmentLength exceeding %d",
189255570Strasz			    MAX_DATA_SEGMENT_LENGTH);
190255570Strasz		}
191255570Strasz
192255570Strasz		pdu->pdu_data_len = len;
193255570Strasz		pdu->pdu_data = malloc(len);
194255570Strasz		if (pdu->pdu_data == NULL)
195255570Strasz			log_err(1, "malloc");
196255570Strasz
197255570Strasz		pdu_read(pdu->pdu_connection->conn_socket,
198255570Strasz		    (char *)pdu->pdu_data, pdu->pdu_data_len);
199255570Strasz
200255570Strasz		padding = pdu_padding(pdu);
201255570Strasz		if (padding != 0) {
202255570Strasz			assert(padding < sizeof(dummy));
203255570Strasz			pdu_read(pdu->pdu_connection->conn_socket,
204255570Strasz			    (char *)dummy, padding);
205255570Strasz		}
206255570Strasz	}
207255570Strasz}
208255570Strasz
209255570Straszvoid
210255570Straszpdu_send(struct pdu *pdu)
211255570Strasz{
212255570Strasz	ssize_t ret, total_len;
213255570Strasz	size_t padding;
214255570Strasz	uint32_t zero = 0;
215255570Strasz	struct iovec iov[3];
216255570Strasz	int iovcnt;
217255570Strasz
218264524Strasz#ifdef ICL_KERNEL_PROXY
219264524Strasz	if (proxy_mode)
220264524Strasz		return (pdu_send_proxy(pdu));
221264524Strasz#endif
222264524Strasz
223264524Strasz	assert(proxy_mode == false);
224264524Strasz
225255570Strasz	pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
226255570Strasz	iov[0].iov_base = pdu->pdu_bhs;
227255570Strasz	iov[0].iov_len = sizeof(*pdu->pdu_bhs);
228255570Strasz	total_len = iov[0].iov_len;
229255570Strasz	iovcnt = 1;
230255570Strasz
231255570Strasz	if (pdu->pdu_data_len > 0) {
232255570Strasz		iov[1].iov_base = pdu->pdu_data;
233255570Strasz		iov[1].iov_len = pdu->pdu_data_len;
234255570Strasz		total_len += iov[1].iov_len;
235255570Strasz		iovcnt = 2;
236255570Strasz
237255570Strasz		padding = pdu_padding(pdu);
238255570Strasz		if (padding > 0) {
239255570Strasz			assert(padding < sizeof(zero));
240255570Strasz			iov[2].iov_base = &zero;
241255570Strasz			iov[2].iov_len = padding;
242255570Strasz			total_len += iov[2].iov_len;
243255570Strasz			iovcnt = 3;
244255570Strasz		}
245255570Strasz	}
246255570Strasz
247255570Strasz	ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt);
248255570Strasz	if (ret < 0) {
249255570Strasz		if (timed_out())
250255570Strasz			log_errx(1, "exiting due to timeout");
251255570Strasz		log_err(1, "writev");
252255570Strasz	}
253255570Strasz	if (ret != total_len)
254255570Strasz		log_errx(1, "short write");
255255570Strasz}
256255570Strasz
257255570Straszvoid
258255570Straszpdu_delete(struct pdu *pdu)
259255570Strasz{
260255570Strasz
261255570Strasz	free(pdu->pdu_data);
262255570Strasz	free(pdu->pdu_bhs);
263255570Strasz	free(pdu);
264255570Strasz}
265