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 *
utils_map_param(int p)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 *
prt_bitmap(int bitmap,char * str,char * buf,int size)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
parse_addr_port_tpgt(char * in,char ** addr,int * type,char ** port,char ** tpgt)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 */
reverse_fqdn(const char * domain,char * buf,int 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
utils_iqn_create(char * iqn_buf,int size)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