]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ctld/pdu.c
Use asprintf instead of sbuf
[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  * 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 <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
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         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
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
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
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
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
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
263 pdu_delete(struct pdu *pdu)
264 {
265
266         free(pdu->pdu_data);
267         free(pdu->pdu_bhs);
268         free(pdu);
269 }