xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_doorclt.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 2008 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 static
57 void
58 iscsi_door_unbind(void);
59 
60 /*
61  * iscsi_door_ini
62  *
63  * This function initializes the variables needed to handle the door upcall.
64  */
65 boolean_t
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
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
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 static
146 void
147 iscsi_door_unbind(void)
148 {
149 	rw_enter(&iscsi_door_lock, RW_WRITER);
150 	if (iscsi_door_handle != NULL) {
151 		door_ki_rele(iscsi_door_handle);
152 		iscsi_door_handle = NULL;
153 	}
154 	rw_exit(&iscsi_door_lock);
155 }
156 
157 /*
158  * iscsi_door_upcall
159  *
160  * This function tries to call the iscsi_door.
161  */
162 static
163 boolean_t
164 iscsi_door_upcall(door_arg_t *arg)
165 {
166 	int	error;
167 
168 	/*
169 	 * This semaphore limits the number of simultaneous calls
170 	 * to the door.
171 	 */
172 	sema_p(&iscsi_door_sema);
173 	/*
174 	 * The mutex protecting the iscsi_door_handle is entered.
175 	 */
176 	rw_enter(&iscsi_door_lock, RW_READER);
177 
178 	if (iscsi_door_handle == NULL) {
179 		/* There's no door handle. */
180 		rw_exit(&iscsi_door_lock);
181 		sema_v(&iscsi_door_sema);
182 		return (B_FALSE);
183 	}
184 	error = door_ki_upcall(iscsi_door_handle, arg);
185 
186 	rw_exit(&iscsi_door_lock);
187 	sema_v(&iscsi_door_sema);
188 
189 	if (error != 0) {
190 		return (B_FALSE);
191 	} else {
192 		return (B_TRUE);
193 	}
194 }
195 
196 /*
197  * kfreehostent
198  *
199  * This function frees the memory returned by kgetipnodebyname.
200  */
201 void
202 kfreehostent(
203 	struct hostent		*hptr
204 )
205 {
206 	mybuffer_t		*buffer;
207 
208 	ASSERT(hptr != NULL);
209 	if (hptr) {
210 		buffer = (mybuffer_t *)((char *)hptr - sizeof (mybuffer_t));
211 		ASSERT(buffer->signature == ISCSI_DOOR_REQ_SIGNATURE);
212 		if (buffer->signature == ISCSI_DOOR_REQ_SIGNATURE) {
213 			kmem_free((void *)buffer, buffer->size);
214 			return;
215 		}
216 	}
217 	/* A message should be logged here. */
218 }
219 
220 /*
221  * kgetipnodebyname
222  *
223  * This function builds a request that will be sent to the iscsi_door.
224  * The iSCSI door after receiving the request calls getipnodebyaddr().
225  * for more information on the input, output parameter and return value,
226  * consult the man page for getipnodebyname().
227  *
228  * Before calling the iscsi door this function tries to do the conversion
229  * locally.  If a name resolution is needed the iscsi door is called.
230  *
231  * There's some limitations to the information returned by this function.
232  * Only one address of the address list returned by getipnodebyname() is
233  * returned.  The other parameters of the structure should be ignored.
234  */
235 struct hostent *
236 kgetipnodebyname(
237 	const char	*name,
238 	int		af,
239 	int		flags,
240 	int		*error_num
241 )
242 {
243 	door_arg_t		arg;
244 	mybuffer_t		*buffer;
245 	size_t			msg_size = ISCSI_DOOR_MAX_DATA_SIZE;
246 	size_t			hostent_size = ISCSI_DOOR_MAX_DATA_SIZE;
247 	size_t			buffer_size;
248 	getipnodebyname_req_t	*req;
249 	getipnodebyname_cnf_t	*cnf;
250 	struct hostent		*hptr;
251 	int			i;
252 	uint16_t		*swap;
253 
254 
255 	buffer_size = msg_size + hostent_size + sizeof (mybuffer_t);
256 	buffer = (mybuffer_t *)kmem_zalloc(buffer_size, KM_SLEEP);
257 
258 	if (buffer) {
259 
260 		/*
261 		 * The buffer was successfully allocated.
262 		 *
263 		 *	  Buffer
264 		 *
265 		 * +--------------------+ <--- buffer
266 		 * |	mybuffer_t	|
267 		 * +--------------------+ <--- hptr
268 		 * |			|
269 		 * |			|
270 		 * |	hostent_size	|
271 		 * |			|
272 		 * |			|
273 		 * |			|
274 		 * +--------------------+ <--- req, cnf
275 		 * |			|
276 		 * |			|
277 		 * |			|
278 		 * |	msg_size	|
279 		 * |			|
280 		 * |			|
281 		 * |			|
282 		 * +--------------------+
283 		 */
284 		buffer->signature = ISCSI_DOOR_REQ_SIGNATURE;
285 		buffer->size = buffer_size;
286 
287 		hptr = (struct hostent *)((char *)buffer + sizeof (mybuffer_t));
288 		req = (getipnodebyname_req_t *)((char *)hptr + hostent_size);
289 		cnf = (getipnodebyname_cnf_t *)((char *)hptr + hostent_size);
290 
291 		hostent_size -= sizeof (struct hostent);
292 
293 		/*
294 		 * We try first locally.  If the conversion cannot be done
295 		 * by inet_pton the door is called.
296 		 * The cnf address is used as output buffer.
297 		 * inet_pton returns '1' if the conversion was successful.
298 		 */
299 		switch (af) {
300 		case AF_INET:
301 			hptr->h_length = sizeof (struct in_addr);
302 			break;
303 		case AF_INET6:
304 			hptr->h_length = sizeof (struct in6_addr);
305 			break;
306 		default:
307 			kfreehostent(hptr);
308 			*error_num = NO_RECOVERY;
309 			return (NULL);
310 		}
311 		if ((msg_size < hptr->h_length) ||
312 		    (hostent_size < sizeof (char *))) {
313 			kfreehostent(hptr);
314 			*error_num = NO_RECOVERY;
315 			return (NULL);
316 		}
317 		if (inet_pton(af, (char *)name, cnf) == 1) {
318 			/*
319 			 * inet_pton converted the string successfully.
320 			 * reset to network order.  swaps based on nfs code
321 			 */
322 			if (af == AF_INET) {
323 				*((uint32_t *)cnf) = htonl(*((uint32_t *)cnf));
324 			} else {
325 				for (swap = ((void *)cnf), i = 0;
326 				    i < hptr->h_length / sizeof (uint16_t);
327 				    i++) {
328 					swap[i] = htons(swap[i]);
329 				}
330 			}
331 			hptr->h_addrtype = af;
332 			hptr->h_addr_list = (char **)((char *)hptr +
333 			    sizeof (struct hostent));
334 			*hptr->h_addr_list = (char *)cnf;
335 			return (hptr);
336 		}
337 
338 		/*
339 		 * The name couldn't ne converted by inet_pton.  The door is
340 		 * called.
341 		 */
342 
343 		/* Header initialization. */
344 		req->hdr.signature = ISCSI_DOOR_REQ_SIGNATURE;
345 		req->hdr.version = ISCSI_DOOR_REQ_VERSION_1;
346 		req->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_REQ;
347 
348 		/* Body initialization. */
349 		req->name_length = strlen(name);
350 		if (req->name_length >
351 		    (msg_size - sizeof (getipnodebyname_req_t) - 1)) {
352 			kfreehostent(hptr);
353 			*error_num = NO_RECOVERY;
354 			return (NULL);
355 		}
356 
357 		req->name_offset = sizeof (getipnodebyname_req_t);
358 		req->af = af;
359 		req->flags = flags;
360 		bcopy(
361 		    name,
362 		    ((char *)req + req->name_offset),
363 		    req->name_length);
364 
365 		/* Door argument initialization. */
366 		arg.data_ptr = (char *)req;
367 		arg.data_size = msg_size;
368 		arg.desc_num = 0;
369 		arg.desc_ptr = NULL;
370 		arg.rbuf = (char *)cnf;
371 		arg.rsize = msg_size;
372 
373 		if (iscsi_door_upcall(&arg) == B_FALSE) {
374 			/* The door call failed */
375 			kfreehostent(hptr);
376 			*error_num = NO_RECOVERY;
377 			return (NULL);
378 		}
379 
380 		/*
381 		 * The door call itself was successful.  The value returned
382 		 * in arg.rbuf should be cnf, but we never know.
383 		 */
384 		cnf = (getipnodebyname_cnf_t *)arg.rbuf;
385 
386 		if ((cnf == NULL) ||
387 		    (arg.rsize < sizeof (getipnodebyname_cnf_t)) ||
388 		    (cnf->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) ||
389 		    (cnf->hdr.version != ISCSI_DOOR_REQ_VERSION_1) ||
390 		    (cnf->hdr.opcode != ISCSI_DOOR_GETIPNODEBYNAME_CNF) ||
391 		    ((cnf->hdr.status != ISCSI_DOOR_STATUS_SUCCESS) &&
392 		    (cnf->hdr.status != ISCSI_DOOR_STATUS_MORE))) {
393 			/* The door didn't like the request */
394 			kfreehostent(hptr);
395 			*error_num = NO_RECOVERY;
396 			return (NULL);
397 		}
398 
399 		if (cnf->h_addr_list_length == 0) {
400 			kfreehostent(hptr);
401 			*error_num = HOST_NOT_FOUND;
402 			return (NULL);
403 		}
404 
405 		hptr->h_addrtype = cnf->h_addrtype;
406 		hptr->h_length = cnf->h_addrlen;
407 		hptr->h_addr_list = (char **)((char *)hptr +
408 		    sizeof (struct hostent));
409 		*hptr->h_addr_list = ((char *)cnf + cnf->h_addr_list_offset);
410 		return (hptr);
411 	} else {
412 		*error_num = NO_RECOVERY;
413 		return (NULL);
414 	}
415 }
416