]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/iscsid/pdu.c
MFC r349376: Fix strsep_quote() on strings without quotes.
[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 iscsi_daemon_receive *idr;
112         size_t len;
113         int error;
114
115         assert(pdu->pdu_connection->conn_conf.isc_iser != 0);
116
117         pdu->pdu_data = malloc(ISCSI_MAX_DATA_SEGMENT_LENGTH);
118         if (pdu->pdu_data == NULL)
119                 log_err(1, "malloc");
120
121         idr = calloc(1, sizeof(*idr));
122         if (idr == NULL)
123                 log_err(1, "calloc");
124
125         idr->idr_session_id = pdu->pdu_connection->conn_session_id;
126         idr->idr_bhs = pdu->pdu_bhs;
127         idr->idr_data_segment_len = ISCSI_MAX_DATA_SEGMENT_LENGTH;
128         idr->idr_data_segment = pdu->pdu_data;
129
130         error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDRECEIVE, idr);
131         if (error != 0)
132                 log_err(1, "ISCSIDRECEIVE");
133
134         len = pdu_ahs_length(pdu);
135         if (len > 0)
136                 log_errx(1, "protocol error: non-empty AHS");
137
138         len = pdu_data_segment_length(pdu);
139         assert(len <= ISCSI_MAX_DATA_SEGMENT_LENGTH);
140         pdu->pdu_data_len = len;
141
142         free(idr);
143 }
144
145 static void
146 pdu_send_proxy(struct pdu *pdu)
147 {
148         struct iscsi_daemon_send *ids;
149         int error;
150
151         assert(pdu->pdu_connection->conn_conf.isc_iser != 0);
152
153         pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
154
155         ids = calloc(1, sizeof(*ids));
156         if (ids == NULL)
157                 log_err(1, "calloc");
158
159         ids->ids_session_id = pdu->pdu_connection->conn_session_id;
160         ids->ids_bhs = pdu->pdu_bhs;
161         ids->ids_data_segment_len = pdu->pdu_data_len;
162         ids->ids_data_segment = pdu->pdu_data;
163
164         error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDSEND, ids);
165         if (error != 0)
166                 log_err(1, "ISCSIDSEND");
167
168         free(ids);
169 }
170
171 #endif /* ICL_KERNEL_PROXY */
172
173 static size_t
174 pdu_padding(const struct pdu *pdu)
175 {
176
177         if ((pdu->pdu_data_len % 4) != 0)
178                 return (4 - (pdu->pdu_data_len % 4));
179
180         return (0);
181 }
182
183 static void
184 pdu_read(const struct connection *conn, char *data, size_t len)
185 {
186         ssize_t ret;
187
188         while (len > 0) {
189                 ret = read(conn->conn_socket, data, len);
190                 if (ret < 0) {
191                         if (timed_out()) {
192                                 fail(conn, "Login Phase timeout");
193                                 log_errx(1, "exiting due to timeout");
194                         }
195                         fail(conn, strerror(errno));
196                         log_err(1, "read");
197                 } else if (ret == 0) {
198                         fail(conn, "connection lost");
199                         log_errx(1, "read: connection lost");
200                 }
201                 len -= ret;
202                 data += ret;
203         }
204 }
205
206 void
207 pdu_receive(struct pdu *pdu)
208 {
209         size_t len, padding;
210         char dummy[4];
211
212 #ifdef ICL_KERNEL_PROXY
213         if (pdu->pdu_connection->conn_conf.isc_iser != 0)
214                 return (pdu_receive_proxy(pdu));
215 #endif
216
217         assert(pdu->pdu_connection->conn_conf.isc_iser == 0);
218
219         pdu_read(pdu->pdu_connection,
220             (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs));
221
222         len = pdu_ahs_length(pdu);
223         if (len > 0)
224                 log_errx(1, "protocol error: non-empty AHS");
225
226         len = pdu_data_segment_length(pdu);
227         if (len > 0) {
228                 if (len > ISCSI_MAX_DATA_SEGMENT_LENGTH) {
229                         log_errx(1, "protocol error: received PDU "
230                             "with DataSegmentLength exceeding %d",
231                             ISCSI_MAX_DATA_SEGMENT_LENGTH);
232                 }
233
234                 pdu->pdu_data_len = len;
235                 pdu->pdu_data = malloc(len);
236                 if (pdu->pdu_data == NULL)
237                         log_err(1, "malloc");
238
239                 pdu_read(pdu->pdu_connection,
240                     (char *)pdu->pdu_data, pdu->pdu_data_len);
241
242                 padding = pdu_padding(pdu);
243                 if (padding != 0) {
244                         assert(padding < sizeof(dummy));
245                         pdu_read(pdu->pdu_connection,
246                             (char *)dummy, padding);
247                 }
248         }
249 }
250
251 void
252 pdu_send(struct pdu *pdu)
253 {
254         ssize_t ret, total_len;
255         size_t padding;
256         uint32_t zero = 0;
257         struct iovec iov[3];
258         int iovcnt;
259
260 #ifdef ICL_KERNEL_PROXY
261         if (pdu->pdu_connection->conn_conf.isc_iser != 0)
262                 return (pdu_send_proxy(pdu));
263 #endif
264
265         assert(pdu->pdu_connection->conn_conf.isc_iser == 0);
266
267         pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
268         iov[0].iov_base = pdu->pdu_bhs;
269         iov[0].iov_len = sizeof(*pdu->pdu_bhs);
270         total_len = iov[0].iov_len;
271         iovcnt = 1;
272
273         if (pdu->pdu_data_len > 0) {
274                 iov[1].iov_base = pdu->pdu_data;
275                 iov[1].iov_len = pdu->pdu_data_len;
276                 total_len += iov[1].iov_len;
277                 iovcnt = 2;
278
279                 padding = pdu_padding(pdu);
280                 if (padding > 0) {
281                         assert(padding < sizeof(zero));
282                         iov[2].iov_base = &zero;
283                         iov[2].iov_len = padding;
284                         total_len += iov[2].iov_len;
285                         iovcnt = 3;
286                 }
287         }
288
289         ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt);
290         if (ret < 0) {
291                 if (timed_out())
292                         log_errx(1, "exiting due to timeout");
293                 log_err(1, "writev");
294         }
295         if (ret != total_len)
296                 log_errx(1, "short write");
297 }
298
299 void
300 pdu_delete(struct pdu *pdu)
301 {
302
303         free(pdu->pdu_data);
304         free(pdu->pdu_bhs);
305         free(pdu);
306 }