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: stable/12/usr.sbin/ctld/pdu.c 326276 2017-11-27 15:37:16Z pfg $");
35
36 #include <sys/types.h>
37 #include <sys/uio.h>
38 #include <assert.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41
42 #include "ctld.h"
43 #include "iscsi_proto.h"
44
45 #ifdef ICL_KERNEL_PROXY
46 #include <sys/ioctl.h>
47 #endif
48
49 extern bool proxy_mode;
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 size_t len;
113
114 assert(proxy_mode);
115 conn = pdu->pdu_connection;
116
117 kernel_receive(pdu);
118
119 len = pdu_ahs_length(pdu);
120 if (len > 0)
121 log_errx(1, "protocol error: non-empty AHS");
122
123 len = pdu_data_segment_length(pdu);
124 assert(len <= (size_t)conn->conn_max_recv_data_segment_length);
125 pdu->pdu_data_len = len;
126 }
127
128 static void
pdu_send_proxy(struct pdu * pdu)129 pdu_send_proxy(struct pdu *pdu)
130 {
131
132 assert(proxy_mode);
133
134 pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
135 kernel_send(pdu);
136 }
137
138 #endif /* ICL_KERNEL_PROXY */
139
140 static size_t
pdu_padding(const struct pdu * pdu)141 pdu_padding(const struct pdu *pdu)
142 {
143
144 if ((pdu->pdu_data_len % 4) != 0)
145 return (4 - (pdu->pdu_data_len % 4));
146
147 return (0);
148 }
149
150 static void
pdu_read(int fd,char * data,size_t len)151 pdu_read(int fd, char *data, size_t len)
152 {
153 ssize_t ret;
154
155 while (len > 0) {
156 ret = read(fd, data, len);
157 if (ret < 0) {
158 if (timed_out())
159 log_errx(1, "exiting due to timeout");
160 log_err(1, "read");
161 } else if (ret == 0)
162 log_errx(1, "read: connection lost");
163 len -= ret;
164 data += ret;
165 }
166 }
167
168 void
pdu_receive(struct pdu * pdu)169 pdu_receive(struct pdu *pdu)
170 {
171 struct connection *conn;
172 size_t len, padding;
173 char dummy[4];
174
175 #ifdef ICL_KERNEL_PROXY
176 if (proxy_mode)
177 return (pdu_receive_proxy(pdu));
178 #endif
179
180 assert(proxy_mode == false);
181 conn = pdu->pdu_connection;
182
183 pdu_read(conn->conn_socket, (char *)pdu->pdu_bhs,
184 sizeof(*pdu->pdu_bhs));
185
186 len = pdu_ahs_length(pdu);
187 if (len > 0)
188 log_errx(1, "protocol error: non-empty AHS");
189
190 len = pdu_data_segment_length(pdu);
191 if (len > 0) {
192 if (len > (size_t)conn->conn_max_recv_data_segment_length) {
193 log_errx(1, "protocol error: received PDU "
194 "with DataSegmentLength exceeding %d",
195 conn->conn_max_recv_data_segment_length);
196 }
197
198 pdu->pdu_data_len = len;
199 pdu->pdu_data = malloc(len);
200 if (pdu->pdu_data == NULL)
201 log_err(1, "malloc");
202
203 pdu_read(conn->conn_socket, (char *)pdu->pdu_data,
204 pdu->pdu_data_len);
205
206 padding = pdu_padding(pdu);
207 if (padding != 0) {
208 assert(padding < sizeof(dummy));
209 pdu_read(conn->conn_socket, (char *)dummy, padding);
210 }
211 }
212 }
213
214 void
pdu_send(struct pdu * pdu)215 pdu_send(struct pdu *pdu)
216 {
217 ssize_t ret, total_len;
218 size_t padding;
219 uint32_t zero = 0;
220 struct iovec iov[3];
221 int iovcnt;
222
223 #ifdef ICL_KERNEL_PROXY
224 if (proxy_mode)
225 return (pdu_send_proxy(pdu));
226 #endif
227
228 assert(proxy_mode == false);
229
230 pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
231 iov[0].iov_base = pdu->pdu_bhs;
232 iov[0].iov_len = sizeof(*pdu->pdu_bhs);
233 total_len = iov[0].iov_len;
234 iovcnt = 1;
235
236 if (pdu->pdu_data_len > 0) {
237 iov[1].iov_base = pdu->pdu_data;
238 iov[1].iov_len = pdu->pdu_data_len;
239 total_len += iov[1].iov_len;
240 iovcnt = 2;
241
242 padding = pdu_padding(pdu);
243 if (padding > 0) {
244 assert(padding < sizeof(zero));
245 iov[2].iov_base = &zero;
246 iov[2].iov_len = padding;
247 total_len += iov[2].iov_len;
248 iovcnt = 3;
249 }
250 }
251
252 ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt);
253 if (ret < 0) {
254 if (timed_out())
255 log_errx(1, "exiting due to timeout");
256 log_err(1, "writev");
257 }
258 if (ret != total_len)
259 log_errx(1, "short write");
260 }
261
262 void
pdu_delete(struct pdu * pdu)263 pdu_delete(struct pdu *pdu)
264 {
265
266 free(pdu->pdu_data);
267 free(pdu->pdu_bhs);
268 free(pdu);
269 }
270