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