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