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
isdigit(int ch)52 isdigit(int ch)
53 {
54 return (ch >= '0' && ch <= '9');
55 }
56
57 static boolean_t
iscsiboot_tgt_prop_read(void)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
iscsiboot_init_prop_read(void)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
iscsiboot_nic_prop_read(void)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
ld_ib_prop()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
parse_lun_num(uchar_t * str_num,uchar_t * hex_num)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
generate_iscsi_initiator_id(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
inet_aton(char * ipstr,uchar_t * ip)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