xref: /illumos-gate/usr/src/common/iscsi/utils.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 
29 
30 #ifdef _KERNEL
31 #include <sys/sunddi.h>
32 #else
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include <ctype.h>
37 #include <netinet/in.h>
38 #include <sys/utsname.h>
39 
40 /*
41  * NOTE: This routine is found in libnsl. There's apparently no prototype to
42  * be found in any of the header files in /usr/include so defining a prototype
43  * here to keep the compiler happy.
44  */
45 int getdomainname(char *, int);
46 
47 static const char *iqn_template		= "iqn.2004-02.%s";
48 #endif
49 
50 #include <sys/scsi/adapters/iscsi_if.h>
51 
52 typedef struct utils_val_name {
53 	int	u_val;
54 	char	*u_name;
55 } utils_val_name_t;
56 
57 utils_val_name_t param_names[] = {
58 	{ ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER, "Sequence In Order"},
59 	{ ISCSI_LOGIN_PARAM_IMMEDIATE_DATA, "Immediate Data"},
60 	{ ISCSI_LOGIN_PARAM_INITIAL_R2T, "Inital R2T"},
61 	{ ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER, "Data PDU In Order"},
62 	{ ISCSI_LOGIN_PARAM_HEADER_DIGEST, "Header Digest"},
63 	{ ISCSI_LOGIN_PARAM_DATA_DIGEST, "Data Digest"},
64 	{ ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN, "Default Time To Retain"},
65 	{ ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT, "Default Time To Wait"},
66 	{ ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH,
67 	    "Max Recv Data Segment Length"},
68 	{ ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH, "First Burst Length"},
69 	{ ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH, "Max Burst Length"},
70 	{ ISCSI_LOGIN_PARAM_MAX_CONNECTIONS, "Max Connections"},
71 	{ ISCSI_LOGIN_PARAM_OUTSTANDING_R2T, "Outstanding R2T"},
72 	{ ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL, "Error Recovery Level"},
73 	{ 0, NULL }
74 };
75 
76 /*
77  * utils_map_param -- Given a parameter return it's ascii name
78  *
79  * This routine was created because previously an array contained in order
80  * the parameter names. Once or twice the parameters value changed which
81  * changed the order, but not the array. To avoid further confusion we'll
82  * do a simple lookup. This code is rarely called so it shouldn't be an
83  * issue.
84  */
85 char *
86 utils_map_param(int p)
87 {
88 	utils_val_name_t	*pn;
89 
90 	for (pn = param_names; pn->u_name != NULL; pn++)
91 		if (pn->u_val == p)
92 			return (pn->u_name);
93 	return (NULL);
94 }
95 
96 /*
97  * prt_bitmap -- print out ascii strings associated with bit numbers.
98  */
99 char *
100 prt_bitmap(int bitmap, char *str, char *buf, int size)
101 {
102 	char	*p		= NULL;
103 	char	*start		= buf;
104 	int	do_put		= 0;
105 
106 	/*
107 	 * The maximum space required will if the bitmap was all 1's which
108 	 * would cause the octal characters to be replaced by '|'. So make
109 	 * sure the buffer has enough space.
110 	 */
111 	if (size < strlen(str))
112 		return ("No room");
113 
114 	for (p = str; size--; p++) {
115 		if (*p < 0x20) {
116 
117 			/*
118 			 * if we have been putting out stuff add separator
119 			 */
120 			if (do_put)
121 				*buf++ = '|';
122 
123 			do_put = ((1 << *p) & bitmap);
124 			bitmap &= ~(1 << *p);
125 
126 		} else if (do_put)
127 			*buf++ = *p;
128 	}
129 
130 	/* ---- remove the last separator if it was added ---- */
131 	if ((buf > start) && (*(buf - 1) == '|'))
132 		buf--;
133 	*buf = '\0';
134 	return (start);
135 }
136 
137 /*
138  * parse_addr_port_tpgt - Used to parse addr, port and tpgt from string
139  *
140  * This function is used to parse addr, port and tpgt from a string.  Callers
141  * of this function are the sendtargets and login redirection code.  The
142  * caller must be aware that this function will modify the callers string
143  * to insert NULL terminators if required.  Port and TPGT are optional.
144  */
145 boolean_t
146 parse_addr_port_tpgt(char *in, char **addr, int *type, char **port, char **tpgt)
147 {
148 	char	*t_port, *t_tpgt;
149 
150 	/* default return values if requested */
151 	if (addr == NULL) {
152 		return (B_FALSE);
153 	} else {
154 		*addr = NULL;
155 	}
156 	if (port != NULL) {
157 		*port = NULL;
158 	}
159 	if (tpgt != NULL) {
160 		*tpgt = NULL;
161 	}
162 
163 	/* extract ip or domain name */
164 	if (*in == '[') {
165 		/* IPV6 */
166 		*type = AF_INET6;
167 		*addr = ++in;
168 		in = strchr(*addr, ']');
169 		if (in == NULL)
170 			return (B_FALSE);
171 			*in++ = '\0';
172 	} else {
173 		/* IPV4 or domainname */
174 		*type = AF_INET;
175 		*addr = in;
176 	}
177 
178 	/* extract port */
179 	if (port != NULL) {
180 		t_port = strchr(in, ':');
181 		if (t_port != NULL) {
182 			*t_port++ = '\0';
183 			*port = in = t_port;
184 		}
185 	}
186 
187 	/* exact tpgt */
188 	if (tpgt != NULL) {
189 		t_tpgt = strchr(in, ',');
190 		if (t_tpgt != NULL) {
191 			*t_tpgt++ = '\0';
192 			*tpgt = in = t_tpgt;
193 		}
194 	}
195 
196 	return (B_TRUE);
197 }
198 
199 #ifndef _KERNEL
200 /*
201  * []--------------------------------------------------------------[]
202  * | reverse_fqdn -- given a fully qualified domain name reverse it |
203  * |                                                                |
204  * | The routine has the obvious problem that it can only handle a  |
205  * | name with 5 or less dots. This needs to be fixed by counting   |
206  * | the number of dots in the incoming name, calloc'ing an array   |
207  * | of the appropriate size and then handling the pointers.	    |
208  * []--------------------------------------------------------------[]
209  */
210 static boolean_t
211 /* LINTED E_FUNC_ARG_UNUSED for 3rd arg size */
212 reverse_fqdn(const char *domain, char *buf, int size)
213 {
214 	char	*ptrs[5];
215 	char	*dp;
216 	char	*dp1;
217 	char	*p;
218 	int	v = 4;
219 
220 	if ((dp = dp1 = malloc(strlen(domain) + 1)) == NULL)
221 		return (B_FALSE);
222 	(void) strcpy(dp, domain);
223 	while ((p = (char *)strchr(dp, '.')) != NULL) {
224 		*p = '\0';
225 		if (v < 0) {
226 			free(dp1);
227 			return (B_FALSE);
228 		}
229 		ptrs[v--] = dp;
230 		dp = p + 1;
231 	}
232 	(void) strcpy(buf, dp);
233 	for (v++; v < 5; v++) {
234 		(void) strcat(buf, ".");
235 		(void) strcat(buf, ptrs[v]);
236 	}
237 	free(dp1);
238 	return (B_TRUE);
239 }
240 
241 /*
242  * []------------------------------------------------------------------[]
243  * | utils_iqn_create -- returns an iqn name for the machine		|
244  * |									|
245  * | The information found in the iqn is not correct. The year and	|
246  * | date should be flexible. Currently this is hardwired to the	|
247  * | current year and month of this project.				|
248  * []------------------------------------------------------------------[]
249  */
250 boolean_t
251 utils_iqn_create(char *iqn_buf, int size)
252 {
253 	struct utsname	uts_info;
254 	char		domainname[256];
255 	char		*temp = NULL;
256 	char		*p;
257 	char		*pmet = NULL; /* temp reversed .. get it */
258 	int		len;
259 	boolean_t	rval = B_FALSE; /* Default */
260 
261 	if (uname(&uts_info) == -1) {
262 		goto out;
263 	}
264 
265 	if (getdomainname(domainname, sizeof (domainname))) {
266 		goto out;
267 	}
268 
269 	if ((temp = malloc(strlen(uts_info.nodename) +
270 	    strlen(domainname) + 2)) == NULL) {
271 		goto out;
272 	}
273 
274 	/*
275 	 * getdomainname always returns something in the order of
276 	 * host.domainname so we need to skip over that portion of the
277 	 * host name because we don't care about it.
278 	 */
279 	if ((p = strchr(domainname, '.')) == NULL)
280 		p = domainname;
281 	else
282 		p++;
283 
284 	/* ---- Create Fully Qualified Domain Name ---- */
285 	(void) snprintf(temp, strlen(p), "%s.%s", uts_info.nodename, p);
286 
287 	/* ---- According to the spec, names must be lower case ---- */
288 	for (p = temp; *p; p++)
289 		if (isupper(*p))
290 			*p = tolower(*p);
291 
292 	len = strlen(temp) + 1;
293 	if ((pmet = malloc(len)) == NULL) {
294 		goto out;
295 	}
296 
297 	if (reverse_fqdn(temp, pmet, len) == B_FALSE) {
298 		goto out;
299 	}
300 
301 	/*
302 	 * Now use the template with the reversed domainname to create
303 	 * an iSCSI name using the IQN format. Only count it a success
304 	 * if the number of characters formated is less than the buffer
305 	 * size.
306 	 */
307 	if (snprintf(iqn_buf, size, iqn_template, pmet) <= size)
308 		rval = B_TRUE;
309 out:
310 	if (temp)
311 		free(temp);
312 	if (pmet)
313 		free(pmet);
314 
315 	return (rval);
316 }
317 #endif /* !_KERNEL */
318