]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/iscsid/pdu.c
Followup to r347996
[FreeBSD/FreeBSD.git] / usr.sbin / iscsid / pdu.c
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
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
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
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 *
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 *
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
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
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
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
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
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
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
304 pdu_delete(struct pdu *pdu)
305 {
306
307         free(pdu->pdu_data);
308         free(pdu->pdu_bhs);
309         free(pdu);
310 }