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