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
45ib_boot_prop_t boot_property;
46extern ib_boot_prop_t *iscsiboot_prop;
47static int inet_aton(char *ipstr, uchar_t *ip);
48static boolean_t parse_lun_num(uchar_t *str_num, uchar_t *hex_num);
49static void generate_iscsi_initiator_id(void);
50
51static int
52isdigit(int ch)
53{
54	return (ch >= '0' && ch <= '9');
55}
56
57static boolean_t
58iscsiboot_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
243static boolean_t
244iscsiboot_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
270static boolean_t
271iscsiboot_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 */
362void
363ld_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
377static boolean_t
378parse_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
411static void
412generate_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 */
430static int
431inet_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