xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_doorclt.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 }
411