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