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