xref: /freebsd-12.1/usr.sbin/iscsid/pdu.c (revision 1de7b4b8)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2012 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Edward Tomasz Napierala under sponsorship
8  * from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/types.h>
37 #include <sys/uio.h>
38 #include <assert.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #include "iscsid.h"
45 #include "iscsi_proto.h"
46 
47 #ifdef ICL_KERNEL_PROXY
48 #include <sys/ioctl.h>
49 #endif
50 
51 static int
pdu_ahs_length(const struct pdu * pdu)52 pdu_ahs_length(const struct pdu *pdu)
53 {
54 
55 	return (pdu->pdu_bhs->bhs_total_ahs_len * 4);
56 }
57 
58 static int
pdu_data_segment_length(const struct pdu * pdu)59 pdu_data_segment_length(const struct pdu *pdu)
60 {
61 	uint32_t len = 0;
62 
63 	len += pdu->pdu_bhs->bhs_data_segment_len[0];
64 	len <<= 8;
65 	len += pdu->pdu_bhs->bhs_data_segment_len[1];
66 	len <<= 8;
67 	len += pdu->pdu_bhs->bhs_data_segment_len[2];
68 
69 	return (len);
70 }
71 
72 static void
pdu_set_data_segment_length(struct pdu * pdu,uint32_t len)73 pdu_set_data_segment_length(struct pdu *pdu, uint32_t len)
74 {
75 
76 	pdu->pdu_bhs->bhs_data_segment_len[2] = len;
77 	pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8;
78 	pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16;
79 }
80 
81 struct pdu *
pdu_new(struct connection * conn)82 pdu_new(struct connection *conn)
83 {
84 	struct pdu *pdu;
85 
86 	pdu = calloc(1, sizeof(*pdu));
87 	if (pdu == NULL)
88 		log_err(1, "calloc");
89 
90 	pdu->pdu_bhs = calloc(1, sizeof(*pdu->pdu_bhs));
91 	if (pdu->pdu_bhs == NULL)
92 		log_err(1, "calloc");
93 
94 	pdu->pdu_connection = conn;
95 
96 	return (pdu);
97 }
98 
99 struct pdu *
pdu_new_response(struct pdu * request)100 pdu_new_response(struct pdu *request)
101 {
102 
103 	return (pdu_new(request->pdu_connection));
104 }
105 
106 #ifdef ICL_KERNEL_PROXY
107 
108 static void
pdu_receive_proxy(struct pdu * pdu)109 pdu_receive_proxy(struct pdu *pdu)
110 {
111 	struct connection *conn;
112 	struct iscsi_daemon_receive *idr;
113 	size_t len;
114 	int error;
115 
116 	conn = pdu->pdu_connection;
117 	assert(conn->conn_conf.isc_iser != 0);
118 
119 	pdu->pdu_data = malloc(conn->conn_max_recv_data_segment_length);
120 	if (pdu->pdu_data == NULL)
121 		log_err(1, "malloc");
122 
123 	idr = calloc(1, sizeof(*idr));
124 	if (idr == NULL)
125 		log_err(1, "calloc");
126 
127 	idr->idr_session_id = conn->conn_session_id;
128 	idr->idr_bhs = pdu->pdu_bhs;
129 	idr->idr_data_segment_len = conn->conn_max_recv_data_segment_length;
130 	idr->idr_data_segment = pdu->pdu_data;
131 
132 	error = ioctl(conn->conn_iscsi_fd, ISCSIDRECEIVE, idr);
133 	if (error != 0)
134 		log_err(1, "ISCSIDRECEIVE");
135 
136 	len = pdu_ahs_length(pdu);
137 	if (len > 0)
138 		log_errx(1, "protocol error: non-empty AHS");
139 
140 	len = pdu_data_segment_length(pdu);
141 	assert(len <= (size_t)conn->conn_max_recv_data_segment_length);
142 	pdu->pdu_data_len = len;
143 
144 	free(idr);
145 }
146 
147 static void
pdu_send_proxy(struct pdu * pdu)148 pdu_send_proxy(struct pdu *pdu)
149 {
150 	struct connection *conn;
151 	struct iscsi_daemon_send *ids;
152 	int error;
153 
154 	conn = pdu->pdu_connection;
155 	assert(conn->conn_conf.isc_iser != 0);
156 
157 	pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
158 
159 	ids = calloc(1, sizeof(*ids));
160 	if (ids == NULL)
161 		log_err(1, "calloc");
162 
163 	ids->ids_session_id = conn->conn_session_id;
164 	ids->ids_bhs = pdu->pdu_bhs;
165 	ids->ids_data_segment_len = pdu->pdu_data_len;
166 	ids->ids_data_segment = pdu->pdu_data;
167 
168 	error = ioctl(conn->conn_iscsi_fd, ISCSIDSEND, ids);
169 	if (error != 0)
170 		log_err(1, "ISCSIDSEND");
171 
172 	free(ids);
173 }
174 
175 #endif /* ICL_KERNEL_PROXY */
176 
177 static size_t
pdu_padding(const struct pdu * pdu)178 pdu_padding(const struct pdu *pdu)
179 {
180 
181 	if ((pdu->pdu_data_len % 4) != 0)
182 		return (4 - (pdu->pdu_data_len % 4));
183 
184 	return (0);
185 }
186 
187 static void
pdu_read(const struct connection * conn,char * data,size_t len)188 pdu_read(const struct connection *conn, char *data, size_t len)
189 {
190 	ssize_t ret;
191 
192 	while (len > 0) {
193 		ret = read(conn->conn_socket, data, len);
194 		if (ret < 0) {
195 			if (timed_out()) {
196 				fail(conn, "Login Phase timeout");
197 				log_errx(1, "exiting due to timeout");
198 			}
199 			fail(conn, strerror(errno));
200 			log_err(1, "read");
201 		} else if (ret == 0) {
202 			fail(conn, "connection lost");
203 			log_errx(1, "read: connection lost");
204 		}
205 		len -= ret;
206 		data += ret;
207 	}
208 }
209 
210 void
pdu_receive(struct pdu * pdu)211 pdu_receive(struct pdu *pdu)
212 {
213 	struct connection *conn;
214 	size_t len, padding;
215 	char dummy[4];
216 
217 	conn = pdu->pdu_connection;
218 #ifdef ICL_KERNEL_PROXY
219 	if (conn->conn_conf.isc_iser != 0)
220 		return (pdu_receive_proxy(pdu));
221 #endif
222 	assert(conn->conn_conf.isc_iser == 0);
223 
224 	pdu_read(conn, (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs));
225 
226 	len = pdu_ahs_length(pdu);
227 	if (len > 0)
228 		log_errx(1, "protocol error: non-empty AHS");
229 
230 	len = pdu_data_segment_length(pdu);
231 	if (len > 0) {
232 		if (len > (size_t)conn->conn_max_recv_data_segment_length) {
233 			log_errx(1, "protocol error: received PDU "
234 			    "with DataSegmentLength exceeding %d",
235 			    conn->conn_max_recv_data_segment_length);
236 		}
237 
238 		pdu->pdu_data_len = len;
239 		pdu->pdu_data = malloc(len);
240 		if (pdu->pdu_data == NULL)
241 			log_err(1, "malloc");
242 
243 		pdu_read(conn, (char *)pdu->pdu_data, pdu->pdu_data_len);
244 
245 		padding = pdu_padding(pdu);
246 		if (padding != 0) {
247 			assert(padding < sizeof(dummy));
248 			pdu_read(conn, (char *)dummy, padding);
249 		}
250 	}
251 }
252 
253 void
pdu_send(struct pdu * pdu)254 pdu_send(struct pdu *pdu)
255 {
256 	struct connection *conn;
257 	ssize_t ret, total_len;
258 	size_t padding;
259 	uint32_t zero = 0;
260 	struct iovec iov[3];
261 	int iovcnt;
262 
263 	conn = pdu->pdu_connection;
264 #ifdef ICL_KERNEL_PROXY
265 	if (conn->conn_conf.isc_iser != 0)
266 		return (pdu_send_proxy(pdu));
267 #endif
268 
269 	assert(conn->conn_conf.isc_iser == 0);
270 
271 	pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
272 	iov[0].iov_base = pdu->pdu_bhs;
273 	iov[0].iov_len = sizeof(*pdu->pdu_bhs);
274 	total_len = iov[0].iov_len;
275 	iovcnt = 1;
276 
277 	if (pdu->pdu_data_len > 0) {
278 		iov[1].iov_base = pdu->pdu_data;
279 		iov[1].iov_len = pdu->pdu_data_len;
280 		total_len += iov[1].iov_len;
281 		iovcnt = 2;
282 
283 		padding = pdu_padding(pdu);
284 		if (padding > 0) {
285 			assert(padding < sizeof(zero));
286 			iov[2].iov_base = &zero;
287 			iov[2].iov_len = padding;
288 			total_len += iov[2].iov_len;
289 			iovcnt = 3;
290 		}
291 	}
292 
293 	ret = writev(conn->conn_socket, iov, iovcnt);
294 	if (ret < 0) {
295 		if (timed_out())
296 			log_errx(1, "exiting due to timeout");
297 		log_err(1, "writev");
298 	}
299 	if (ret != total_len)
300 		log_errx(1, "short write");
301 }
302 
303 void
pdu_delete(struct pdu * pdu)304 pdu_delete(struct pdu *pdu)
305 {
306 
307 	free(pdu->pdu_data);
308 	free(pdu->pdu_bhs);
309 	free(pdu);
310 }
311