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