xref: /illumos-gate/usr/src/uts/sparc/os/iscsi_boot.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/bootprops.h>
27 #include <sys/bootconf.h>
28 #include <sys/modctl.h>
29 #include <sys/mman.h>
30 #include <sys/kmem.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/types.h>
34 #include <sys/obpdefs.h>
35 #include <sys/promif.h>
36 #include <sys/iscsi_protocol.h>
37 
38 #define	ISCSI_OBP_MAX_CHAP_USER_LEN	16
39 #define	ISCSI_OBP_MIN_CHAP_LEN		12
40 #define	ISCSI_OBP_MAX_CHAP_LEN		16
41 
42 #define	OBP_GET_KEY_STATUS_OK		0
43 #define	OBP_GET_KEY_STATUS_NOT_EXIST	-3
44 
45 ib_boot_prop_t boot_property;
46 extern ib_boot_prop_t *iscsiboot_prop;
47 static int inet_aton(char *ipstr, uchar_t *ip);
48 static boolean_t parse_lun_num(uchar_t *str_num, uchar_t *hex_num);
49 static void generate_iscsi_initiator_id(void);
50 
51 static int
52 isdigit(int ch)
53 {
54 	return (ch >= '0' && ch <= '9');
55 }
56 
57 static boolean_t
58 iscsiboot_tgt_prop_read(void)
59 {
60 	int		proplen;
61 	boolean_t	set		= B_FALSE;
62 	char		iscsi_target_ip[INET6_ADDRSTRLEN];
63 	uchar_t		iscsi_target_name[ISCSI_MAX_NAME_LEN];
64 	uchar_t		iscsi_par[8];
65 	char		chap_user[ISCSI_OBP_MAX_CHAP_USER_LEN]	= {0};
66 	char		chap_password[ISCSI_OBP_MAX_CHAP_LEN]	= {0};
67 	uchar_t		iscsi_port[8];
68 	uchar_t		iscsi_lun[8];
69 	uchar_t		iscsi_tpgt[8];
70 	long		iscsi_tpgtl;
71 	long		port;
72 	int		ret		= 0;
73 	int		status		= 0;
74 	int		chap_user_len	= 0;
75 	int		chap_pwd_len	= 0;
76 
77 	/* Get iscsi target IP address */
78 	proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_TARGET_IP);
79 	if (proplen > 0) {
80 		if (BOP_GETPROP(bootops, BP_ISCSI_TARGET_IP,
81 		    iscsi_target_ip) > 0) {
82 			if (inet_aton(iscsi_target_ip,
83 			    (uchar_t *)&boot_property.boot_tgt.tgt_ip_u) ==
84 			    0) {
85 				boot_property.boot_tgt.sin_family = AF_INET;
86 				set = B_TRUE;
87 			}
88 		}
89 	}
90 	if (set != B_TRUE) {
91 		return (B_FALSE);
92 	}
93 
94 	/* Get iscsi target port number */
95 	set = B_FALSE;
96 	proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_PORT);
97 	if (proplen > 0) {
98 		if (BOP_GETPROP(bootops, BP_ISCSI_PORT,
99 		    iscsi_port) > 0) {
100 			if (ddi_strtol((const char *)iscsi_port, NULL,
101 			    10, &port) == 0) {
102 				boot_property.boot_tgt.tgt_port =
103 				    (unsigned int)port;
104 				set = B_TRUE;
105 			}
106 		}
107 	}
108 	if (set != B_TRUE) {
109 		boot_property.boot_tgt.tgt_port = 3260;
110 	}
111 
112 	/* Get iscsi target LUN number */
113 	set = B_FALSE;
114 	proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_LUN);
115 	if (proplen > 0) {
116 		if (BOP_GETPROP(bootops, BP_ISCSI_LUN,
117 		    iscsi_lun) > 0) {
118 			if (parse_lun_num(iscsi_lun,
119 			    (uchar_t *)
120 			    (&boot_property.boot_tgt.tgt_boot_lun[0]))
121 			    == B_TRUE) {
122 				set = B_TRUE;
123 			}
124 		}
125 	}
126 	if (set != B_TRUE) {
127 		bzero((void *)boot_property.boot_tgt.tgt_boot_lun, 8);
128 	}
129 
130 	/* Get iscsi target portal group tag */
131 	set = B_FALSE;
132 	proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_TPGT);
133 	if (proplen > 0) {
134 		if (BOP_GETPROP(bootops, BP_ISCSI_TPGT,
135 		    iscsi_tpgt) > 0) {
136 			if (ddi_strtol((const char *)iscsi_tpgt, NULL, 10,
137 			    &iscsi_tpgtl) == 0) {
138 				boot_property.boot_tgt.tgt_tpgt =
139 				    (uint16_t)iscsi_tpgtl;
140 				set = B_TRUE;
141 			}
142 		}
143 	}
144 	if (set != B_TRUE) {
145 		boot_property.boot_tgt.tgt_tpgt = 1;
146 	}
147 
148 	/* Get iscsi target node name */
149 	set = B_FALSE;
150 	boot_property.boot_tgt.tgt_name = NULL;
151 	proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_TARGET_NAME);
152 	if (proplen > 0) {
153 		if (BOP_GETPROP(bootops, BP_ISCSI_TARGET_NAME,
154 		    iscsi_target_name) > 0) {
155 			boot_property.boot_tgt.tgt_name =
156 			    (uchar_t *)kmem_zalloc(proplen + 1, KM_SLEEP);
157 			boot_property.boot_tgt.tgt_name_len = proplen + 1;
158 			(void) snprintf((char *)boot_property.boot_tgt.tgt_name,
159 			    proplen + 1, "%s", iscsi_target_name);
160 			set = B_TRUE;
161 		}
162 	}
163 	if (set != B_TRUE) {
164 		if (boot_property.boot_tgt.tgt_name != NULL) {
165 			kmem_free(boot_property.boot_tgt.tgt_name,
166 			    boot_property.boot_tgt.tgt_name_len);
167 			boot_property.boot_tgt.tgt_name = NULL;
168 			boot_property.boot_tgt.tgt_name_len = 0;
169 		}
170 		return (B_FALSE);
171 	}
172 
173 	/* Get iscsi target boot partition */
174 	set = B_FALSE;
175 	boot_property.boot_tgt.tgt_boot_par = NULL;
176 	boot_property.boot_tgt.tgt_boot_par_len = 0;
177 	proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_PAR);
178 	if (proplen > 0) {
179 		if (BOP_GETPROP(bootops, BP_ISCSI_PAR, iscsi_par) > 0) {
180 			boot_property.boot_tgt.tgt_boot_par =
181 			    (uchar_t *)kmem_zalloc(proplen + 1, KM_SLEEP);
182 			boot_property.boot_tgt.tgt_boot_par_len = proplen + 1;
183 			(void) snprintf(
184 			    (char *)boot_property.boot_tgt.tgt_boot_par,
185 			    proplen + 1, "%s", iscsi_par);
186 			set = B_TRUE;
187 		}
188 	}
189 	if (set != B_TRUE) {
190 		boot_property.boot_tgt.tgt_boot_par =
191 		    (uchar_t *)kmem_zalloc(2, KM_SLEEP);
192 		boot_property.boot_tgt.tgt_boot_par_len = 2;
193 		boot_property.boot_tgt.tgt_boot_par[0] = 'a';
194 	}
195 
196 	/* Get CHAP name and secret */
197 	ret = prom_get_security_key(BP_CHAP_USER, chap_user,
198 	    ISCSI_OBP_MAX_CHAP_USER_LEN, &chap_user_len, &status);
199 	if (ret != 0) {
200 		return (B_FALSE);
201 	}
202 	if (status == OBP_GET_KEY_STATUS_NOT_EXIST) {
203 		/* No chap name */
204 		return (B_TRUE);
205 	}
206 	if (status != OBP_GET_KEY_STATUS_OK ||
207 	    chap_user_len > ISCSI_OBP_MAX_CHAP_USER_LEN ||
208 	    chap_user_len <= 0) {
209 		return (B_FALSE);
210 	}
211 
212 	ret = prom_get_security_key(BP_CHAP_PASSWORD, chap_password,
213 	    ISCSI_OBP_MAX_CHAP_LEN, &chap_pwd_len, &status);
214 	if (ret != 0) {
215 		return (B_FALSE);
216 	}
217 
218 	if (status == OBP_GET_KEY_STATUS_NOT_EXIST) {
219 		/* No chap secret */
220 		return (B_TRUE);
221 	}
222 	if (status != OBP_GET_KEY_STATUS_OK ||
223 	    chap_pwd_len > ISCSI_OBP_MAX_CHAP_LEN ||
224 	    chap_pwd_len <= 0) {
225 		return (B_FALSE);
226 	}
227 
228 	boot_property.boot_init.ini_chap_name =
229 	    (uchar_t *)kmem_zalloc(chap_user_len + 1, KM_SLEEP);
230 	boot_property.boot_init.ini_chap_name_len = chap_user_len + 1;
231 	(void) memcpy(boot_property.boot_init.ini_chap_name, chap_user,
232 	    chap_user_len);
233 
234 	boot_property.boot_init.ini_chap_sec =
235 	    (uchar_t *)kmem_zalloc(chap_pwd_len + 1, KM_SLEEP);
236 	boot_property.boot_init.ini_chap_sec_len = chap_pwd_len + 1;
237 	(void) memcpy(boot_property.boot_init.ini_chap_sec, chap_password,
238 	    chap_pwd_len);
239 
240 	return (B_TRUE);
241 }
242 
243 static boolean_t
244 iscsiboot_init_prop_read(void)
245 {
246 	int	proplen;
247 	uchar_t	iscsi_initiator_id[ISCSI_MAX_NAME_LEN];
248 	boolean_t	set = B_FALSE;
249 
250 	/* Get initiator node name */
251 	proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_INITIATOR_ID);
252 	if (proplen > 0) {
253 		if (BOP_GETPROP(bootops, BP_ISCSI_INITIATOR_ID,
254 		    iscsi_initiator_id) > 0) {
255 			boot_property.boot_init.ini_name =
256 			    (uchar_t *)kmem_zalloc(proplen + 1, KM_SLEEP);
257 			boot_property.boot_init.ini_name_len = proplen + 1;
258 			(void) snprintf(
259 			    (char *)boot_property.boot_init.ini_name,
260 			    proplen + 1, "%s", iscsi_initiator_id);
261 			set = B_TRUE;
262 		}
263 	}
264 	if (set != B_TRUE) {
265 		generate_iscsi_initiator_id();
266 	}
267 	return (B_TRUE);
268 }
269 
270 static boolean_t
271 iscsiboot_nic_prop_read(void)
272 {
273 	int	proplen;
274 	char	host_ip[INET6_ADDRSTRLEN];
275 	char	router_ip[INET6_ADDRSTRLEN];
276 	char	subnet_mask[INET6_ADDRSTRLEN];
277 	uchar_t	iscsi_network_path[MAXPATHLEN];
278 	char	host_mac[6];
279 	uchar_t	hex_netmask[4];
280 	pnode_t	nodeid;
281 	boolean_t	set = B_FALSE;
282 
283 	/* Get host IP address */
284 	proplen = BOP_GETPROPLEN(bootops, BP_HOST_IP);
285 	if (proplen > 0) {
286 		if (BOP_GETPROP(bootops, BP_HOST_IP,
287 		    host_ip) > 0) {
288 			if (inet_aton(host_ip,
289 			    (uchar_t *)&boot_property.boot_nic.nic_ip_u) ==
290 			    0) {
291 				boot_property.boot_nic.sin_family = AF_INET;
292 				set = B_TRUE;
293 			}
294 		}
295 	}
296 	if (set != B_TRUE) {
297 		return (B_FALSE);
298 	}
299 
300 	/* Get router IP address */
301 	proplen = BOP_GETPROPLEN(bootops, BP_ROUTER_IP);
302 	if (proplen > 0) {
303 		if (BOP_GETPROP(bootops, BP_ROUTER_IP,
304 		    router_ip) > 0) {
305 			(void) inet_aton(router_ip,
306 			    (uchar_t *)&boot_property.boot_nic.nic_gw_u);
307 		}
308 	}
309 
310 	/* Get host netmask */
311 	set = B_FALSE;
312 	proplen = BOP_GETPROPLEN(bootops, BP_SUBNET_MASK);
313 	if (proplen > 0) {
314 		if (BOP_GETPROP(bootops, BP_SUBNET_MASK,
315 		    subnet_mask) > 0) {
316 			if (inet_aton(subnet_mask, hex_netmask) == 0) {
317 				int i = 0;
318 				uint32_t tmp = *((uint32_t *)hex_netmask);
319 				while (tmp) {
320 					i ++;
321 					tmp = tmp << 1;
322 				}
323 				boot_property.boot_nic.sub_mask_prefix = i;
324 				set = B_TRUE;
325 			}
326 		}
327 	}
328 	if (set != B_TRUE) {
329 		boot_property.boot_nic.sub_mask_prefix = 24;
330 	}
331 
332 	/* Get iscsi boot NIC path in OBP */
333 	set = B_FALSE;
334 	proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_NETWORK_BOOTPATH);
335 	if (proplen > 0) {
336 		if (BOP_GETPROP(bootops, BP_ISCSI_NETWORK_BOOTPATH,
337 		    iscsi_network_path) > 0) {
338 			nodeid = prom_finddevice((char *)iscsi_network_path);
339 			proplen = prom_getproplen(nodeid, BP_LOCAL_MAC_ADDRESS);
340 			if (proplen > 0) {
341 				if (prom_getprop(nodeid, BP_LOCAL_MAC_ADDRESS,
342 				    host_mac) > 0) {
343 					(void) memcpy(
344 					    boot_property.boot_nic.nic_mac,
345 					    host_mac, 6);
346 					set = B_TRUE;
347 				}
348 			}
349 		}
350 	}
351 	if (set != B_TRUE) {
352 		return (B_FALSE);
353 	}
354 
355 	return (B_TRUE);
356 }
357 
358 /*
359  * Manully construct iscsiboot_prop table based on
360  * OBP '/chosen' properties related to iscsi boot
361  */
362 void
363 ld_ib_prop()
364 {
365 	if (iscsiboot_prop != NULL)
366 		return;
367 
368 	if ((iscsiboot_tgt_prop_read() == B_TRUE) &&
369 	    (iscsiboot_init_prop_read() == B_TRUE) &&
370 	    (iscsiboot_nic_prop_read() == B_TRUE)) {
371 		iscsiboot_prop = &boot_property;
372 	} else {
373 		iscsi_boot_prop_free();
374 	}
375 }
376 
377 static boolean_t
378 parse_lun_num(uchar_t *str_num, uchar_t *hex_num)
379 {
380 	char *p, *buf;
381 	uint16_t *conv_num = (uint16_t *)hex_num;
382 	long tmp;
383 	int i = 0;
384 
385 	if ((str_num == NULL) || (hex_num == NULL)) {
386 		return (B_FALSE);
387 	}
388 	bzero((void *)hex_num, 8);
389 	buf = (char *)str_num;
390 
391 	for (i = 0; i < 4; i++) {
392 		p = NULL;
393 		p = strchr((const char *)buf, '-');
394 		if (p != NULL) {
395 			*p = '\0';
396 		}
397 		if (ddi_strtol((const char *)buf, NULL, 16, &tmp) != 0) {
398 			return (B_FALSE);
399 		}
400 		conv_num[i] = (uint16_t)tmp;
401 		if (p != NULL) {
402 			buf = p + 1;
403 		} else {
404 			break;
405 		}
406 	}
407 
408 	return (B_TRUE);
409 }
410 
411 static void
412 generate_iscsi_initiator_id(void)
413 {
414 	boot_property.boot_init.ini_name_len = 38;
415 	boot_property.boot_init.ini_name =
416 	    (uchar_t *)kmem_zalloc(boot_property.boot_init.ini_name_len,
417 	    KM_SLEEP);
418 	(void) snprintf((char *)boot_property.boot_init.ini_name,
419 	    38, "iqn.1986-03.com.sun:boot.%02x%02x%02x%02x%02x%02x",
420 	    boot_property.boot_nic.nic_mac[0],
421 	    boot_property.boot_nic.nic_mac[1],
422 	    boot_property.boot_nic.nic_mac[2],
423 	    boot_property.boot_nic.nic_mac[3],
424 	    boot_property.boot_nic.nic_mac[4],
425 	    boot_property.boot_nic.nic_mac[5]);
426 }
427 
428 
429 /* We only deal with a.b.c.d decimal format. ip points to 4 byte storage */
430 static int
431 inet_aton(char *ipstr, uchar_t *ip)
432 {
433 	int i = 0;
434 	uchar_t val[4] = {0};
435 	char c = *ipstr;
436 
437 	for (;;) {
438 		if (!isdigit(c))
439 			return (-1);
440 		for (;;) {
441 			if (!isdigit(c))
442 				break;
443 			val[i] = val[i] * 10 + (c - '0');
444 			c = *++ipstr;
445 		}
446 		i++;
447 		if (i == 4)
448 			break;
449 		if (c != '.')
450 			return (-1);
451 		c = *++ipstr;
452 	}
453 	if (c != 0)
454 		return (-1);
455 	bcopy(val, ip, 4);
456 	return (0);
457 }
458