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