1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2000 by Cisco Systems, Inc. All rights reserved.
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
27 */
28
29 /*
30 * iSCSI Software Initiator
31 */
32
33 #include <sys/types.h>
34 #include <sys/errno.h>
35 #include <sys/conf.h>
36 #include <sys/cmn_err.h>
37 #include <sys/stat.h>
38 #include <sys/pathname.h>
39 #include <sys/door.h>
40 #include <sys/kmem.h>
41 #include <sys/socket.h>
42 #include <sys/fs/snode.h>
43 #include <netinet/in.h>
44
45 #include <sys/scsi/adapters/iscsi_door.h>
46 #include "iscsi.h"
47
48 #define ISCSI_DOOR_MAX_SEMA_VALUE 16
49
50 static boolean_t iscsi_door_init = B_FALSE;
51 static ksema_t iscsi_door_sema;
52 static krwlock_t iscsi_door_lock;
53 static door_handle_t iscsi_door_handle;
54
55 typedef struct _mybuffer {
56 size_t signature;
57 size_t size;
58 } mybuffer_t;
59
60 /*
61 * iscsi_door_ini
62 *
63 * This function initializes the variables needed to handle the door upcall.
64 */
65 boolean_t
iscsi_door_ini(void)66 iscsi_door_ini(void)
67 {
68 ASSERT(!iscsi_door_init);
69 if (!iscsi_door_init) {
70 rw_init(
71 &iscsi_door_lock,
72 NULL,
73 RW_DRIVER,
74 NULL);
75
76 sema_init(
77 &iscsi_door_sema,
78 ISCSI_DOOR_MAX_SEMA_VALUE,
79 NULL,
80 SEMA_DRIVER,
81 NULL);
82
83 iscsi_door_handle = NULL;
84 iscsi_door_init = B_TRUE;
85 return (B_TRUE);
86 }
87 return (B_FALSE);
88 }
89
90 /*
91 * iscsi_door_term
92 *
93 * This function releases the resources allocated to handle the door
94 * upcall. It disconnects from the door if currently connected.
95 */
96 boolean_t
iscsi_door_term(void)97 iscsi_door_term(void)
98 {
99 ASSERT(iscsi_door_init);
100 if (iscsi_door_init) {
101 iscsi_door_init = B_FALSE;
102 iscsi_door_unbind();
103 rw_destroy(&iscsi_door_lock);
104 sema_destroy(&iscsi_door_sema);
105 return (B_TRUE);
106 }
107 return (B_FALSE);
108 }
109
110 /*
111 * iscsi_door_bind
112 *
113 * This function tries to connect the iscsi_door. If it succeeds
114 * it keeps the vnode.
115 */
116 boolean_t
iscsi_door_bind(int did)117 iscsi_door_bind(
118 int did
119 )
120 {
121 door_handle_t new_handle;
122
123 new_handle = door_ki_lookup(did);
124 if (new_handle == NULL) {
125 /* The lookup failed. */
126 return (B_FALSE);
127 }
128
129 /* The new handle is stored. If we had one, it is released. */
130 rw_enter(&iscsi_door_lock, RW_WRITER);
131 if (iscsi_door_handle != NULL) {
132 door_ki_rele(iscsi_door_handle);
133 }
134 iscsi_door_handle = new_handle;
135 rw_exit(&iscsi_door_lock);
136
137 return (B_TRUE);
138 }
139
140 /*
141 * iscsi_door_unbind
142 *
143 * This function releases the current door handle.
144 */
145 void
iscsi_door_unbind(void)146 iscsi_door_unbind(void)
147 {
148 rw_enter(&iscsi_door_lock, RW_WRITER);
149 if (iscsi_door_handle != NULL) {
150 door_ki_rele(iscsi_door_handle);
151 iscsi_door_handle = NULL;
152 }
153 rw_exit(&iscsi_door_lock);
154 }
155
156 /*
157 * iscsi_door_upcall
158 *
159 * This function tries to call the iscsi_door.
160 */
161 static
162 boolean_t
iscsi_door_upcall(door_arg_t * arg)163 iscsi_door_upcall(door_arg_t *arg)
164 {
165 int error;
166
167 /*
168 * This semaphore limits the number of simultaneous calls
169 * to the door.
170 */
171 sema_p(&iscsi_door_sema);
172 /*
173 * The mutex protecting the iscsi_door_handle is entered.
174 */
175 rw_enter(&iscsi_door_lock, RW_READER);
176
177 if (iscsi_door_handle == NULL) {
178 /* There's no door handle. */
179 rw_exit(&iscsi_door_lock);
180 sema_v(&iscsi_door_sema);
181 return (B_FALSE);
182 }
183 error = door_ki_upcall(iscsi_door_handle, arg);
184
185 rw_exit(&iscsi_door_lock);
186 sema_v(&iscsi_door_sema);
187
188 if (error != 0) {
189 return (B_FALSE);
190 } else {
191 return (B_TRUE);
192 }
193 }
194
195 /*
196 * kfreehostent
197 *
198 * This function frees the memory returned by kgetipnodebyname.
199 */
200 void
kfreehostent(struct hostent * hptr)201 kfreehostent(
202 struct hostent *hptr
203 )
204 {
205 mybuffer_t *buffer;
206
207 ASSERT(hptr != NULL);
208 if (hptr) {
209 buffer = (mybuffer_t *)((char *)hptr - sizeof (mybuffer_t));
210 ASSERT(buffer->signature == ISCSI_DOOR_REQ_SIGNATURE);
211 if (buffer->signature == ISCSI_DOOR_REQ_SIGNATURE) {
212 kmem_free((void *)buffer, buffer->size);
213 return;
214 }
215 }
216 /* A message should be logged here. */
217 }
218
219 /*
220 * kgetipnodebyname
221 *
222 * This function builds a request that will be sent to the iscsi_door.
223 * The iSCSI door after receiving the request calls getipnodebyaddr().
224 * for more information on the input, output parameter and return value,
225 * consult the man page for getipnodebyname().
226 *
227 * Before calling the iscsi door this function tries to do the conversion
228 * locally. If a name resolution is needed the iscsi door is called.
229 *
230 * There's some limitations to the information returned by this function.
231 * Only one address of the address list returned by getipnodebyname() is
232 * returned. The other parameters of the structure should be ignored.
233 */
234 struct hostent *
kgetipnodebyname(const char * name,int af,int flags,int * error_num)235 kgetipnodebyname(
236 const char *name,
237 int af,
238 int flags,
239 int *error_num
240 )
241 {
242 door_arg_t arg;
243 mybuffer_t *buffer;
244 size_t msg_size = ISCSI_DOOR_MAX_DATA_SIZE;
245 size_t hostent_size = ISCSI_DOOR_MAX_DATA_SIZE;
246 size_t buffer_size;
247 getipnodebyname_req_t *req;
248 getipnodebyname_cnf_t *cnf;
249 struct hostent *hptr;
250
251
252 buffer_size = msg_size + hostent_size + sizeof (mybuffer_t);
253 buffer = (mybuffer_t *)kmem_zalloc(buffer_size, KM_SLEEP);
254
255 if (buffer) {
256
257 /*
258 * The buffer was successfully allocated.
259 *
260 * Buffer
261 *
262 * +--------------------+ <--- buffer
263 * | mybuffer_t |
264 * +--------------------+ <--- hptr
265 * | |
266 * | |
267 * | hostent_size |
268 * | |
269 * | |
270 * | |
271 * +--------------------+ <--- req, cnf
272 * | |
273 * | |
274 * | |
275 * | msg_size |
276 * | |
277 * | |
278 * | |
279 * +--------------------+
280 */
281 buffer->signature = ISCSI_DOOR_REQ_SIGNATURE;
282 buffer->size = buffer_size;
283
284 hptr = (struct hostent *)((char *)buffer + sizeof (mybuffer_t));
285 req = (getipnodebyname_req_t *)((char *)hptr + hostent_size);
286 cnf = (getipnodebyname_cnf_t *)((char *)hptr + hostent_size);
287
288 hostent_size -= sizeof (struct hostent);
289
290 /*
291 * We try first locally. If the conversion cannot be done
292 * by inet_pton the door is called.
293 * The cnf address is used as output buffer.
294 * inet_pton returns '1' if the conversion was successful.
295 */
296 switch (af) {
297 case AF_INET:
298 hptr->h_length = sizeof (struct in_addr);
299 break;
300 case AF_INET6:
301 hptr->h_length = sizeof (struct in6_addr);
302 break;
303 default:
304 kfreehostent(hptr);
305 *error_num = NO_RECOVERY;
306 return (NULL);
307 }
308 if ((msg_size < hptr->h_length) ||
309 (hostent_size < sizeof (char *))) {
310 kfreehostent(hptr);
311 *error_num = NO_RECOVERY;
312 return (NULL);
313 }
314 if (inet_pton(af, (char *)name, cnf) == 1) {
315 /*
316 * inet_pton converted the string successfully.
317 */
318 hptr->h_addrtype = af;
319 hptr->h_addr_list = (char **)((char *)hptr +
320 sizeof (struct hostent));
321 *hptr->h_addr_list = (char *)cnf;
322 return (hptr);
323 }
324
325 /*
326 * The name couldn't ne converted by inet_pton. The door is
327 * called.
328 */
329
330 /* Header initialization. */
331 req->hdr.signature = ISCSI_DOOR_REQ_SIGNATURE;
332 req->hdr.version = ISCSI_DOOR_REQ_VERSION_1;
333 req->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_REQ;
334
335 /* Body initialization. */
336 req->name_length = strlen(name);
337 if (req->name_length >
338 (msg_size - sizeof (getipnodebyname_req_t) - 1)) {
339 kfreehostent(hptr);
340 *error_num = NO_RECOVERY;
341 return (NULL);
342 }
343
344 req->name_offset = sizeof (getipnodebyname_req_t);
345 req->af = af;
346 req->flags = flags;
347 bcopy(
348 name,
349 ((char *)req + req->name_offset),
350 req->name_length);
351
352 /* Door argument initialization. */
353 arg.data_ptr = (char *)req;
354 arg.data_size = msg_size;
355 arg.desc_num = 0;
356 arg.desc_ptr = NULL;
357 arg.rbuf = (char *)cnf;
358 arg.rsize = msg_size;
359
360 if (iscsi_door_upcall(&arg) == B_FALSE) {
361 /* The door call failed */
362 kfreehostent(hptr);
363 *error_num = NO_RECOVERY;
364 return (NULL);
365 }
366
367 /*
368 * The door call itself was successful. The value returned
369 * in arg.rbuf should be cnf, but we never know.
370 */
371 cnf = (getipnodebyname_cnf_t *)arg.rbuf;
372
373 if ((cnf == NULL) ||
374 (arg.rsize < sizeof (getipnodebyname_cnf_t)) ||
375 (cnf->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) ||
376 (cnf->hdr.version != ISCSI_DOOR_REQ_VERSION_1) ||
377 (cnf->hdr.opcode != ISCSI_DOOR_GETIPNODEBYNAME_CNF) ||
378 ((cnf->hdr.status != ISCSI_DOOR_STATUS_SUCCESS) &&
379 (cnf->hdr.status != ISCSI_DOOR_STATUS_MORE))) {
380 /* The door didn't like the request */
381 kfreehostent(hptr);
382 *error_num = NO_RECOVERY;
383 return (NULL);
384 }
385
386 if (cnf->h_addr_list_length == 0) {
387 kfreehostent(hptr);
388 *error_num = HOST_NOT_FOUND;
389 return (NULL);
390 }
391
392 hptr->h_addrtype = cnf->h_addrtype;
393 hptr->h_length = cnf->h_addrlen;
394 hptr->h_addr_list = (char **)((char *)hptr +
395 sizeof (struct hostent));
396 *hptr->h_addr_list = ((char *)cnf + cnf->h_addr_list_offset);
397 return (hptr);
398 } else {
399 *error_num = NO_RECOVERY;
400 return (NULL);
401 }
402 }
403