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/types.h>
27#include <sys/conf.h>
28#include <sys/file.h>
29#include <sys/ddi.h>
30#include <sys/sunddi.h>
31#include <sys/sysmacros.h>
32#include <sys/socket.h>
33
34#include <sys/iscsi_protocol.h>
35#include <sys/idm/idm.h>
36#include <sys/idm/idm_text.h>
37
38
39extern int
40iscsi_base64_str_to_binary(char *hstr, int hstr_len,
41    uint8_t *binary, int binary_buf_len, int *out_len);
42
43
44static const char idm_hex_to_ascii[] = "0123456789abcdefABCDEF";
45
46static const idm_kv_xlate_t idm_kvpair_xlate[] = {
47	/*
48	 * iSCSI Security Text Keys and Authentication Methods
49	 */
50
51	{ KI_AUTH_METHOD, "AuthMethod", KT_LIST_OF_VALUES, B_FALSE },
52	/*
53	 * For values with RFC comments we need to read the RFC to see
54	 * what type is appropriate.  For now just treat the value as
55	 * text.
56	 */
57
58	/* Kerberos */
59	{ KI_KRB_AP_REQ, "KRB_AP_REQ", KT_TEXT /* RFC1510 */, B_TRUE},
60	{ KI_KRB_AP_REP, "KRB_AP_REP", KT_TEXT /* RFC1510 */, B_TRUE},
61
62	/* SPKM */
63	{ KI_SPKM_REQ, "SPKM_REQ", KT_TEXT /* RFC2025 */, B_TRUE},
64	{ KI_SPKM_ERROR, "SPKM_ERROR", KT_TEXT /* RFC2025 */, B_TRUE},
65	{ KI_SPKM_REP_TI, "SPKM_REP_TI", KT_TEXT /* RFC2025 */, B_TRUE},
66	{ KI_SPKM_REP_IT, "SPKM_REP_IT", KT_TEXT /* RFC2025 */, B_TRUE},
67
68	/*
69	 * SRP
70	 * U, s, A, B, M, and H(A | M | K) are defined in [RFC2945]
71	 */
72	{ KI_SRP_U, "SRP_U", KT_TEXT /* <U> */, B_TRUE},
73	{ KI_TARGET_AUTH, "TargetAuth", KT_BOOLEAN, B_TRUE},
74	{ KI_SRP_GROUP, "SRP_GROUP", KT_LIST_OF_VALUES /* <G1,..> */, B_FALSE},
75	{ KI_SRP_A, "SRP_A", KT_TEXT /* <A> */, B_TRUE},
76	{ KI_SRP_B, "SRP_B", KT_TEXT /* <B> */, B_TRUE},
77	{ KI_SRP_M, "SRP_M", KT_TEXT /* <M> */, B_TRUE},
78	{ KI_SRM_HM, "SRP_HM", KT_TEXT /* <H(A | M | K)> */, B_TRUE},
79
80	/*
81	 * CHAP
82	 */
83	{ KI_CHAP_A, "CHAP_A", KT_LIST_OF_VALUES /* <A1,A2,..> */, B_FALSE },
84	{ KI_CHAP_I, "CHAP_I", KT_NUMERICAL /* <I> */, B_TRUE },
85	{ KI_CHAP_C, "CHAP_C", KT_BINARY /* <C> */, B_TRUE },
86	{ KI_CHAP_N, "CHAP_N", KT_TEXT /* <N> */, B_TRUE },
87	{ KI_CHAP_R, "CHAP_R", KT_BINARY /* <N> */, B_TRUE },
88
89
90	/*
91	 * ISCSI Operational Parameter Keys
92	 */
93	{ KI_HEADER_DIGEST, "HeaderDigest", KT_LIST_OF_VALUES, B_FALSE },
94	{ KI_DATA_DIGEST, "DataDigest", KT_LIST_OF_VALUES, B_FALSE },
95	{ KI_MAX_CONNECTIONS, "MaxConnections", KT_NUMERICAL, B_FALSE },
96	{ KI_SEND_TARGETS, "SendTargets", KT_TEXT, B_FALSE },
97	{ KI_TARGET_NAME, "TargetName", KT_ISCSI_NAME, B_TRUE},
98	{ KI_INITIATOR_NAME, "InitiatorName", KT_ISCSI_NAME, B_TRUE},
99	{ KI_TARGET_ALIAS, "TargetAlias", KT_ISCSI_LOCAL_NAME, B_TRUE},
100	{ KI_INITIATOR_ALIAS, "InitiatorAlias", KT_ISCSI_LOCAL_NAME, B_TRUE},
101	{ KI_TARGET_ADDRESS, "TargetAddress", KT_TEXT, B_TRUE},
102	{ KI_TARGET_PORTAL_GROUP_TAG, "TargetPortalGroupTag",
103	    KT_NUMERICAL, B_TRUE },
104	{ KI_INITIAL_R2T, "InitialR2T", KT_BOOLEAN, B_FALSE },
105	{ KI_IMMEDIATE_DATA, "ImmediateData", KT_BOOLEAN, B_FALSE },
106	{ KI_MAX_RECV_DATA_SEGMENT_LENGTH, "MaxRecvDataSegmentLength",
107	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_TRUE },
108	{ KI_MAX_BURST_LENGTH, "MaxBurstLength",
109	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
110	{ KI_FIRST_BURST_LENGTH, "FirstBurstLength",
111	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
112	{ KI_DEFAULT_TIME_2_WAIT, "DefaultTime2Wait",
113	    KT_NUMERICAL /* 0 to 2600 */, B_FALSE },
114	{ KI_DEFAULT_TIME_2_RETAIN, "DefaultTime2Retain",
115	    KT_NUMERICAL /* 0 to 2600 */, B_FALSE },
116	{ KI_MAX_OUTSTANDING_R2T, "MaxOutstandingR2T",
117	    KT_NUMERICAL /* 1 to 65535 */, B_FALSE },
118	{ KI_DATA_PDU_IN_ORDER, "DataPDUInOrder", KT_BOOLEAN, B_FALSE },
119	{ KI_DATA_SEQUENCE_IN_ORDER, "DataSequenceInOrder",
120	    KT_BOOLEAN, B_FALSE },
121	{ KI_ERROR_RECOVERY_LEVEL, "ErrorRecoveryLevel",
122	    KT_NUMERICAL /* 0 to 2 */, B_FALSE },
123	{ KI_SESSION_TYPE, "SessionType", KT_TEXT, B_TRUE },
124	{ KI_OFMARKER, "OFMarker", KT_BOOLEAN, B_FALSE },
125	{ KI_OFMARKERINT, "OFMarkerInt", KT_NUMERIC_RANGE, B_FALSE },
126	{ KI_IFMARKER, "IFMarker", KT_BOOLEAN, B_FALSE },
127	{ KI_IFMARKERINT, "IFMarkerInt", KT_NUMERIC_RANGE, B_FALSE },
128
129	/*
130	 * iSER-specific keys
131	 */
132	{ KI_RDMA_EXTENSIONS, "RDMAExtensions", KT_BOOLEAN, B_FALSE },
133	{ KI_TARGET_RECV_DATA_SEGMENT_LENGTH, "TargetRecvDataSegmentLength",
134	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
135	{ KI_INITIATOR_RECV_DATA_SEGMENT_LENGTH,
136	    "InitiatorRecvDataSegmentLength",
137	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
138	{ KI_MAX_OUTSTANDING_UNEXPECTED_PDUS, "MaxOutstandingUnexpectedPDUs",
139	    KT_NUMERICAL /* 2 to 2^32 - 1 | 0 */, B_TRUE },
140
141	/*
142	 * Table terminator. The type KT_TEXT will allow the response
143	 * value of "NotUnderstood".
144	 */
145	{ KI_MAX_KEY, NULL, KT_TEXT, B_TRUE } /* Terminator */
146};
147
148
149#define	TEXTBUF_CHUNKSIZE 8192
150
151typedef struct {
152	char	*itb_mem;
153	int	itb_offset;
154	int	itb_mem_len;
155} idm_textbuf_t;
156
157/*
158 * Ignore all but the following keys during security negotiation
159 *
160 * SessionType
161 * InitiatorName
162 * TargetName
163 * TargetAddress
164 * InitiatorAlias
165 * TargetAlias
166 * TargetPortalGroupTag
167 * AuthMethod and associated auth keys
168 */
169
170static int idm_keyvalue_get_next(char **tb_scan, int *tb_len,
171    char **key, int *keylen, char **value);
172
173static int idm_nvlist_add_kv(nvlist_t *nvl, const idm_kv_xlate_t *ikvx,
174    char *value);
175
176static int idm_nvlist_add_string(nvlist_t *nvl,
177    const idm_kv_xlate_t *ikvx, char *value);
178
179static int idm_nvlist_add_boolean(nvlist_t *nvl,
180    const idm_kv_xlate_t *ikvx, char *value);
181
182static int idm_nvlist_add_binary(nvlist_t *nvl,
183    const idm_kv_xlate_t *ikvx, char *value);
184
185static int idm_nvlist_add_large_numerical(nvlist_t *nvl,
186    const idm_kv_xlate_t *ikvx, char *value);
187
188static int idm_nvlist_add_numerical(nvlist_t *nvl,
189    const idm_kv_xlate_t *ikvx, char *value);
190
191static int idm_nvlist_add_numeric_range(nvlist_t *nvl,
192    const idm_kv_xlate_t *ikvx, char *value);
193
194static int idm_nvlist_add_list_of_values(nvlist_t *nvl,
195    const idm_kv_xlate_t *ikvx, char *value);
196
197static int idm_itextbuf_add_nvpair(nvpair_t *nvp, idm_textbuf_t *itb);
198
199static int idm_itextbuf_add_string(nvpair_t *nvp,
200    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
201
202static int idm_itextbuf_add_boolean(nvpair_t *nvp,
203    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
204
205static int idm_itextbuf_add_binary(nvpair_t *nvp,
206    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
207
208static int idm_itextbuf_add_large_numerical(nvpair_t *nvp,
209    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
210
211static int idm_itextbuf_add_numerical(nvpair_t *nvp,
212    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
213
214static int idm_itextbuf_add_numeric_range(nvpair_t *nvp,
215    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
216
217static int idm_itextbuf_add_list_of_values(nvpair_t *nvp,
218    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
219
220static void textbuf_memcpy(idm_textbuf_t *itb, void *mem, int mem_len);
221
222static void textbuf_strcpy(idm_textbuf_t *itb, char *str);
223
224static void textbuf_append_char(idm_textbuf_t *itb, char c);
225
226static void textbuf_terminate_kvpair(idm_textbuf_t *itb);
227
228static int idm_ascii_to_hex(char *enc_hex_byte, uint8_t *bin_val);
229
230static int idm_base16_str_to_binary(char *hstr, int hstr_len,
231    uint8_t *binary, int binary_length);
232
233static size_t idm_strcspn(const char *string, const char *charset);
234
235static size_t idm_strnlen(const char *str, size_t maxlen);
236
237/*
238 * Processes all whole iSCSI name-value pairs in a text buffer and adds
239 * a corresponding Solaris nvpair_t to the provided nvlist.  If the last
240 * iSCSI name-value pair in textbuf is truncated (which can occur when
241 * the request spans multiple PDU's) then upon return textbuf will
242 * point to the truncated iSCSI name-value pair in the buffer and
243 * textbuflen will contain the remaining bytes in the buffer.  The
244 * caller can save off this fragment of the iSCSI name-value pair for
245 * use when the next PDU in the request arrives.
246 *
247 * textbuflen includes the trailing 0x00!
248 */
249
250int
251idm_textbuf_to_nvlist(nvlist_t *nvl, char **textbuf, int *textbuflen)
252{
253	int rc = 0;
254	char *tbscan, *key, *value;
255	int tblen, keylen;
256
257	tbscan = *textbuf;
258	tblen = *textbuflen;
259
260	for (;;) {
261		if ((rc = idm_keyvalue_get_next(&tbscan, &tblen,
262		    &key, &keylen, &value)) != 0) {
263			/* There was a problem reading the key/value pair */
264			break;
265		}
266
267		if ((rc = idm_nvlist_add_keyvalue(nvl,
268		    key, keylen, value)) != 0) {
269			/* Something was wrong with either the key or value */
270			break;
271		}
272
273		if (tblen == 0) {
274			/* End of text buffer */
275			break;
276		}
277	}
278
279	*textbuf = tbscan;
280	*textbuflen = tblen;
281
282	return (rc);
283}
284
285/*
286 * If a test buffer starts with an ISCSI name-value pair fragment (a
287 * continuation from a previous buffer) return the length of the fragment
288 * contained in this buffer.  We do not handle name-value pairs that span
289 * more than two buffers so if this buffer does not contain the remainder
290 * of the name value pair the function will return 0.  If the first
291 * name-value pair in the buffer is complete the functionw will return 0.
292 */
293int
294idm_textbuf_to_firstfraglen(void *textbuf, int textbuflen)
295{
296	return (idm_strnlen(textbuf, textbuflen));
297}
298
299static int
300idm_keyvalue_get_next(char **tb_scan, int *tb_len,
301    char **key, int *keylen, char **value)
302{
303	/*
304	 * Caller doesn't need "valuelen" returned since "value" will
305	 * always be a NULL-terminated string.
306	 */
307	size_t total_len, valuelen;
308
309	/*
310	 * How many bytes to the first '\0'?  This represents the total
311	 * length of our iSCSI key/value pair.
312	 */
313	total_len = idm_strnlen(*tb_scan, *tb_len);
314	if (total_len == *tb_len) {
315		/*
316		 * No '\0', perhaps this key/value pair is continued in
317		 * another buffer
318		 */
319		return (E2BIG);
320	}
321
322	/*
323	 * Found NULL, so this is a possible key-value pair.  At
324	 * the same time we've validated that there is actually a
325	 * NULL in this string so it's safe to use regular
326	 * string functions (i.e. strcpy instead of strncpy)
327	 */
328	*key = *tb_scan;
329	*keylen = idm_strcspn(*tb_scan, "=");
330
331	if (*keylen == total_len) {
332		/* No '=', bad format */
333		return (EINVAL);
334	}
335
336	*tb_scan += *keylen + 1; /* Skip the '=' */
337	*tb_len -= *keylen + 1;
338
339	/*
340	 * The remaining text after the '=' is the value
341	 */
342	*value = *tb_scan;
343	valuelen = total_len - (*keylen + 1);
344
345	*tb_scan += valuelen + 1; /* Skip the '\0' */
346	*tb_len -= valuelen + 1;
347
348	return (0);
349}
350
351const idm_kv_xlate_t *
352idm_lookup_kv_xlate(const char *key, int keylen)
353{
354	const idm_kv_xlate_t *ikvx = &idm_kvpair_xlate[0];
355
356	/*
357	 * Look for a matching key value in the key/value pair table.
358	 * The matching entry in the table will tell us how to encode
359	 * the key and value in the nvlist.  If we don't recognize
360	 * the key then we will simply encode it in string format.
361	 * The login or text request code can generate the appropriate
362	 * "not understood" resposne.
363	 */
364	while (ikvx->ik_key_id != KI_MAX_KEY) {
365		/*
366		 * Compare strings.  "key" is not NULL-terminated so
367		 * use strncmp.  Since we are using strncmp we
368		 * need to check that the lengths match, otherwise
369		 * we might unintentionally lookup "TargetAddress"
370		 * with a key of "Target" (or something similar).
371		 *
372		 * "value" is NULL-terminated so we can use it as
373		 * a regular string.
374		 */
375		if ((strncmp(ikvx->ik_key_name, key, keylen) == 0) &&
376		    (strlen(ikvx->ik_key_name) == keylen)) {
377			/* Exit the loop since we found a match */
378			break;
379		}
380
381		/* No match, look at the next entry */
382		ikvx++;
383	}
384
385	return (ikvx);
386}
387
388static int
389idm_nvlist_add_kv(nvlist_t *nvl,  const idm_kv_xlate_t *ikvx, char *value)
390{
391	int rc;
392
393	switch (ikvx->ik_idm_type) {
394	case KT_TEXT:
395	case KT_SIMPLE:
396	case KT_ISCSI_NAME:
397	case KT_ISCSI_LOCAL_NAME:
398		rc = idm_nvlist_add_string(nvl, ikvx, value);
399		break;
400	case KT_BOOLEAN:
401		rc = idm_nvlist_add_boolean(nvl, ikvx, value);
402		break;
403	case KT_REGULAR_BINARY:
404	case KT_LARGE_BINARY:
405	case KT_BINARY:
406		rc = idm_nvlist_add_binary(nvl, ikvx, value);
407		break;
408	case KT_LARGE_NUMERICAL:
409		rc = idm_nvlist_add_large_numerical(nvl, ikvx,
410		    value);
411		break;
412	case KT_NUMERICAL:
413		rc = idm_nvlist_add_numerical(nvl, ikvx,
414		    value);
415		break;
416	case KT_NUMERIC_RANGE:
417		rc = idm_nvlist_add_numeric_range(nvl, ikvx,
418		    value);
419		break;
420	case KT_LIST_OF_VALUES:
421		rc = idm_nvlist_add_list_of_values(nvl, ikvx,
422		    value);
423		break;
424	default:
425		ASSERT(0); /* This should never happen */
426		break;
427	}
428	if (rc != 0) {
429		/* could be one of the text constants */
430		rc = idm_nvlist_add_string(nvl, ikvx, value);
431	}
432
433	return (rc);
434}
435
436static int
437idm_nvlist_add_string(nvlist_t *nvl,
438    const idm_kv_xlate_t *ikvx, char *value)
439{
440	return (nvlist_add_string(nvl, ikvx->ik_key_name, value));
441}
442
443static int
444idm_nvlist_add_boolean(nvlist_t *nvl,
445    const idm_kv_xlate_t *ikvx, char *value)
446{
447	int rc;
448	boolean_t bool_val;
449
450	if (strcasecmp(value, "Yes") == 0) {
451		bool_val = B_TRUE;
452	} else if (strcasecmp(value, "No") == 0) {
453		bool_val = B_FALSE;
454	} else {
455		return (EINVAL);
456	}
457
458	rc = nvlist_add_boolean_value(nvl, ikvx->ik_key_name, bool_val);
459
460	return (rc);
461}
462
463static boolean_t
464kv_is_hex(char *value)
465{
466	return ((strncmp(value, "0x", strlen("0x")) == 0) ||
467	    (strncmp(value, "0X", strlen("0X")) == 0));
468}
469
470static boolean_t
471kv_is_base64(char *value)
472{
473	return ((strncmp(value, "0b", strlen("0b")) == 0) ||
474	    (strncmp(value, "0B", strlen("0B")) == 0));
475}
476
477
478static int
479idm_nvlist_add_binary(nvlist_t *nvl,
480    const idm_kv_xlate_t *ikvx, char *value)
481{
482	int		rc;
483	int		value_length;
484	uint64_t	uint64_value;
485	int		binary_length;
486	uchar_t		*binary_array;
487
488	/*
489	 * A binary value can be either decimal, hex or base64.  If it's
490	 * decimal then the encoded string must be less than 64 bits in
491	 * length (8 characters).  In all cases we will convert the
492	 * included value to a byte array starting with the MSB.  The
493	 * assumption is that values meant to be treated as integers will
494	 * use the "numerical" and "large numerical" types.
495	 */
496	if (kv_is_hex(value)) {
497		value += strlen("0x");
498		value_length = strlen(value);
499		binary_length = (value_length + 1) / 2;
500		binary_array = kmem_alloc(binary_length, KM_SLEEP);
501
502		if (idm_base16_str_to_binary(value, value_length,
503		    binary_array, binary_length) != 0) {
504			kmem_free(binary_array, binary_length);
505			return (EINVAL);
506		}
507
508		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
509		    binary_array, binary_length);
510
511		kmem_free(binary_array, binary_length);
512
513		return (rc);
514
515	} else if (kv_is_base64(value)) {
516		value += strlen("0b");
517		value_length = strlen(value);
518		binary_array = kmem_alloc(value_length, KM_NOSLEEP);
519		if (binary_array == NULL) {
520			return (ENOMEM);
521		}
522
523		if (iscsi_base64_str_to_binary(value, value_length,
524		    binary_array, value_length, &binary_length) != 0) {
525			kmem_free(binary_array, value_length);
526			return (EINVAL);
527		}
528
529		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
530		    binary_array, binary_length);
531
532		kmem_free(binary_array, value_length);
533
534		return (rc);
535	} else {
536		/*
537		 * Decimal value (not permitted for "large-binary_value" so
538		 * it must be smaller than 64 bits.  It's not really
539		 * clear from the RFC what a decimal-binary-value might
540		 * represent but presumably it should be treated the same
541		 * as a hex or base64 value.  Therefore we'll convert it
542		 * to an array of bytes.
543		 */
544		if ((rc = ddi_strtoull(value, NULL, 0,
545		    (u_longlong_t *)&uint64_value)) != 0)
546			return (rc);
547
548		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
549		    (uint8_t *)&uint64_value, sizeof (uint64_value));
550
551		return (rc);
552	}
553
554	/* NOTREACHED */
555}
556
557
558static int
559idm_nvlist_add_large_numerical(nvlist_t *nvl,
560    const idm_kv_xlate_t *ikvx, char *value)
561{
562	/*
563	 * A "large numerical" value can be larger than 64-bits.  Since
564	 * there is no upper bound on the size of the value, we will
565	 * punt and store it in string form.  We could also potentially
566	 * treat the value as binary data.
567	 */
568	return (nvlist_add_string(nvl, ikvx->ik_key_name, value));
569}
570
571
572static int
573idm_nvlist_add_numerical(nvlist_t *nvl,
574    const idm_kv_xlate_t *ikvx, char *value)
575{
576	int rc;
577	uint64_t uint64_value;
578
579	/*
580	 * "Numerical" values in the iSCSI standard are up to 64-bits wide.
581	 * On a 32-bit system we could see an overflow here during conversion.
582	 * This shouldn't happen with real-world values for the current
583	 * iSCSI parameters of "numerical" type.
584	 */
585	rc = ddi_strtoull(value, NULL, 0, (u_longlong_t *)&uint64_value);
586	if (rc == 0) {
587		rc = nvlist_add_uint64(nvl, ikvx->ik_key_name, uint64_value);
588	}
589
590	return (rc);
591}
592
593
594static int
595idm_nvlist_add_numeric_range(nvlist_t *nvl,
596    const idm_kv_xlate_t *ikvx, char *range)
597{
598	nvlist_t *range_nvl;
599	char *val_scan = range;
600	uint64_t start_val, end_val;
601	int val_len, range_len;
602	int rc;
603
604	/* We'll store the range an an nvlist with two values */
605	rc = nvlist_alloc(&range_nvl, NV_UNIQUE_NAME, KM_NOSLEEP);
606	if (rc != 0) {
607		return (rc);
608	}
609
610	/*
611	 * We expect idm_keyvalue_get_next to ensure the string is
612	 * terminated
613	 */
614	range_len = strlen(range);
615
616	/*
617	 * Find range separator
618	 */
619	val_len = idm_strcspn(val_scan, "~");
620
621	if (val_len == range_len) {
622		/* invalid range */
623		nvlist_free(range_nvl);
624		return (EINVAL);
625	}
626
627	/*
628	 * Start value
629	 */
630	*(val_scan + val_len + 1) = '\0';
631	rc = ddi_strtoull(val_scan, NULL, 0, (u_longlong_t *)&start_val);
632	if (rc == 0) {
633		rc = nvlist_add_uint64(range_nvl, "start", start_val);
634	}
635	if (rc != 0) {
636		nvlist_free(range_nvl);
637		return (rc);
638	}
639
640	/*
641	 * End value
642	 */
643	val_scan += val_len + 1;
644	rc = ddi_strtoull(val_scan, NULL, 0, (u_longlong_t *)&end_val);
645	if (rc == 0) {
646		rc = nvlist_add_uint64(range_nvl, "start", end_val);
647	}
648	if (rc != 0) {
649		nvlist_free(range_nvl);
650		return (rc);
651	}
652
653	/*
654	 * Now add the "range" nvlist to the main nvlist
655	 */
656	rc = nvlist_add_nvlist(nvl, ikvx->ik_key_name, range_nvl);
657	if (rc != 0) {
658		nvlist_free(range_nvl);
659		return (rc);
660	}
661
662	nvlist_free(range_nvl);
663	return (0);
664}
665
666
667static int
668idm_nvlist_add_list_of_values(nvlist_t *nvl,
669    const idm_kv_xlate_t *ikvx, char *value_list)
670{
671	char value_name[8];
672	nvlist_t *value_list_nvl;
673	char *val_scan = value_list;
674	int value_index = 0;
675	int val_len, val_list_len;
676	int rc;
677
678	rc = nvlist_alloc(&value_list_nvl, NV_UNIQUE_NAME, KM_NOSLEEP);
679	if (rc != 0) {
680		return (rc);
681	}
682
683	/*
684	 * We expect idm_keyvalue_get_next to ensure the string is
685	 * terminated
686	 */
687	val_list_len = strlen(value_list);
688	if (val_list_len == 0) {
689		nvlist_free(value_list_nvl);
690		return (EINVAL);
691	}
692
693	for (;;) {
694		(void) snprintf(value_name, 8, "value%d", value_index);
695
696		val_len = idm_strcspn(val_scan, ",");
697
698		if (*(val_scan + val_len) != '\0') {
699			*(val_scan + val_len) = '\0';
700		}
701		rc = nvlist_add_string(value_list_nvl, value_name, val_scan);
702		if (rc != 0) {
703			nvlist_free(value_list_nvl);
704			return (rc);
705		}
706
707		/*
708		 * Move to next value, see if we're at the end of the value
709		 * list
710		 */
711		val_scan += val_len + 1;
712		if (val_scan == value_list + val_list_len + 1) {
713			break;
714		}
715
716		value_index++;
717	}
718
719	rc = nvlist_add_nvlist(nvl, ikvx->ik_key_name, value_list_nvl);
720	if (rc != 0) {
721		nvlist_free(value_list_nvl);
722		return (rc);
723	}
724
725	nvlist_free(value_list_nvl);
726	return (0);
727}
728
729/*
730 * Convert an nvlist containing standard iSCSI key names and values into
731 * a text buffer with properly formatted iSCSI key-value pairs ready to
732 * transmit on the wire.  *textbuf should be NULL and will be set to point
733 * the resulting text buffer.
734 */
735
736int
737idm_nvlist_to_textbuf(nvlist_t *nvl, char **textbuf, int *textbuflen,
738    int *validlen)
739{
740	int rc = 0;
741	nvpair_t *nvp = NULL;
742	idm_textbuf_t itb;
743
744	bzero(&itb, sizeof (itb));
745
746	for (;;) {
747		nvp = nvlist_next_nvpair(nvl, nvp);
748
749		if (nvp == NULL) {
750			/* Last nvpair in nvlist, we're done */
751			break;
752		}
753
754		if ((rc = idm_itextbuf_add_nvpair(nvp, &itb)) != 0) {
755			/* There was a problem building the key/value pair */
756			break;
757		}
758	}
759
760	*textbuf = itb.itb_mem;
761	*textbuflen = itb.itb_mem_len;
762	*validlen = itb.itb_offset;
763
764	return (rc);
765}
766
767static int
768idm_itextbuf_add_nvpair(nvpair_t *nvp,
769    idm_textbuf_t *itb)
770{
771	int rc = 0;
772	char *key;
773	const idm_kv_xlate_t *ikvx;
774
775	key = nvpair_name(nvp);
776
777	ikvx = idm_lookup_kv_xlate(key, strlen(key));
778
779	/*
780	 * Any key supplied by the initiator that is not in our table
781	 * will be responded to with the string value "NotUnderstood".
782	 * An example is a vendor specific key.
783	 */
784	ASSERT((ikvx->ik_key_id != KI_MAX_KEY) ||
785	    (nvpair_type(nvp) == DATA_TYPE_STRING));
786
787	/*
788	 * Look for a matching key value in the key/value pair table.
789	 * The matching entry in the table will tell us how to encode
790	 * the key and value in the nvlist.
791	 */
792	switch (ikvx->ik_idm_type) {
793	case KT_TEXT:
794	case KT_SIMPLE:
795	case KT_ISCSI_NAME:
796	case KT_ISCSI_LOCAL_NAME:
797		rc = idm_itextbuf_add_string(nvp, ikvx, itb);
798		break;
799	case KT_BOOLEAN:
800		rc = idm_itextbuf_add_boolean(nvp, ikvx, itb);
801		break;
802	case KT_REGULAR_BINARY:
803	case KT_LARGE_BINARY:
804	case KT_BINARY:
805		rc = idm_itextbuf_add_binary(nvp, ikvx, itb);
806		break;
807	case KT_LARGE_NUMERICAL:
808		rc = idm_itextbuf_add_large_numerical(nvp, ikvx, itb);
809		break;
810	case KT_NUMERICAL:
811		rc = idm_itextbuf_add_numerical(nvp, ikvx, itb);
812		break;
813	case KT_NUMERIC_RANGE:
814		rc = idm_itextbuf_add_numeric_range(nvp, ikvx, itb);
815		break;
816	case KT_LIST_OF_VALUES:
817		rc = idm_itextbuf_add_list_of_values(nvp, ikvx, itb);
818		break;
819	default:
820		ASSERT(0); /* This should never happen */
821		break;
822	}
823
824	return (rc);
825}
826
827/* ARGSUSED */
828static int
829idm_itextbuf_add_string(nvpair_t *nvp,
830    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
831{
832	char	*key_name;
833	char	*value;
834	int	rc;
835
836	/* Start with the key name */
837	key_name = nvpair_name(nvp);
838	textbuf_strcpy(itb, key_name);
839
840	/* Add separator */
841	textbuf_append_char(itb, '=');
842
843	/* Add value */
844	rc = nvpair_value_string(nvp, &value);
845	ASSERT(rc == 0);
846	textbuf_strcpy(itb, value);
847
848	/* Add trailing 0x00 */
849	textbuf_terminate_kvpair(itb);
850
851	return (0);
852}
853
854
855/* ARGSUSED */
856static int
857idm_itextbuf_add_boolean(nvpair_t *nvp,
858    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
859{
860	char		*key_name;
861	boolean_t	value;
862	int	rc;
863
864	/* Start with the key name */
865	key_name = nvpair_name(nvp);
866	textbuf_strcpy(itb, key_name);
867
868	/* Add separator */
869	textbuf_append_char(itb, '=');
870
871	/* Add value */
872	rc = nvpair_value_boolean_value(nvp, &value);
873	ASSERT(rc == 0);
874	textbuf_strcpy(itb, value ? "Yes" : "No");
875
876	/* Add trailing 0x00 */
877	textbuf_terminate_kvpair(itb);
878
879	return (0);
880}
881
882/* ARGSUSED */
883static int
884idm_itextbuf_add_binary(nvpair_t *nvp,
885    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
886{
887	char		*key_name;
888	unsigned char	*value;
889	unsigned int	len;
890	unsigned long	n;
891	int	rc;
892
893	/* Start with the key name */
894	key_name = nvpair_name(nvp);
895	textbuf_strcpy(itb, key_name);
896
897	/* Add separator */
898	textbuf_append_char(itb, '=');
899
900	/* Add value */
901	rc = nvpair_value_byte_array(nvp, &value, &len);
902	ASSERT(rc == 0);
903
904	textbuf_strcpy(itb, "0x");
905
906	while (len > 0) {
907		n = *value++;
908		len--;
909
910		textbuf_append_char(itb, idm_hex_to_ascii[(n >> 4) & 0xf]);
911		textbuf_append_char(itb, idm_hex_to_ascii[n & 0xf]);
912	}
913
914	/* Add trailing 0x00 */
915	textbuf_terminate_kvpair(itb);
916
917	return (0);
918}
919
920/* ARGSUSED */
921static int
922idm_itextbuf_add_large_numerical(nvpair_t *nvp,
923    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
924{
925	ASSERT(0);
926	return (0);
927}
928
929/* ARGSUSED */
930static int
931idm_itextbuf_add_numerical(nvpair_t *nvp,
932    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
933{
934	char		*key_name;
935	uint64_t	value;
936	int	rc;
937	char		str[16];
938
939	/* Start with the key name */
940	key_name = nvpair_name(nvp);
941	textbuf_strcpy(itb, key_name);
942
943	/* Add separator */
944	textbuf_append_char(itb, '=');
945
946	/* Add value */
947	rc = nvpair_value_uint64(nvp, &value);
948	ASSERT(rc == 0);
949	(void) sprintf(str, "%llu", (u_longlong_t)value);
950	textbuf_strcpy(itb, str);
951
952	/* Add trailing 0x00 */
953	textbuf_terminate_kvpair(itb);
954
955	return (0);
956}
957
958/* ARGSUSED */
959static int
960idm_itextbuf_add_numeric_range(nvpair_t *nvp,
961    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
962{
963	ASSERT(0);
964	return (0);
965}
966
967/* ARGSUSED */
968static int
969idm_itextbuf_add_list_of_values(nvpair_t *nvp,
970    const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
971{
972	char		*key_name;
973	nvpair_t	*vchoice = NULL;
974	char		*vchoice_string = NULL;
975	int		rc;
976
977	/* Start with the key name */
978	key_name = nvpair_name(nvp);
979	textbuf_strcpy(itb, key_name);
980
981	/* Add separator */
982	textbuf_append_char(itb, '=');
983
984	/* Add value choices */
985	vchoice = idm_get_next_listvalue(nvp, NULL);
986	while (vchoice != NULL) {
987		rc = nvpair_value_string(vchoice, &vchoice_string);
988		ASSERT(rc == 0);
989		textbuf_strcpy(itb, vchoice_string);
990		vchoice = idm_get_next_listvalue(nvp, vchoice);
991		if (vchoice != NULL) {
992			/* Add ',' between choices */
993			textbuf_append_char(itb, ',');
994		}
995	}
996
997	/* Add trailing 0x00 */
998	textbuf_terminate_kvpair(itb);
999
1000	return (0);
1001}
1002
1003
1004static void
1005textbuf_makeroom(idm_textbuf_t *itb, int size)
1006{
1007	char	*new_mem;
1008	int	new_mem_len;
1009
1010	if (itb->itb_mem == NULL) {
1011		itb->itb_mem_len = MAX(TEXTBUF_CHUNKSIZE, size);
1012		itb->itb_mem = kmem_alloc(itb->itb_mem_len, KM_SLEEP);
1013	} else if ((itb->itb_offset + size) > itb->itb_mem_len) {
1014		new_mem_len = itb->itb_mem_len + MAX(TEXTBUF_CHUNKSIZE, size);
1015		new_mem = kmem_alloc(new_mem_len, KM_SLEEP);
1016		bcopy(itb->itb_mem, new_mem, itb->itb_mem_len);
1017		kmem_free(itb->itb_mem, itb->itb_mem_len);
1018		itb->itb_mem = new_mem;
1019		itb->itb_mem_len = new_mem_len;
1020	}
1021}
1022
1023static void
1024textbuf_memcpy(idm_textbuf_t *itb, void *mem, int mem_len)
1025{
1026	textbuf_makeroom(itb, mem_len);
1027	(void) memcpy(itb->itb_mem + itb->itb_offset, mem, mem_len);
1028	itb->itb_offset += mem_len;
1029}
1030
1031static void
1032textbuf_strcpy(idm_textbuf_t *itb, char *str)
1033{
1034	textbuf_memcpy(itb, str, strlen(str));
1035}
1036
1037static void
1038textbuf_append_char(idm_textbuf_t *itb, char c)
1039{
1040	textbuf_makeroom(itb, sizeof (char));
1041	*(itb->itb_mem + itb->itb_offset) = c;
1042	itb->itb_offset++;
1043}
1044
1045static void
1046textbuf_terminate_kvpair(idm_textbuf_t *itb)
1047{
1048	textbuf_append_char(itb, '\0');
1049}
1050
1051static int
1052idm_ascii_to_hex(char *enc_hex_byte, uint8_t *bin_val)
1053{
1054	uint8_t nibble1, nibble2;
1055	char enc_char = *enc_hex_byte;
1056
1057	if (enc_char >= '0' && enc_char <= '9') {
1058		nibble1 = (enc_char - '0');
1059	} else if (enc_char >= 'A' && enc_char <= 'F') {
1060		nibble1 = (0xA + (enc_char - 'A'));
1061	} else if (enc_char >= 'a' && enc_char <= 'f') {
1062		nibble1 = (0xA + (enc_char - 'a'));
1063	} else {
1064		return (EINVAL);
1065	}
1066
1067	enc_hex_byte++;
1068	enc_char = *enc_hex_byte;
1069
1070	if (enc_char >= '0' && enc_char <= '9') {
1071		nibble2 = (enc_char - '0');
1072	} else if (enc_char >= 'A' && enc_char <= 'F') {
1073		nibble2 = (0xA + (enc_char - 'A'));
1074	} else if (enc_char >= 'a' && enc_char <= 'f') {
1075		nibble2 = (0xA + (enc_char - 'a'));
1076	} else {
1077		return (EINVAL);
1078	}
1079
1080	*bin_val = (nibble1 << 4) | nibble2;
1081
1082	return (0);
1083}
1084
1085
1086static int idm_base16_str_to_binary(char *hstr, int hstr_len,
1087    uint8_t *binary_array, int binary_length)
1088{
1089	char	tmpstr[2];
1090	uchar_t *binary_scan;
1091
1092	binary_scan = binary_array;
1093
1094	/*
1095	 * If the length of the encoded ascii hex value is a multiple
1096	 * of two then every two ascii characters correspond to a hex
1097	 * byte.  If the length of the value is not a multiple of two
1098	 * then the first character is the first hex byte and then for
1099	 * the remaining of the string every two ascii characters
1100	 * correspond to a hex byte
1101	 */
1102	if ((hstr_len % 2) != 0) {
1103
1104		tmpstr[0] = '0';
1105		tmpstr[1] = *hstr;
1106
1107		if (idm_ascii_to_hex(tmpstr, binary_scan) != 0) {
1108			return (EINVAL);
1109		}
1110
1111		hstr++;
1112		binary_scan++;
1113	}
1114
1115	while (binary_scan != binary_array + binary_length) {
1116		if (idm_ascii_to_hex(hstr, binary_scan) != 0) {
1117			return (EINVAL);
1118		}
1119
1120		hstr += 2;
1121		binary_scan++;
1122	}
1123
1124	return (0);
1125}
1126
1127static size_t
1128idm_strnlen(const char *str, size_t maxlen)
1129{
1130	const char *ptr;
1131
1132	ptr = memchr(str, 0, maxlen);
1133	if (ptr == NULL)
1134		return (maxlen);
1135
1136	return ((uintptr_t)ptr - (uintptr_t)str);
1137}
1138
1139
1140size_t
1141idm_strcspn(const char *string, const char *charset)
1142{
1143	const char *p, *q;
1144
1145	for (q = string; *q != '\0'; ++q) {
1146		for (p = charset; *p != '\0' && *p != *q; )
1147			p++;
1148		if (*p != '\0') {
1149			break;
1150		}
1151	}
1152	return ((uintptr_t)q - (uintptr_t)string);
1153}
1154
1155/*
1156 * We allow a list of choices to be represented as a single nvpair
1157 * (list with one value choice), or as an nvlist with a single nvpair
1158 * (also a list with on value choice), or as an nvlist with multiple
1159 * nvpairs (a list with multiple value choices).  This function implements
1160 * the "get next" functionality regardless of the choice list structure.
1161 *
1162 * nvpair_t's that contain choices are always strings.
1163 */
1164nvpair_t *
1165idm_get_next_listvalue(nvpair_t *value_list, nvpair_t *curr_nvp)
1166{
1167	nvpair_t	*result;
1168	nvlist_t	*nvl;
1169	int		nvrc;
1170	data_type_t	nvp_type;
1171
1172	nvp_type = nvpair_type(value_list);
1173
1174	switch (nvp_type) {
1175	case DATA_TYPE_NVLIST:
1176		nvrc = nvpair_value_nvlist(value_list, &nvl);
1177		ASSERT(nvrc == 0);
1178		result = nvlist_next_nvpair(nvl, curr_nvp);
1179		break;
1180	case DATA_TYPE_STRING:
1181		/* Single choice */
1182		if (curr_nvp == NULL) {
1183			result = value_list;
1184		} else {
1185			result = NULL;
1186		}
1187		break;
1188	default:
1189		ASSERT(0); /* Malformed choice list */
1190		result = NULL;
1191		break;
1192	}
1193
1194	return (result);
1195}
1196
1197kv_status_t
1198idm_nvstat_to_kvstat(int nvrc)
1199{
1200	kv_status_t result;
1201	switch (nvrc) {
1202	case 0:
1203		result = KV_HANDLED;
1204		break;
1205	case ENOMEM:
1206		result = KV_NO_RESOURCES;
1207		break;
1208	case EINVAL:
1209		result = KV_VALUE_ERROR;
1210		break;
1211	case EFAULT:
1212	case ENOTSUP:
1213	default:
1214		result = KV_INTERNAL_ERROR;
1215		break;
1216	}
1217
1218	return (result);
1219}
1220
1221void
1222idm_kvstat_to_error(kv_status_t kvrc, uint8_t *class, uint8_t *detail)
1223{
1224	switch (kvrc) {
1225	case KV_HANDLED:
1226	case KV_HANDLED_NO_TRANSIT:
1227		*class = ISCSI_STATUS_CLASS_SUCCESS;
1228		*detail = ISCSI_LOGIN_STATUS_ACCEPT;
1229		break;
1230	case KV_UNHANDLED:
1231	case KV_TARGET_ONLY:
1232		/* protocol error */
1233		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
1234		*detail = ISCSI_LOGIN_STATUS_INVALID_REQUEST;
1235		break;
1236	case KV_VALUE_ERROR:
1237		/* invalid value */
1238		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
1239		*detail = ISCSI_LOGIN_STATUS_INIT_ERR;
1240		break;
1241	case KV_NO_RESOURCES:
1242		/* no memory */
1243		*class = ISCSI_STATUS_CLASS_TARGET_ERR;
1244		*detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
1245		break;
1246	case KV_MISSING_FIELDS:
1247		/* key/value pair(s) missing */
1248		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
1249		*detail = ISCSI_LOGIN_STATUS_MISSING_FIELDS;
1250		break;
1251	case KV_AUTH_FAILED:
1252		/* authentication failed */
1253		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
1254		*detail = ISCSI_LOGIN_STATUS_AUTH_FAILED;
1255		break;
1256	default:
1257		/* target error */
1258		*class = ISCSI_STATUS_CLASS_TARGET_ERR;
1259		*detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
1260		break;
1261	}
1262}
1263
1264int
1265idm_nvlist_add_keyvalue(nvlist_t *nvl,
1266    char *key, int keylen, char *value)
1267{
1268	const idm_kv_xlate_t *ikvx;
1269
1270	ikvx = idm_lookup_kv_xlate(key, keylen);
1271
1272	if (ikvx->ik_key_id == KI_MAX_KEY) {
1273		char *nkey;
1274		int rc;
1275		size_t len;
1276
1277		/*
1278		 * key is not a NULL terminated string, so create one
1279		 */
1280		len = (size_t)(keylen+1);
1281		nkey = kmem_zalloc(len, KM_SLEEP);
1282		(void) strncpy(nkey, key, len-1);
1283		rc = nvlist_add_string(nvl, nkey, value);
1284		kmem_free(nkey, len);
1285		return (rc);
1286	}
1287
1288	return (idm_nvlist_add_kv(nvl, ikvx, value));
1289}
1290
1291int
1292idm_nvlist_add_id(nvlist_t *nvl, iscsikey_id_t kv_id, char *value)
1293{
1294	int i;
1295	for (i = 0; i < KI_MAX_KEY; i++) {
1296		if (idm_kvpair_xlate[i].ik_key_id == kv_id) {
1297			return
1298			    (idm_nvlist_add_kv(nvl,
1299			    &idm_kvpair_xlate[i], value));
1300		}
1301	}
1302	return (EFAULT);
1303}
1304
1305char *
1306idm_id_to_name(iscsikey_id_t kv_id)
1307{
1308	int i;
1309	for (i = 0; i < KI_MAX_KEY; i++) {
1310		if (idm_kvpair_xlate[i].ik_key_id == kv_id) {
1311			return (idm_kvpair_xlate[i].ik_key_name);
1312		}
1313	}
1314	return (NULL);
1315}
1316
1317/*
1318 * return the value in a buffer that must be freed by the caller
1319 */
1320char *
1321idm_nvpair_value_to_textbuf(nvpair_t *nvp)
1322{
1323	int rv, len;
1324	idm_textbuf_t itb;
1325	char *str;
1326
1327	bzero(&itb, sizeof (itb));
1328	rv = idm_itextbuf_add_nvpair(nvp, &itb);
1329	if (rv != 0)
1330		return (NULL);
1331	str = kmem_alloc(itb.itb_mem_len, KM_SLEEP);
1332	len = idm_strcspn(itb.itb_mem, "=");
1333	if (len > strlen(itb.itb_mem)) {
1334		kmem_free(itb.itb_mem, itb.itb_mem_len);
1335		return (NULL);
1336	}
1337	(void) strcpy(str, &itb.itb_mem[len+1]);
1338	/* free the allocation done in idm_textbuf_add_nvpair */
1339	kmem_free(itb.itb_mem, itb.itb_mem_len);
1340	return (str);
1341}
1342
1343/*
1344 * build an iscsi text buffer - the memory gets freed in
1345 * idm_itextbuf_free
1346 */
1347void *
1348idm_nvlist_to_itextbuf(nvlist_t *nvl)
1349{
1350	idm_textbuf_t *itb;
1351	char		*textbuf;
1352	int		validlen, textbuflen;
1353
1354	if (idm_nvlist_to_textbuf(nvl, &textbuf, &textbuflen,
1355	    &validlen) != IDM_STATUS_SUCCESS) {
1356		return (NULL);
1357	}
1358	itb = kmem_zalloc(sizeof (idm_textbuf_t), KM_SLEEP);
1359	ASSERT(itb != NULL);
1360	itb->itb_mem = textbuf;
1361	itb->itb_mem_len = textbuflen;
1362	itb->itb_offset = validlen;
1363	return ((void *)itb);
1364}
1365
1366/*
1367 * Copy as much of the text buffer as will fit in the pdu.
1368 * The first call to this routine should send
1369 * a NULL bufptr. Subsequent calls send in the buffer returned.
1370 * Call this routine until the string returned is NULL
1371 */
1372char *
1373idm_pdu_init_text_data(idm_pdu_t *pdu, void *arg,
1374    int max_xfer_len, char *bufptr, int *transit)
1375{
1376	char		*start_ptr, *end_ptr, *ptr;
1377	idm_textbuf_t	*itb = arg;
1378	iscsi_hdr_t	*ihp = pdu->isp_hdr;
1379	int		send = 0;
1380
1381	ASSERT(itb != NULL);
1382	ASSERT(pdu != NULL);
1383	ASSERT(transit != NULL);
1384	if (bufptr == NULL) {
1385		/* first call - check the length */
1386		if (itb->itb_offset <= max_xfer_len) {
1387			/*
1388			 * the entire text buffer fits in the pdu
1389			 */
1390			bcopy((uint8_t *)itb->itb_mem, pdu->isp_data,
1391			    (size_t)itb->itb_offset);
1392			pdu->isp_datalen = itb->itb_offset;
1393			ihp->flags &= ~ISCSI_FLAG_TEXT_CONTINUE;
1394			*transit = 1;
1395			return (NULL);
1396		}
1397		/* we have more data than will fit in one pdu */
1398		start_ptr = itb->itb_mem;
1399		end_ptr = &itb->itb_mem[max_xfer_len - 1];
1400
1401	} else {
1402		uint_t len;
1403
1404		len =  (uintptr_t)&itb->itb_mem[itb->itb_offset] -
1405		    (uintptr_t)bufptr;
1406		if (len <= max_xfer_len) {
1407			/*
1408			 * the remaining text fits in the pdu
1409			 */
1410			bcopy(bufptr, pdu->isp_data, (size_t)len);
1411			pdu->isp_datalen = len;
1412			ihp->flags &= ~ISCSI_FLAG_TEXT_CONTINUE;
1413			*transit = 1;
1414			return (NULL);
1415		}
1416		/* we still have more data then will fit in one pdu */
1417		start_ptr = bufptr;
1418		end_ptr = &bufptr[max_xfer_len - 1];
1419	}
1420	/* break after key, after =, after the value or after '\0' */
1421	ptr = end_ptr;
1422	if (end_ptr + 1 <= &itb->itb_mem[itb->itb_offset]) {
1423		/* if next char is an '=' or '\0' send it */
1424		if (*(end_ptr + 1) == '=' || *(end_ptr + 1) == '\0') {
1425			send = 1;
1426		}
1427	}
1428	if (!send) {
1429		while (*ptr != '\0' && *ptr != '=' && ptr != start_ptr) {
1430			ptr--;
1431		}
1432	}
1433	bcopy(start_ptr, pdu->isp_data,
1434	    ((uintptr_t)ptr - (uintptr_t)start_ptr) + 1);
1435	pdu->isp_datalen = ((uintptr_t)ptr - (uintptr_t)start_ptr) + 1;
1436	ihp->flags |= ISCSI_FLAG_TEXT_CONTINUE;
1437	*transit = 0;
1438	return (++ptr);
1439}
1440
1441void
1442idm_itextbuf_free(void *arg)
1443{
1444	idm_textbuf_t	*itb = arg;
1445	ASSERT(itb != NULL);
1446	kmem_free(itb->itb_mem, itb->itb_mem_len);
1447	kmem_free(itb, sizeof (idm_textbuf_t));
1448}
1449
1450/*
1451 * Allocate an nvlist and poputlate with key=value from the pdu list.
1452 * NOTE: caller must free the list
1453 */
1454idm_status_t
1455idm_pdu_list_to_nvlist(list_t *pdu_list, nvlist_t **nvlist,
1456    uint8_t *error_detail)
1457{
1458	idm_pdu_t		*pdu, *next_pdu;
1459	boolean_t		split_kv = B_FALSE;
1460	char			*textbuf, *leftover_textbuf = NULL;
1461	int			textbuflen, leftover_textbuflen = 0;
1462	char			*split_kvbuf;
1463	int			split_kvbuflen, cont_fraglen;
1464	iscsi_login_hdr_t	*lh;
1465	int			rc;
1466	int			ret = IDM_STATUS_SUCCESS;
1467
1468	*error_detail = ISCSI_LOGIN_STATUS_ACCEPT;
1469	/* Allocate a new nvlist for request key/value pairs */
1470	rc = nvlist_alloc(nvlist, NV_UNIQUE_NAME,
1471	    KM_NOSLEEP);
1472	if (rc != 0) {
1473		*error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
1474		ret = IDM_STATUS_FAIL;
1475		goto cleanup;
1476	}
1477
1478	/*
1479	 * A login request can be split across multiple PDU's.  The state
1480	 * machine has collected all the PDU's that make up this login request
1481	 * and assembled them on the "icl_pdu_list" queue.  Process each PDU
1482	 * and convert the text keywords to nvlist form.
1483	 */
1484	pdu = list_head(pdu_list);
1485	while (pdu != NULL) {
1486		next_pdu = list_next(pdu_list, pdu);
1487
1488		lh = (iscsi_login_hdr_t *)pdu->isp_hdr;
1489
1490		textbuf = (char *)pdu->isp_data;
1491		textbuflen = pdu->isp_datalen;
1492		if (textbuflen == 0) {
1493			/* This shouldn't really happen but it could.. */
1494			list_remove(pdu_list, pdu);
1495			idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1496			pdu = next_pdu;
1497			continue;
1498		}
1499
1500		/*
1501		 * If we encountered a split key-value pair on the last
1502		 * PDU then handle it now by grabbing the remainder of the
1503		 * key-value pair from the next PDU and splicing them
1504		 * together.  Obviously on the first PDU this will never
1505		 * happen.
1506		 */
1507		if (split_kv) {
1508			cont_fraglen = idm_textbuf_to_firstfraglen(textbuf,
1509			    textbuflen);
1510			if (cont_fraglen == pdu->isp_datalen) {
1511				/*
1512				 * This key-value pair spans more than two
1513				 * PDU's.  We don't handle this.
1514				 */
1515				*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
1516				ret = IDM_STATUS_FAIL;
1517				goto cleanup;
1518			}
1519
1520			split_kvbuflen = leftover_textbuflen + cont_fraglen;
1521			split_kvbuf = kmem_alloc(split_kvbuflen, KM_NOSLEEP);
1522			if (split_kvbuf == NULL) {
1523				*error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
1524				ret = IDM_STATUS_FAIL;
1525				goto cleanup;
1526			}
1527
1528			bcopy(leftover_textbuf, split_kvbuf,
1529			    leftover_textbuflen);
1530			bcopy(textbuf,
1531			    (uint8_t *)split_kvbuf + leftover_textbuflen,
1532			    cont_fraglen);
1533
1534
1535			if (idm_textbuf_to_nvlist(*nvlist,
1536			    &split_kvbuf, &split_kvbuflen) != 0) {
1537				/*
1538				 * Need to handle E2BIG case, indicating that
1539				 * a key-value pair is split across multiple
1540				 * PDU's.
1541				 */
1542				kmem_free(split_kvbuf, split_kvbuflen);
1543
1544				*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
1545				ret = IDM_STATUS_FAIL;
1546				goto cleanup;
1547			}
1548
1549			ASSERT(split_kvbuflen != 0);
1550			kmem_free(split_kvbuf, split_kvbuflen);
1551
1552			/* Now handle the remainder of the PDU as normal */
1553			textbuf += (cont_fraglen + 1);
1554			textbuflen -= (cont_fraglen + 1);
1555		}
1556
1557		/*
1558		 * Convert each key-value pair in the text buffer to nvlist
1559		 * format.  If the list has already been created the nvpair
1560		 * elements will be added on to the existing list.  Otherwise
1561		 * a new nvlist will be created.
1562		 */
1563		if (idm_textbuf_to_nvlist(*nvlist,
1564		    &textbuf, &textbuflen) != 0) {
1565
1566			*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
1567			ret = IDM_STATUS_FAIL;
1568			goto cleanup;
1569		}
1570
1571		ASSERT(
1572		    ((lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
1573		    (next_pdu != NULL)) ||
1574		    (!(lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
1575		    (next_pdu == NULL)));
1576
1577		if ((lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &
1578		    (textbuflen != 0)) {
1579			/*
1580			 * Key-value pair is split over two PDU's.  We
1581			 * assume it willl never be split over more than
1582			 * two PDU's.
1583			 */
1584			split_kv = B_TRUE;
1585			leftover_textbuf = textbuf;
1586			leftover_textbuflen = textbuflen;
1587		} else {
1588			split_kv = B_FALSE;
1589			if (textbuflen != 0) {
1590				/*
1591				 * Incomplete keyword but no additional
1592				 * PDU's.  This is a malformed login
1593				 * request.
1594				 */
1595				*error_detail =
1596				    ISCSI_LOGIN_STATUS_INVALID_REQUEST;
1597				ret = IDM_STATUS_FAIL;
1598				goto cleanup;
1599			}
1600		}
1601
1602		list_remove(pdu_list, pdu);
1603		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1604		pdu = next_pdu;
1605	}
1606
1607cleanup:
1608
1609	/*
1610	 * Free any remaining PDUs on the list. This will only
1611	 * happen if there were errors encountered during
1612	 * processing of the textbuf.
1613	 */
1614	pdu = list_head(pdu_list);
1615	while (pdu != NULL) {
1616		next_pdu = list_next(pdu_list, pdu);
1617		list_remove(pdu_list, pdu);
1618		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1619		pdu = next_pdu;
1620	}
1621
1622	/*
1623	 * If there were no errors, we have a complete nvlist representing
1624	 * all the iSCSI key-value pairs in the login request PDU's
1625	 * that make up this request.
1626	 */
1627	return (ret);
1628}
1629