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