xref: /spdk/lib/iscsi/param.c (revision 2d18887fbd6fe5727abbdaa0a17b37d51238f6e3)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
5  *   Copyright (c) Intel Corporation.
6  *   All rights reserved.
7  *
8  *   Redistribution and use in source and binary forms, with or without
9  *   modification, are permitted provided that the following conditions
10  *   are met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in
16  *       the documentation and/or other materials provided with the
17  *       distribution.
18  *     * Neither the name of Intel Corporation nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "spdk/stdinc.h"
36 
37 #include "spdk/string.h"
38 #include "iscsi/iscsi.h"
39 #include "iscsi/param.h"
40 #include "iscsi/conn.h"
41 #include "spdk/string.h"
42 
43 #include "spdk_internal/log.h"
44 
45 #define MAX_TMPBUF 1024
46 
47 /* whose value may be bigger than 255 */
48 static const char *non_simple_value_params[] = {
49 	"CHAP_C",
50 	"CHAP_R",
51 	NULL,
52 };
53 
54 void
55 spdk_iscsi_param_free(struct iscsi_param *params)
56 {
57 	struct iscsi_param *param, *next_param;
58 
59 	if (params == NULL)
60 		return;
61 	for (param = params; param != NULL; param = next_param) {
62 		next_param = param->next;
63 		if (param->list)
64 			free(param->list);
65 		free(param->val);
66 		free(param->key);
67 		free(param);
68 	}
69 }
70 
71 static int
72 spdk_iscsi_find_key_in_array(const char *key, const char *array[])
73 {
74 	int i;
75 
76 	for (i = 0; array[i] != NULL; i++) {
77 		if (strcasecmp(key, array[i]) == 0)
78 			return 1;
79 	}
80 	return 0;
81 }
82 
83 struct iscsi_param *
84 spdk_iscsi_param_find(struct iscsi_param *params, const char *key)
85 {
86 	struct iscsi_param *param;
87 
88 	if (params == NULL || key == NULL)
89 		return NULL;
90 	for (param = params; param != NULL; param = param->next) {
91 		if (param->key != NULL && param->key[0] == key[0]
92 		    && strcasecmp(param->key, key) == 0) {
93 			return param;
94 		}
95 	}
96 	return NULL;
97 }
98 
99 int
100 spdk_iscsi_param_del(struct iscsi_param **params, const char *key)
101 {
102 	struct iscsi_param *param, *prev_param = NULL;
103 
104 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "del %s\n", key);
105 	if (params == NULL || key == NULL)
106 		return 0;
107 	for (param = *params; param != NULL; param = param->next) {
108 		if (param->key != NULL && param->key[0] == key[0]
109 		    && strcasecmp(param->key, key) == 0) {
110 			if (prev_param != NULL) {
111 				prev_param->next = param->next;
112 			} else {
113 				*params = param->next;
114 			}
115 			param->next = NULL;
116 			spdk_iscsi_param_free(param);
117 			return 0;
118 		}
119 		prev_param = param;
120 	}
121 	return -1;
122 }
123 
124 int
125 spdk_iscsi_param_add(struct iscsi_param **params, const char *key,
126 		     const char *val, const char *list, int type)
127 {
128 	struct iscsi_param *param, *last_param;
129 
130 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "add %s=%s, list=[%s], type=%d\n",
131 		      key, val, list, type);
132 	if (key == NULL)
133 		return -1;
134 
135 	param = spdk_iscsi_param_find(*params, key);
136 	if (param != NULL)
137 		spdk_iscsi_param_del(params, key);
138 
139 	param = malloc(sizeof * param);
140 	if (!param) {
141 		perror("param");
142 		return -ENOMEM;
143 	}
144 
145 	memset(param, 0, sizeof * param);
146 	param->next = NULL;
147 	param->key = xstrdup(key);
148 	param->val = xstrdup(val);
149 	param->list = xstrdup(list);
150 	param->type = type;
151 
152 	last_param = *params;
153 	if (last_param != NULL) {
154 		while (last_param->next != NULL) {
155 			last_param = last_param->next;
156 		}
157 		last_param->next = param;
158 	} else {
159 		*params = param;
160 	}
161 
162 	return 0;
163 }
164 
165 int
166 spdk_iscsi_param_set(struct iscsi_param *params, const char *key,
167 		     const char *val)
168 {
169 	struct iscsi_param *param;
170 
171 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set %s=%s\n", key, val);
172 	param = spdk_iscsi_param_find(params, key);
173 	if (param == NULL) {
174 		SPDK_ERRLOG("no key %s\n", key);
175 		return -1;
176 	}
177 
178 	free(param->val);
179 
180 	param->val = xstrdup(val);
181 
182 	return 0;
183 }
184 
185 int
186 spdk_iscsi_param_set_int(struct iscsi_param *params, const char *key, uint32_t val)
187 {
188 	char buf[MAX_TMPBUF];
189 	struct iscsi_param *param;
190 
191 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set %s=%d\n", key, val);
192 	param = spdk_iscsi_param_find(params, key);
193 	if (param == NULL) {
194 		SPDK_ERRLOG("no key %s\n", key);
195 		return -1;
196 	}
197 
198 	free(param->val);
199 	snprintf(buf, sizeof buf, "%d", val);
200 
201 	param->val = strdup(buf);
202 
203 	return 0;
204 }
205 
206 /**
207  * Parse a single KEY=VAL pair
208  *
209  * data = "KEY=VAL<NUL>"
210  */
211 static int
212 spdk_iscsi_parse_param(struct iscsi_param **params, const uint8_t *data)
213 {
214 	int rc;
215 	uint8_t *key_copy;
216 	const uint8_t *key_end, *val;
217 	int key_len, val_len;
218 	int max_len;
219 
220 	key_end = strchr(data, '=');
221 	if (!key_end) {
222 		SPDK_ERRLOG("'=' not found\n");
223 		return -1;
224 	}
225 
226 	key_len = key_end - data;
227 	if (key_len == 0) {
228 		SPDK_ERRLOG("Empty key\n");
229 		return -1;
230 	}
231 	/*
232 	 * RFC 7143 6.1
233 	 */
234 	if (key_len > ISCSI_TEXT_MAX_KEY_LEN) {
235 		SPDK_ERRLOG("Key name length is bigger than 63\n");
236 		return -1;
237 	}
238 
239 	key_copy = malloc(key_len + 1);
240 	if (!key_copy) {
241 		perror("key_copy");
242 		return -ENOMEM;
243 	}
244 
245 	memcpy(key_copy, data, key_len);
246 	key_copy[key_len] = '\0';
247 	/* check whether this key is duplicated */
248 	if (NULL != spdk_iscsi_param_find(*params, key_copy)) {
249 		SPDK_ERRLOG("Duplicated Key %s\n", key_copy);
250 		free(key_copy);
251 		return -1;
252 	}
253 
254 	val = key_end + 1; /* +1 to skip over the '=' */
255 	val_len = strlen(val);
256 	/*
257 	 * RFC 3720 5.1
258 	 * If not otherwise specified, the maximum length of a simple-value
259 	 * (not its encoded representation) is 255 bytes, not including the delimiter
260 	 * (comma or zero byte).
261 	 */
262 	/*
263 	 * comma or zero is counted in, otherwise we need to iterate each parameter
264 	 * value
265 	 */
266 	max_len = spdk_iscsi_find_key_in_array(key_copy, non_simple_value_params) ?
267 		  ISCSI_TEXT_MAX_VAL_LEN : ISCSI_TEXT_MAX_SIMPLE_VAL_LEN;
268 	if (val_len > max_len) {
269 		SPDK_ERRLOG("Overflow Val %d\n", val_len);
270 		free(key_copy);
271 		return -1;
272 	}
273 
274 	rc = spdk_iscsi_param_add(params, key_copy, val, NULL, 0);
275 	free(key_copy);
276 	if (rc < 0) {
277 		SPDK_ERRLOG("iscsi_param_add() failed\n");
278 		return -1;
279 	}
280 
281 	/* return number of bytes consumed
282 	 * +1 for '=' and +1 for NUL
283 	 */
284 	return key_len + 1 + val_len + 1;
285 }
286 
287 /**
288  * Parse a sequence of KEY=VAL pairs.
289  *
290  * \param data "KEY=VAL<NUL>KEY=VAL<NUL>..."
291  * \param len length of data in bytes
292  */
293 int
294 spdk_iscsi_parse_params(struct iscsi_param **params, const uint8_t *data,
295 			int len, bool cbit_enabled, char **partial_parameter)
296 {
297 	int rc, offset = 0;
298 	char *p;
299 	int i;
300 
301 	/* strip the partial text parameters if previous PDU have C enabled */
302 	if (partial_parameter && *partial_parameter) {
303 		for (i = 0; i < len && data[i] != '\0'; i++)
304 			;
305 		p = spdk_sprintf_alloc("%s%s", *partial_parameter, (const char *)data);
306 		if (!p) {
307 			return -1;
308 		}
309 		rc = spdk_iscsi_parse_param(params, p);
310 		free(p);
311 		if (rc < 0)
312 			return -1;
313 		free(*partial_parameter);
314 		*partial_parameter = NULL;
315 
316 		data = data + i + 1;
317 		len = len - (i + 1);
318 	}
319 
320 	/* strip the partial text parameters if C bit is enabled */
321 	if (cbit_enabled) {
322 		if (partial_parameter == NULL) {
323 			SPDK_ERRLOG("C bit set but no partial parameters provided\n");
324 			return -1;
325 		}
326 
327 		/*
328 		 * reverse iterate the string from the tail not including '\0'
329 		 * index of last '\0' is len -1.
330 		 */
331 		for (i = len - 2; data[i] != '\0' && i > 0; i--)
332 			;
333 		*partial_parameter = xstrdup(&data[i == 0 ? 0 : i + 1]);
334 		len = (i == 0 ? 0 : i + 1);
335 	}
336 
337 	while (offset < len && data[offset] != '\0') {
338 		rc = spdk_iscsi_parse_param(params, data + offset);
339 		if (rc < 0) {
340 			return -1;
341 		}
342 		offset += rc;
343 	}
344 	return 0;
345 }
346 
347 char *
348 spdk_iscsi_param_get_val(struct iscsi_param *params, const char *key)
349 {
350 	struct iscsi_param *param;
351 
352 	param = spdk_iscsi_param_find(params, key);
353 	if (param == NULL)
354 		return NULL;
355 	return param->val;
356 }
357 
358 int
359 spdk_iscsi_param_eq_val(struct iscsi_param *params, const char *key,
360 			const char *val)
361 {
362 	struct iscsi_param *param;
363 
364 	param = spdk_iscsi_param_find(params, key);
365 	if (param == NULL)
366 		return 0;
367 	if (strcasecmp(param->val, val) == 0)
368 		return 1;
369 	return 0;
370 }
371 
372 struct iscsi_param_table {
373 	const char *key;
374 	const char *val;
375 	const char *list;
376 	int type;
377 };
378 
379 static const struct iscsi_param_table conn_param_table[] = {
380 	{ "HeaderDigest", "None", "CRC32C,None", ISPT_LIST },
381 	{ "DataDigest", "None", "CRC32C,None", ISPT_LIST },
382 	{ "MaxRecvDataSegmentLength", "8192", "512,16777215", ISPT_NUMERICAL_DECLARATIVE },
383 	{ "OFMarker", "No", "Yes,No", ISPT_BOOLEAN_AND },
384 	{ "IFMarker", "No", "Yes,No", ISPT_BOOLEAN_AND },
385 	{ "OFMarkInt", "1", "1,65535", ISPT_NUMERICAL_MIN },
386 	{ "IFMarkInt", "1", "1,65535", ISPT_NUMERICAL_MIN },
387 	{ "AuthMethod", "None", "CHAP,None", ISPT_LIST },
388 	{ "CHAP_A", "5", "5", ISPT_LIST },
389 	{ "CHAP_N", "", "", ISPT_DECLARATIVE },
390 	{ "CHAP_R", "", "", ISPT_DECLARATIVE },
391 	{ "CHAP_I", "", "", ISPT_DECLARATIVE },
392 	{ "CHAP_C", "", "", ISPT_DECLARATIVE },
393 	{ NULL, NULL, NULL, ISPT_INVALID },
394 };
395 
396 static const struct iscsi_param_table sess_param_table[] = {
397 	{ "MaxConnections", "1", "1,65535", ISPT_NUMERICAL_MIN },
398 #if 0
399 	/* need special handling */
400 	{ "SendTargets", "", "", ISPT_DECLARATIVE },
401 #endif
402 	{ "TargetName", "", "", ISPT_DECLARATIVE },
403 	{ "InitiatorName", "", "", ISPT_DECLARATIVE },
404 	{ "TargetAlias", "", "", ISPT_DECLARATIVE },
405 	{ "InitiatorAlias", "", "", ISPT_DECLARATIVE },
406 	{ "TargetAddress", "", "", ISPT_DECLARATIVE },
407 	{ "TargetPortalGroupTag", "1", "1,65535", ISPT_NUMERICAL_DECLARATIVE },
408 	{ "InitialR2T", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
409 	{ "ImmediateData", "Yes", "Yes,No", ISPT_BOOLEAN_AND },
410 	{ "MaxBurstLength", "262144", "512,16777215", ISPT_NUMERICAL_MIN },
411 	{ "FirstBurstLength", "65536", "512,16777215", ISPT_NUMERICAL_MIN },
412 	{ "DefaultTime2Wait", "2", "0,3600", ISPT_NUMERICAL_MAX },
413 	{ "DefaultTime2Retain", "20", "0,3600", ISPT_NUMERICAL_MIN },
414 	{ "MaxOutstandingR2T", "1", "1,65536", ISPT_NUMERICAL_MIN },
415 	{ "DataPDUInOrder", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
416 	{ "DataSequenceInOrder", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
417 	{ "ErrorRecoveryLevel", "0", "0,2", ISPT_NUMERICAL_MIN },
418 	{ "SessionType", "Normal", "Normal,Discovery", ISPT_DECLARATIVE },
419 	{ NULL, NULL, NULL, ISPT_INVALID },
420 };
421 
422 static int
423 spdk_iscsi_params_init_internal(struct iscsi_param **params,
424 				const struct iscsi_param_table *table)
425 {
426 	int rc;
427 	int i;
428 	struct iscsi_param *param;
429 
430 	for (i = 0; table[i].key != NULL; i++) {
431 		rc = spdk_iscsi_param_add(params, table[i].key, table[i].val,
432 					  table[i].list, table[i].type);
433 		if (rc < 0) {
434 			SPDK_ERRLOG("iscsi_param_add() failed\n");
435 			return -1;
436 		}
437 		param = spdk_iscsi_param_find(*params, table[i].key);
438 		if (param != NULL) {
439 			param->state_index = i;
440 		} else {
441 			SPDK_ERRLOG("spdk_iscsi_param_find() failed\n");
442 			return -1;
443 		}
444 	}
445 
446 	return 0;
447 }
448 
449 int
450 spdk_iscsi_conn_params_init(struct iscsi_param **params)
451 {
452 	return spdk_iscsi_params_init_internal(params, &conn_param_table[0]);
453 }
454 
455 int
456 spdk_iscsi_sess_params_init(struct iscsi_param **params)
457 {
458 	return spdk_iscsi_params_init_internal(params, &sess_param_table[0]);
459 }
460 
461 static const char *chap_type[] = {
462 	"CHAP_A",
463 	"CHAP_N",
464 	"CHAP_R",
465 	"CHAP_I",
466 	"CHAP_C",
467 	NULL,
468 };
469 
470 static const char *discovery_ignored_param[] = {
471 	"MaxConnections",
472 	"InitialR2T",
473 	"ImmediateData",
474 	"MaxBurstLength",
475 	"FirstBurstLength"
476 	"MaxOutstandingR2T",
477 	"DataPDUInOrder",
478 	NULL,
479 };
480 
481 static const char *multi_negot_conn_params[] = {
482 	"MaxRecvDataSegmentLength",
483 	NULL,
484 };
485 
486 /* The following params should be declared by target */
487 static const char *target_declarative_params[] = {
488 	"TargetAlias",
489 	"TargetAddress",
490 	"TargetPortalGroupTag",
491 	NULL,
492 };
493 
494 /* This function is used to contruct the data from the special param (e.g.,
495  * MaxRecvDataSegmentLength)
496  * return:
497  * normal: the total len of the data
498  * error: -1
499  */
500 static int
501 spdk_iscsi_special_param_construction(struct spdk_iscsi_conn *conn,
502 				      struct iscsi_param *param,
503 				      bool FirstBurstLength_flag, char *data,
504 				      int alloc_len, int total)
505 {
506 	int len;
507 	struct iscsi_param *param_first;
508 	struct iscsi_param *param_max;
509 	uint32_t FirstBurstLength;
510 	uint32_t MaxBurstLength;
511 	char *val;
512 
513 	val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
514 	if (!val) {
515 		perror("val");
516 		return -ENOMEM;
517 	}
518 
519 	if (strcasecmp(param->key, "MaxRecvDataSegmentLength") == 0) {
520 		/*
521 		 * MaxRecvDataSegmentLength is sent by both
522 		 *      initiator and target, but is declarative - meaning
523 		 *      each direction can have different values.
524 		 * So when MaxRecvDataSegmentLength is found in the
525 		 *      the parameter set sent from the initiator, add SPDK
526 		 *      iscsi target's MaxRecvDataSegmentLength value to
527 		 *      the returned parameter list.
528 		 */
529 		if (alloc_len - total < 1) {
530 			SPDK_ERRLOG("data space small %d\n", alloc_len);
531 			free(val);
532 			return -1;
533 		}
534 
535 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI,
536 			      "returning MaxRecvDataSegmentLength=%d\n",
537 			      g_spdk_iscsi.MaxRecvDataSegmentLength);
538 		len = snprintf((char *)data + total, alloc_len - total,
539 			       "MaxRecvDataSegmentLength=%d",
540 			       g_spdk_iscsi.MaxRecvDataSegmentLength);
541 		total += len + 1;
542 	}
543 
544 	if (strcasecmp(param->key, "MaxBurstLength") == 0 &&
545 	    !FirstBurstLength_flag) {
546 		if (alloc_len - total < 1) {
547 			SPDK_ERRLOG("data space small %d\n", alloc_len);
548 			free(val);
549 			return -1;
550 		}
551 
552 		param_first = spdk_iscsi_param_find(conn->sess->params,
553 						    "FirstBurstLength");
554 		if (param_first != NULL) {
555 			FirstBurstLength = (uint32_t)strtol(param_first->val, NULL, 10);
556 		} else {
557 			FirstBurstLength = SPDK_ISCSI_FIRST_BURST_LENGTH;
558 		}
559 		param_max = spdk_iscsi_param_find(conn->sess->params,
560 						  "MaxBurstLength");
561 		if (param_max != NULL) {
562 			MaxBurstLength = (uint32_t)strtol(param_max->val, NULL, 10);
563 		} else {
564 			MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH;
565 		}
566 
567 		if (FirstBurstLength > MaxBurstLength) {
568 			FirstBurstLength = MaxBurstLength;
569 			if (param_first != NULL) {
570 				free(param_first->val);
571 				snprintf(val, ISCSI_TEXT_MAX_VAL_LEN, "%d",
572 					 FirstBurstLength);
573 				param_first->val = xstrdup(val);
574 			}
575 		}
576 		len = snprintf((char *)data + total, alloc_len - total,
577 			       "FirstBurstLength=%d", FirstBurstLength);
578 		total += len + 1;
579 	}
580 
581 	free(val);
582 	return total;
583 
584 }
585 
586 /**
587  * spdk_iscsi_construct_data_from_param:
588  * To construct the data which will be returned to the initiator
589  * return: length of the negotiated data, -1 inidicates error;
590  */
591 static int
592 spdk_iscsi_construct_data_from_param(struct iscsi_param *param, char *new_val,
593 				     char *data, int alloc_len, int total)
594 
595 {
596 	int len;
597 
598 	if (param->type != ISPT_DECLARATIVE &&
599 	    param->type != ISPT_NUMERICAL_DECLARATIVE) {
600 		if (alloc_len - total < 1) {
601 			SPDK_ERRLOG("data space small %d\n", alloc_len);
602 			return -1;
603 		}
604 
605 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "negotiated %s=%s\n",
606 			      param->key, new_val);
607 		len = snprintf((char *)data + total, alloc_len - total, "%s=%s",
608 			       param->key, new_val);
609 		total += len + 1;
610 	}
611 	return total;
612 
613 }
614 
615 /**
616  * To negotiate param with
617  * type = ISPT_LIST
618  * return: the negotiated value of the key
619  */
620 static char *spdk_iscsi_negotiate_param_list(int *add_param_value,
621 		struct iscsi_param *param,
622 		char *valid_list, char *in_val,
623 		char *cur_val)
624 {
625 	char *val_start, *val_end;
626 	char *in_start, *in_end;
627 	int flag = 0;
628 
629 	if (add_param_value == NULL)
630 		return NULL;
631 
632 	in_start = in_val;
633 	do {
634 		if ((in_end = strchr(in_start, (int)',')) != NULL) {
635 			*in_end = '\0';
636 		}
637 		val_start = valid_list;
638 		do {
639 			if ((val_end = strchr(val_start, (int)',')) != NULL) {
640 				*val_end = '\0';
641 			}
642 			if (strcasecmp(in_start, val_start) == 0) {
643 				SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "match %s\n",
644 					      val_start);
645 				flag = 1;
646 				break;
647 			}
648 			if (val_end) {
649 				*val_end = ',';
650 				val_start = val_end + 1;
651 			}
652 		} while (val_end);
653 		if (flag)
654 			break;
655 		if (in_end) {
656 			*in_end = ',';
657 			in_start = in_end + 1;
658 		}
659 	} while (in_end);
660 
661 	return flag ? val_start : NULL;
662 }
663 
664 /**
665  * To negotiate param with
666  * type = ISPT_NUMERICAL_MIN/MAX, ISPT_NUMERICAL_DECLARATIVE
667  * return: the negotiated value of the key
668  */
669 static char *spdk_iscsi_negotiate_param_numerical(int *add_param_value,
670 		struct iscsi_param *param,
671 		char *valid_list, char *in_val,
672 		char *cur_val)
673 {
674 	char *valid_next;
675 	char *new_val = NULL;
676 	char *min_val, *max_val;
677 	int val_i, cur_val_i;
678 	int min_i, max_i;
679 
680 	if (add_param_value == NULL)
681 		return NULL;
682 
683 	val_i = (int)strtol(param->val, NULL, 10);
684 	/* check whether the key is FirstBurstLength, if that we use in_val */
685 	if (strcasecmp(param->key, "FirstBurstLength") == 0)
686 		val_i = (int)strtol(in_val, NULL, 10);
687 
688 	cur_val_i = (int)strtol(cur_val, NULL, 10);
689 	valid_next = valid_list;
690 	min_val = spdk_strsepq(&valid_next, ",");
691 	max_val = spdk_strsepq(&valid_next, ",");
692 	min_i = (min_val != NULL) ? (int)strtol(min_val, NULL, 10) : 0;
693 	max_i = (max_val != NULL) ? (int)strtol(max_val, NULL, 10) : 0;
694 	if (val_i < min_i || val_i > max_i) {
695 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "key %.64s reject\n", param->key);
696 		new_val = NULL;
697 	} else {
698 		switch (param->type) {
699 		case ISPT_NUMERICAL_MIN:
700 			if (val_i > cur_val_i) {
701 				val_i = cur_val_i;
702 			}
703 			break;
704 		case ISPT_NUMERICAL_MAX:
705 			if (val_i < cur_val_i) {
706 				val_i = cur_val_i;
707 			}
708 			break;
709 		default:
710 			break;
711 		}
712 		snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d", val_i);
713 		new_val = in_val;
714 	}
715 
716 	return new_val;
717 }
718 
719 /**
720  * To negotiate param with
721  * type = ISPT_BOOLEAN_OR, ISPT_BOOLEAN_AND
722  * return: the negotiated value of the key
723  */
724 static char *spdk_iscsi_negotiate_param_boolean(int *add_param_value,
725 		struct iscsi_param *param,
726 		char *in_val, char *cur_val,
727 		const char *value)
728 {
729 	char *new_val = NULL;
730 
731 	if (add_param_value == NULL)
732 		return NULL;
733 
734 	/* Make sure the val is Yes or No */
735 	if (!((strcasecmp(in_val, "Yes") == 0) ||
736 	      (strcasecmp(in_val, "No") == 0))) {
737 		/* unknown value */
738 		snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "Reject");
739 		new_val = in_val;
740 		*add_param_value = 1;
741 		return new_val;
742 	}
743 
744 	if (strcasecmp(cur_val, value) == 0) {
745 		snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", value);
746 		new_val = in_val;
747 	} else
748 		new_val = param->val;
749 
750 	return new_val;
751 }
752 
753 /**
754  * The entry function to handle each type of the param
755  * return value: the new negotiated value
756  */
757 static char *
758 spdk_iscsi_negotiate_param_all(int *add_param_value, struct iscsi_param *param,
759 			       char *valid_list, char *in_val, char *cur_val)
760 {
761 	char *new_val;
762 	switch (param->type) {
763 	case ISPT_LIST:
764 		new_val = spdk_iscsi_negotiate_param_list(add_param_value,
765 				param,
766 				valid_list,
767 				in_val,
768 				cur_val);
769 		break;
770 
771 	case ISPT_NUMERICAL_MIN:
772 	case ISPT_NUMERICAL_MAX:
773 	case ISPT_NUMERICAL_DECLARATIVE:
774 		new_val = spdk_iscsi_negotiate_param_numerical(add_param_value,
775 				param,
776 				valid_list,
777 				in_val,
778 				cur_val);
779 		break;
780 
781 	case ISPT_BOOLEAN_OR:
782 		new_val = spdk_iscsi_negotiate_param_boolean(add_param_value,
783 				param,
784 				in_val,
785 				cur_val,
786 				"Yes");
787 		break;
788 	case ISPT_BOOLEAN_AND:
789 		new_val = spdk_iscsi_negotiate_param_boolean(add_param_value,
790 				param,
791 				in_val,
792 				cur_val,
793 				"No");
794 		break;
795 
796 	default:
797 		snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
798 		new_val = in_val;
799 		break;
800 	}
801 
802 	return new_val;
803 }
804 
805 /**
806  * This function is used to judge whether the param is in session's params or
807  * connection's params
808  */
809 static int
810 spdk_iscsi_negotiate_param_init(struct spdk_iscsi_conn *conn,
811 				struct iscsi_param **cur_param_p,
812 				struct iscsi_param **params_dst_p,
813 				struct iscsi_param *param)
814 {
815 	int index;
816 
817 	*cur_param_p = spdk_iscsi_param_find(*params_dst_p, param->key);
818 	if (*cur_param_p == NULL) {
819 		*params_dst_p = conn->sess->params;
820 		*cur_param_p = spdk_iscsi_param_find(*params_dst_p, param->key);
821 		if (*cur_param_p == NULL) {
822 			if ((strncasecmp(param->key, "X-", 2) == 0) ||
823 			    (strncasecmp(param->key, "X#", 2) == 0)) {
824 				/* Extension Key */
825 				SPDK_DEBUGLOG(SPDK_TRACE_ISCSI,
826 					      "extension key %.64s\n",
827 					      param->key);
828 			} else {
829 				SPDK_ERRLOG("unknown key %.64s\n", param->key);
830 			}
831 			return 1;
832 		} else {
833 			index = (*cur_param_p)->state_index;
834 			if (conn->sess_param_state_negotiated[index] &&
835 			    !spdk_iscsi_find_key_in_array(param->key,
836 							  target_declarative_params))
837 				return SPDK_ISCSI_PARAMETER_EXCHANGE_NOT_ONCE;
838 			conn->sess_param_state_negotiated[index] = true;
839 		}
840 	} else {
841 		index = (*cur_param_p)->state_index;
842 		if (conn->conn_param_state_negotiated[index] &&
843 		    !spdk_iscsi_find_key_in_array(param->key,
844 						  multi_negot_conn_params))
845 			return SPDK_ISCSI_PARAMETER_EXCHANGE_NOT_ONCE;
846 		conn->conn_param_state_negotiated[index] = true;
847 	}
848 
849 	return 0;
850 }
851 
852 int
853 spdk_iscsi_negotiate_params(struct spdk_iscsi_conn *conn,
854 			    struct iscsi_param **params, uint8_t *data, int alloc_len,
855 			    int data_len)
856 {
857 	struct iscsi_param *param;
858 	struct iscsi_param *cur_param;
859 	char *valid_list, *in_val;
860 	char *cur_val;
861 	char *new_val;
862 	int discovery;
863 	int total;
864 	int rc;
865 	uint32_t FirstBurstLength;
866 	uint32_t MaxBurstLength;
867 	bool FirstBurstLength_flag = false;
868 	int type;
869 	total = data_len;
870 	if (alloc_len < 1) {
871 		return 0;
872 	}
873 	if (total > alloc_len) {
874 		total = alloc_len;
875 		data[total - 1] = '\0';
876 		return total;
877 	}
878 
879 	if (*params == NULL) {
880 		/* no input */
881 		return total;
882 	}
883 
884 	/* discovery? */
885 	discovery = 0;
886 	cur_param = spdk_iscsi_param_find(*params, "SessionType");
887 	if (cur_param == NULL) {
888 		cur_param = spdk_iscsi_param_find(conn->sess->params, "SessionType");
889 		if (cur_param == NULL) {
890 			/* no session type */
891 		} else {
892 			if (strcasecmp(cur_param->val, "Discovery") == 0) {
893 				discovery = 1;
894 			}
895 		}
896 	} else {
897 		if (strcasecmp(cur_param->val, "Discovery") == 0) {
898 			discovery = 1;
899 		}
900 	}
901 
902 	/* for temporary store */
903 	valid_list = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
904 	if (!valid_list) {
905 		perror("valid_list");
906 		return -ENOMEM;
907 	}
908 
909 	in_val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
910 	if (!in_val) {
911 		perror("in_val");
912 		free(valid_list);
913 		return -ENOMEM;
914 	}
915 
916 	cur_val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
917 	if (!cur_val) {
918 		perror("cur_val");
919 		free(valid_list);
920 		free(in_val);
921 		return -ENOMEM;
922 	}
923 
924 	/* To adjust the location of FirstBurstLength location and put it to
925 	 *  the end, then we can always firstly determine the MaxBurstLength
926 	 */
927 	param = spdk_iscsi_param_find(*params, "MaxBurstLength");
928 	if (param != NULL) {
929 		param = spdk_iscsi_param_find(*params, "FirstBurstLength");
930 
931 		/* check the existence of FirstBurstLength */
932 		if (param != NULL) {
933 			FirstBurstLength_flag = true;
934 			if (param->next != NULL) {
935 				snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
936 				type = param->type;
937 				spdk_iscsi_param_add(params, "FirstBurstLength",
938 						     in_val, NULL, type);
939 			}
940 		}
941 	}
942 
943 	for (param = *params; param != NULL; param = param->next) {
944 		struct iscsi_param *params_dst = conn->params;
945 		int add_param_value = 0;
946 		new_val = NULL;
947 		param->type = ISPT_INVALID;
948 
949 		/* sendtargets is special */
950 		if (strcasecmp(param->key, "SendTargets") == 0) {
951 			continue;
952 		}
953 		/* CHAP keys */
954 		if (spdk_iscsi_find_key_in_array(param->key, chap_type))
955 			continue;
956 
957 		/* 12.2, 12.10, 12.11, 12.13, 12.14, 12.17, 12.18, 12.19 */
958 		if (discovery &&
959 		    spdk_iscsi_find_key_in_array(param->key,
960 						 discovery_ignored_param)) {
961 			snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "Irrelevant");
962 			new_val = in_val;
963 			add_param_value = 1;
964 		} else {
965 			rc = spdk_iscsi_negotiate_param_init(conn,
966 							     &cur_param,
967 							     &params_dst,
968 							     param);
969 			if (rc < 0) {
970 				free(valid_list);
971 				free(in_val);
972 				free(cur_val);
973 				return rc;
974 			} else if (rc > 0) {
975 				snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "NotUnderstood");
976 				new_val = in_val;
977 				add_param_value = 1;
978 			} else {
979 				snprintf(valid_list, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", cur_param->list);
980 				snprintf(cur_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", cur_param->val);
981 				param->type = cur_param->type;
982 			}
983 		}
984 
985 		if (param->type > 0) {
986 			snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
987 
988 			/* "NotUnderstood" value shouldn't be assigned to "Understood" key */
989 			if (strcasecmp(in_val, "NotUnderstood") == 0) {
990 				free(in_val);
991 				free(valid_list);
992 				free(cur_val);
993 				return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
994 			}
995 
996 			if (strcasecmp(param->key, "FirstBurstLength") == 0) {
997 				FirstBurstLength = (uint32_t)strtol(param->val, NULL,
998 								    10);
999 				new_val = spdk_iscsi_param_get_val(conn->sess->params,
1000 								   "MaxBurstLength");
1001 				if (new_val != NULL) {
1002 					MaxBurstLength = (uint32_t) strtol(new_val, NULL,
1003 									   10);
1004 				} else {
1005 					MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH;
1006 				}
1007 				if (FirstBurstLength < MAX_FIRSTBURSTLENGTH &&
1008 				    FirstBurstLength > MaxBurstLength) {
1009 					FirstBurstLength = MaxBurstLength;
1010 					snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d",
1011 						 FirstBurstLength);
1012 				}
1013 			}
1014 
1015 			/* prevent target's declarative params from being changed by initiator */
1016 			if (spdk_iscsi_find_key_in_array(param->key, target_declarative_params)) {
1017 				add_param_value = 1;
1018 			}
1019 
1020 			new_val = spdk_iscsi_negotiate_param_all(&add_param_value,
1021 					param,
1022 					valid_list,
1023 					in_val,
1024 					cur_val);
1025 		}
1026 
1027 		/* check the negotiated value of the key */
1028 		if (new_val != NULL) {
1029 			/* add_param_value = 0 means updating the value of
1030 			 *      existed key in the connection's parameters
1031 			 */
1032 			if (add_param_value == 0)
1033 				spdk_iscsi_param_set(params_dst, param->key,
1034 						     new_val);
1035 
1036 			total = spdk_iscsi_construct_data_from_param(param,
1037 					new_val,
1038 					data,
1039 					alloc_len,
1040 					total);
1041 			if (total < 0)
1042 				goto final_return;
1043 
1044 			total = spdk_iscsi_special_param_construction(conn,
1045 					param,
1046 					FirstBurstLength_flag,
1047 					data,
1048 					alloc_len,
1049 					total);
1050 			if (total < 0)
1051 				goto final_return;
1052 		} else {
1053 			total = -1;
1054 			break;
1055 		}
1056 	}
1057 
1058 final_return:
1059 	free(valid_list);
1060 	free(in_val);
1061 	free(cur_val);
1062 
1063 	return total;
1064 }
1065 
1066 int
1067 spdk_iscsi_copy_param2var(struct spdk_iscsi_conn *conn)
1068 {
1069 	const char *val;
1070 
1071 	val = spdk_iscsi_param_get_val(conn->params, "MaxRecvDataSegmentLength");
1072 	if (val == NULL) {
1073 		SPDK_ERRLOG("Getval MaxRecvDataSegmentLength failed\n");
1074 		return -1;
1075 	}
1076 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI,
1077 		      "copy MaxRecvDataSegmentLength=%s\n", val);
1078 	conn->MaxRecvDataSegmentLength = (int)strtol(val, NULL, 10);
1079 	if (conn->MaxRecvDataSegmentLength > SPDK_ISCSI_MAX_SEND_DATA_SEGMENT_LENGTH) {
1080 		conn->MaxRecvDataSegmentLength = SPDK_ISCSI_MAX_SEND_DATA_SEGMENT_LENGTH;
1081 	}
1082 
1083 	val = spdk_iscsi_param_get_val(conn->params, "HeaderDigest");
1084 	if (val == NULL) {
1085 		SPDK_ERRLOG("Getval HeaderDigest failed\n");
1086 		return -1;
1087 	}
1088 	if (strcasecmp(val, "CRC32C") == 0) {
1089 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set HeaderDigest=1\n");
1090 		conn->header_digest = 1;
1091 	} else {
1092 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set HeaderDigest=0\n");
1093 		conn->header_digest = 0;
1094 	}
1095 	val = spdk_iscsi_param_get_val(conn->params, "DataDigest");
1096 	if (val == NULL) {
1097 		SPDK_ERRLOG("Getval DataDigest failed\n");
1098 		return -1;
1099 	}
1100 	if (strcasecmp(val, "CRC32C") == 0) {
1101 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set DataDigest=1\n");
1102 		conn->data_digest = 1;
1103 	} else {
1104 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set DataDigest=0\n");
1105 		conn->data_digest = 0;
1106 	}
1107 
1108 	val = spdk_iscsi_param_get_val(conn->sess->params, "MaxConnections");
1109 	if (val == NULL) {
1110 		SPDK_ERRLOG("Getval MaxConnections failed\n");
1111 		return -1;
1112 	}
1113 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "copy MaxConnections=%s\n", val);
1114 	conn->sess->MaxConnections = (uint32_t) strtol(val, NULL, 10);
1115 	val = spdk_iscsi_param_get_val(conn->sess->params, "MaxOutstandingR2T");
1116 	if (val == NULL) {
1117 		SPDK_ERRLOG("Getval MaxOutstandingR2T failed\n");
1118 		return -1;
1119 	}
1120 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "copy MaxOutstandingR2T=%s\n", val);
1121 	conn->sess->MaxOutstandingR2T = (uint32_t) strtol(val, NULL, 10);
1122 	val = spdk_iscsi_param_get_val(conn->sess->params, "FirstBurstLength");
1123 	if (val == NULL) {
1124 		SPDK_ERRLOG("Getval FirstBurstLength failed\n");
1125 		return -1;
1126 	}
1127 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "copy FirstBurstLength=%s\n", val);
1128 	conn->sess->FirstBurstLength = (uint32_t) strtol(val, NULL, 10);
1129 	val = spdk_iscsi_param_get_val(conn->sess->params, "MaxBurstLength");
1130 	if (val == NULL) {
1131 		SPDK_ERRLOG("Getval MaxBurstLength failed\n");
1132 		return -1;
1133 	}
1134 	SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "copy MaxBurstLength=%s\n", val);
1135 	conn->sess->MaxBurstLength = (uint32_t) strtol(val, NULL, 10);
1136 	val = spdk_iscsi_param_get_val(conn->sess->params, "InitialR2T");
1137 	if (val == NULL) {
1138 		SPDK_ERRLOG("Getval InitialR2T failed\n");
1139 		return -1;
1140 	}
1141 	if (strcasecmp(val, "Yes") == 0) {
1142 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set InitialR2T=1\n");
1143 		conn->sess->InitialR2T = 1;
1144 	} else {
1145 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set InitialR2T=0\n");
1146 		conn->sess->InitialR2T = 0;
1147 	}
1148 	val = spdk_iscsi_param_get_val(conn->sess->params, "ImmediateData");
1149 	if (val == NULL) {
1150 		SPDK_ERRLOG("Getval ImmediateData failed\n");
1151 		return -1;
1152 	}
1153 	if (strcasecmp(val, "Yes") == 0) {
1154 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set ImmediateData=1\n");
1155 		conn->sess->ImmediateData = 1;
1156 	} else {
1157 		SPDK_DEBUGLOG(SPDK_TRACE_ISCSI, "set ImmediateData=0\n");
1158 		conn->sess->ImmediateData = 0;
1159 	}
1160 	return 0;
1161 }
1162