374005d
From 062718a9579a10ea7c87e46162f80e3f57e80b67 Mon Sep 17 00:00:00 2001
374005d
From: Adheer Chandravanshi <adheer.chandravanshi@qlogic.com>
374005d
Date: Tue, 17 Sep 2013 08:07:32 -0400
374005d
Subject: [PATCH] iscsiadm: Add support to set CHAP entry using host chap mode
374005d
374005d
Provide support to add and update CHAP entry using chap submode of
374005d
iscsiadm host mode.
374005d
Both, new and update, iscsiadm operations perform the same function.
374005d
Currently only one entry can be added or updated at a time.
374005d
374005d
Signed-off-by: Adheer Chandravanshi <adheer.chandravanshi@qlogic.com>
374005d
Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
374005d
---
374005d
 include/iscsi_if.h |  19 ++++-
374005d
 usr/host.c         | 110 ++++++++++++++++++++++++++++
374005d
 usr/host.h         |   1 +
374005d
 usr/idbm.c         |   8 ++-
374005d
 usr/idbm.h         |   1 +
374005d
 usr/iscsi_ipc.h    |   3 +
374005d
 usr/iscsiadm.c     | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++---
374005d
 usr/netlink.c      |  25 +++++++
374005d
 8 files changed, 360 insertions(+), 14 deletions(-)
374005d
374005d
diff --git a/include/iscsi_if.h b/include/iscsi_if.h
374005d
index 01d38e7..0284662 100644
374005d
--- a/include/iscsi_if.h
374005d
+++ b/include/iscsi_if.h
374005d
@@ -74,8 +74,9 @@ enum iscsi_uevent_e {
374005d
 	ISCSI_UEVENT_LOGIN_FLASHNODE	= UEVENT_BASE + 28,
374005d
 	ISCSI_UEVENT_LOGOUT_FLASHNODE	= UEVENT_BASE + 29,
374005d
 	ISCSI_UEVENT_LOGOUT_FLASHNODE_SID	= UEVENT_BASE + 30,
374005d
+	ISCSI_UEVENT_SET_CHAP		= UEVENT_BASE + 31,
374005d
 
374005d
-	ISCSI_UEVENT_MAX		= ISCSI_UEVENT_LOGOUT_FLASHNODE_SID,
374005d
+	ISCSI_UEVENT_MAX		= ISCSI_UEVENT_SET_CHAP,
374005d
 
374005d
 	/* up events */
374005d
 	ISCSI_KEVENT_RECV_PDU		= KEVENT_BASE + 1,
374005d
@@ -318,8 +319,16 @@ enum iscsi_param_type {
374005d
 	ISCSI_HOST_PARAM,	/* iscsi_host_param */
374005d
 	ISCSI_NET_PARAM,	/* iscsi_net_param */
374005d
 	ISCSI_FLASHNODE_PARAM,	/* iscsi_flashnode_param */
374005d
+	ISCSI_CHAP_PARAM,	/* iscsi_chap_param */
374005d
 };
374005d
 
374005d
+/* structure for minimalist usecase */
374005d
+struct iscsi_param_info {
374005d
+	uint32_t len;		/* Actual length of the param value */
374005d
+	uint16_t param;		/* iscsi param */
374005d
+	uint8_t value[0];	/* length sized value follows */
374005d
+} __attribute__((__packed__));
374005d
+
374005d
 struct iscsi_iface_param_info {
374005d
 	uint32_t iface_num;	/* iface number, 0 - n */
374005d
 	uint32_t len;		/* Actual length of the param */
374005d
@@ -748,6 +757,14 @@ enum chap_type_e {
374005d
 	CHAP_TYPE_IN,
374005d
 };
374005d
 
374005d
+enum iscsi_chap_param {
374005d
+	ISCSI_CHAP_PARAM_INDEX,
374005d
+	ISCSI_CHAP_PARAM_CHAP_TYPE,
374005d
+	ISCSI_CHAP_PARAM_USERNAME,
374005d
+	ISCSI_CHAP_PARAM_PASSWORD,
374005d
+	ISCSI_CHAP_PARAM_PASSWORD_LEN
374005d
+};
374005d
+
374005d
 #define ISCSI_CHAP_AUTH_NAME_MAX_LEN	256
374005d
 #define ISCSI_CHAP_AUTH_SECRET_MAX_LEN	256
374005d
 struct iscsi_chap_rec {
374005d
diff --git a/usr/host.c b/usr/host.c
374005d
index 1fcb350..f2052d3 100644
374005d
--- a/usr/host.c
374005d
+++ b/usr/host.c
374005d
@@ -34,6 +34,7 @@
374005d
 #include "initiator.h"
374005d
 #include "iface.h"
374005d
 #include "iscsi_err.h"
374005d
+#include "iscsi_netlink.h"
374005d
 
374005d
 static int match_host_to_session(void *data, struct session_info *info)
374005d
 {
374005d
@@ -314,3 +315,112 @@ int host_info_print(int info_level, uint32_t host_no)
374005d
 	}
374005d
 	return 0;
374005d
 }
374005d
+
374005d
+static int chap_fill_param_uint(struct iovec *iov, int param,
374005d
+				uint32_t param_val, int param_len)
374005d
+{
374005d
+	struct iscsi_param_info *param_info;
374005d
+	struct nlattr *attr;
374005d
+	int len;
374005d
+	uint8_t val8 = 0;
374005d
+	uint16_t val16 = 0;
374005d
+	uint32_t val32 = 0;
374005d
+	char *val = NULL;
374005d
+
374005d
+	len = sizeof(struct iscsi_param_info) + param_len;
374005d
+	iov->iov_base = iscsi_nla_alloc(param, len);
374005d
+	if (!iov->iov_base)
374005d
+		return 1;
374005d
+
374005d
+	attr = iov->iov_base;
374005d
+	iov->iov_len = NLA_ALIGN(attr->nla_len);
374005d
+
374005d
+	param_info = (struct iscsi_param_info *)ISCSI_NLA_DATA(attr);
374005d
+	param_info->param = param;
374005d
+	param_info->len = param_len;
374005d
+
374005d
+	switch (param_len) {
374005d
+	case 1:
374005d
+		val8 = (uint8_t)param_val;
374005d
+		val = (char *)&val;;
374005d
+		break;
374005d
+
374005d
+	case 2:
374005d
+		val16 = (uint16_t)param_val;
374005d
+		val = (char *)&val16;
374005d
+		break;
374005d
+
374005d
+	case 4:
374005d
+		val32 = (uint32_t)param_val;
374005d
+		val = (char *)&val32;
374005d
+		break;
374005d
+
374005d
+	default:
374005d
+		goto free;
374005d
+	}
374005d
+	memcpy(param_info->value, val, param_len);
374005d
+
374005d
+	return 0;
374005d
+
374005d
+free:
374005d
+	free(iov->iov_base);
374005d
+	iov->iov_base = NULL;
374005d
+	iov->iov_len = 0;
374005d
+	return 1;
374005d
+}
374005d
+
374005d
+static int chap_fill_param_str(struct iovec *iov, int param, char *param_val,
374005d
+			       int param_len)
374005d
+{
374005d
+	struct iscsi_param_info *param_info;
374005d
+	struct nlattr *attr;
374005d
+	int len;
374005d
+
374005d
+	len = sizeof(struct iscsi_param_info) + param_len;
374005d
+	iov->iov_base = iscsi_nla_alloc(param, len);
374005d
+	if (!iov->iov_base)
374005d
+		return 1;
374005d
+
374005d
+	attr = iov->iov_base;
374005d
+	iov->iov_len = NLA_ALIGN(attr->nla_len);
374005d
+
374005d
+	param_info = (struct iscsi_param_info *)ISCSI_NLA_DATA(attr);
374005d
+	param_info->param = param;
374005d
+	param_info->len = param_len;
374005d
+	memcpy(param_info->value, param_val, param_len);
374005d
+	return 0;
374005d
+}
374005d
+
374005d
+int chap_build_config(struct iscsi_chap_rec *crec, struct iovec *iovs)
374005d
+{
374005d
+	struct iovec *iov = NULL;
374005d
+	int count = 0;
374005d
+
374005d
+	/* start at 2, because 0 is for nlmsghdr and 1 for event */
374005d
+	iov = iovs + 2;
374005d
+
374005d
+	if (!chap_fill_param_uint(&iov[count], ISCSI_CHAP_PARAM_INDEX,
374005d
+				  crec->chap_tbl_idx,
374005d
+				  sizeof(crec->chap_tbl_idx)))
374005d
+		count++;
374005d
+
374005d
+	if (!chap_fill_param_uint(&iov[count], ISCSI_CHAP_PARAM_CHAP_TYPE,
374005d
+				  crec->chap_type, sizeof(crec->chap_type)))
374005d
+		count++;
374005d
+
374005d
+	if (!chap_fill_param_str(&iov[count], ISCSI_CHAP_PARAM_USERNAME,
374005d
+				 crec->username, strlen(crec->username)))
374005d
+		count++;
374005d
+
374005d
+	if (!chap_fill_param_str(&iov[count], ISCSI_CHAP_PARAM_PASSWORD,
374005d
+				 (char *)crec->password,
374005d
+				 strlen((char *)crec->password)))
374005d
+		count++;
374005d
+
374005d
+	if (!chap_fill_param_uint(&iov[count], ISCSI_CHAP_PARAM_PASSWORD_LEN,
374005d
+				  crec->password_length,
374005d
+				  sizeof(crec->password_length)))
374005d
+		count++;
374005d
+
374005d
+	return count;
374005d
+}
374005d
diff --git a/usr/host.h b/usr/host.h
374005d
index 52e5b9e..149aa0d 100644
374005d
--- a/usr/host.h
374005d
+++ b/usr/host.h
374005d
@@ -17,5 +17,6 @@ struct host_info {
374005d
 };
374005d
 
374005d
 extern int host_info_print(int info_level, uint32_t host_no);
374005d
+extern int chap_build_config(struct iscsi_chap_rec *crec, struct iovec *iovs);
374005d
 
374005d
 #endif
374005d
diff --git a/usr/idbm.c b/usr/idbm.c
374005d
index 1e4f8c8..6b6f57c 100644
374005d
--- a/usr/idbm.c
374005d
+++ b/usr/idbm.c
374005d
@@ -456,7 +456,7 @@ void idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri)
374005d
 	__recinfo_uint16(IFACE_PORT, ri, r, port, IDBM_SHOW, num, 1);
374005d
 }
374005d
 
374005d
-static void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri)
374005d
+void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri)
374005d
 {
374005d
 	int num = 0;
374005d
 
374005d
@@ -465,14 +465,14 @@ static void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri)
374005d
 
374005d
 	if (r->chap_type == CHAP_TYPE_OUT) {
374005d
 		__recinfo_str(HOST_AUTH_USERNAME, ri, r, username, IDBM_SHOW,
374005d
-			      num, 0);
374005d
+			      num, 1);
374005d
 		__recinfo_str(HOST_AUTH_PASSWORD, ri, r, password, IDBM_MASKED,
374005d
 			      num, 1);
374005d
 		__recinfo_int(HOST_AUTH_PASSWORD_LEN, ri, r, password_length,
374005d
 			      IDBM_HIDE, num, 1);
374005d
 	} else {
374005d
 		__recinfo_str(HOST_AUTH_USERNAME_IN, ri, r, username, IDBM_SHOW,
374005d
-			      num, 0);
374005d
+			      num, 1);
374005d
 		__recinfo_str(HOST_AUTH_PASSWORD_IN, ri, r, password,
374005d
 			      IDBM_MASKED, num, 1);
374005d
 		__recinfo_int(HOST_AUTH_PASSWORD_IN_LEN, ri, r, password_length,
374005d
@@ -852,6 +852,8 @@ updated:
374005d
 	check_password_param(discovery.sendtargets.auth.password_in);
374005d
 	check_password_param(discovery.slp.auth.password);
374005d
 	check_password_param(discovery.slp.auth.password_in);
374005d
+	check_password_param(host.auth.password);
374005d
+	check_password_param(host.auth.password_in);
374005d
 
374005d
 	return 0;
374005d
 }
374005d
diff --git a/usr/idbm.h b/usr/idbm.h
374005d
index 5e4038d..b9020fe 100644
374005d
--- a/usr/idbm.h
374005d
+++ b/usr/idbm.h
374005d
@@ -185,6 +185,7 @@ extern struct node_rec *
374005d
 idbm_create_rec_from_boot_context(struct boot_context *context);
374005d
 
374005d
 extern int idbm_print_host_chap_info(struct iscsi_chap_rec *chap);
374005d
+extern void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri);
374005d
 
374005d
 extern int idbm_print_flashnode_info(struct flashnode_rec *target);
374005d
 extern void idbm_recinfo_flashnode(struct flashnode_rec *r, recinfo_t *ri);
374005d
diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h
374005d
index b6665cb..a32da1c 100644
374005d
--- a/usr/iscsi_ipc.h
374005d
+++ b/usr/iscsi_ipc.h
374005d
@@ -143,6 +143,9 @@ struct iscsi_ipc {
374005d
 			 uint16_t chap_tbl_idx, uint32_t num_entries,
374005d
 			 char *chap_buf, uint32_t *valid_chap_entries);
374005d
 
374005d
+	int (*set_chap) (uint64_t transport_handle, uint32_t host_no,
374005d
+			 struct iovec *iovs, uint32_t param_count);
374005d
+
374005d
 	int (*delete_chap) (uint64_t transport_handle, uint32_t host_no,
374005d
 			    uint16_t chap_tbl_idx);
374005d
 	int (*set_flash_node_params) (uint64_t transport_handle,
374005d
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
374005d
index beabdf0..045259b 100644
374005d
--- a/usr/iscsiadm.c
374005d
+++ b/usr/iscsiadm.c
374005d
@@ -115,7 +115,7 @@ static struct option const long_options[] =
374005d
 	{"packetsize", required_argument, NULL, 'b'},
374005d
 	{"count", required_argument, NULL, 'c'},
374005d
 	{"interval", required_argument, NULL, 'i'},
374005d
-	{"index", optional_argument, NULL, 'x'},
374005d
+	{"index", required_argument, NULL, 'x'},
374005d
 	{"portal_type", optional_argument, NULL, 'A'},
374005d
 	{NULL, 0, NULL, 0},
374005d
 };
374005d
@@ -1426,11 +1426,193 @@ exit_chap_info:
374005d
 	return rc;
374005d
 }
374005d
 
374005d
+static int fill_host_chap_rec(struct list_head *params,
374005d
+			      struct iscsi_chap_rec *crec, recinfo_t *cinfo,
374005d
+			      uint16_t chap_tbl_idx, int type, int *param_count)
374005d
+{
374005d
+	struct user_param *param;
374005d
+	int rc = 0;
374005d
+
374005d
+	crec->chap_tbl_idx = chap_tbl_idx;
374005d
+	crec->chap_type = type;
374005d
+
374005d
+	idbm_recinfo_host_chap(crec, cinfo);
374005d
+
374005d
+	list_for_each_entry(param, params, list) {
374005d
+		rc = idbm_rec_update_param(cinfo, param->name, param->value, 0);
374005d
+		if (rc)
374005d
+			break;
374005d
+	}
374005d
+
374005d
+	if (!rc)
374005d
+		*param_count += 3; /* index, type and password_length */
374005d
+
374005d
+	return rc;
374005d
+}
374005d
+
374005d
+static int verify_host_chap_params(struct list_head *params, int *type,
374005d
+				   int *param_count)
374005d
+{
374005d
+	struct user_param *param;
374005d
+	int username = -1;
374005d
+	int password = -1;
374005d
+	int rc = 0;
374005d
+
374005d
+	list_for_each_entry(param, params, list) {
374005d
+		*param_count += 1;
374005d
+
374005d
+		if (!strcmp(param->name, HOST_AUTH_USERNAME))
374005d
+			username = CHAP_TYPE_OUT;
374005d
+		else if (!strcmp(param->name, HOST_AUTH_PASSWORD))
374005d
+			password = CHAP_TYPE_OUT;
374005d
+		else if (!strcmp(param->name, HOST_AUTH_USERNAME_IN))
374005d
+			username = CHAP_TYPE_IN;
374005d
+		else if (!strcmp(param->name, HOST_AUTH_PASSWORD_IN))
374005d
+			password = CHAP_TYPE_IN;
374005d
+		else
374005d
+			continue;
374005d
+	}
374005d
+
374005d
+	if ((username == CHAP_TYPE_OUT) && (password == CHAP_TYPE_OUT)) {
374005d
+		if (type)
374005d
+			*type = CHAP_TYPE_OUT;
374005d
+
374005d
+		rc = ISCSI_SUCCESS;
374005d
+	} else if ((username == CHAP_TYPE_IN) && (password == CHAP_TYPE_IN)) {
374005d
+		if (type)
374005d
+			*type = CHAP_TYPE_IN;
374005d
+
374005d
+		rc = ISCSI_SUCCESS;
374005d
+	} else {
374005d
+		rc = ISCSI_ERR;
374005d
+	}
374005d
+
374005d
+	return rc;
374005d
+}
374005d
+
374005d
+static int set_host_chap_info(uint32_t host_no, uint64_t chap_index,
374005d
+			      struct list_head *params)
374005d
+{
374005d
+	struct iscsi_transport *t = NULL;
374005d
+	struct iscsi_chap_rec crec;
374005d
+	recinfo_t *chap_info = NULL;
374005d
+	struct iovec *iovs = NULL;
374005d
+	struct iovec *iov = NULL;
374005d
+	int type;
374005d
+	int param_count;
374005d
+	int param_used;
374005d
+	int rc = 0;
374005d
+	int fd, i = 0;
374005d
+
374005d
+	if (list_empty(params)) {
374005d
+		log_error("Chap username/password not provided.");
374005d
+		goto exit_set_chap;
374005d
+	}
374005d
+
374005d
+	chap_info = idbm_recinfo_alloc(MAX_KEYS);
374005d
+	if (!chap_info) {
374005d
+		log_error("Out of Memory.");
374005d
+		rc = ISCSI_ERR_NOMEM;
374005d
+		goto exit_set_chap;
374005d
+	}
374005d
+
374005d
+	t = iscsi_sysfs_get_transport_by_hba(host_no);
374005d
+	if (!t) {
374005d
+		log_error("Could not match hostno %d to transport.", host_no);
374005d
+		rc = ISCSI_ERR_TRANS_NOT_FOUND;
374005d
+		goto free_info_rec;
374005d
+	}
374005d
+
374005d
+	rc = verify_host_chap_params(params, &type, &param_count);
374005d
+	if (rc) {
374005d
+		log_error("Invalid username/password pair passed. Unable to determine the type of chap entry");
374005d
+		rc = ISCSI_ERR_INVAL;
374005d
+		goto free_info_rec;
374005d
+	}
374005d
+
374005d
+	if (param_count > 2) {
374005d
+		log_error("Only one pair of username/password can be passed.");
374005d
+		rc = ISCSI_ERR;
374005d
+		goto free_info_rec;
374005d
+	}
374005d
+
374005d
+	memset(&crec, 0, sizeof(crec));
374005d
+	rc = fill_host_chap_rec(params, &crec, chap_info, chap_index, type,
374005d
+				&param_count);
374005d
+	if (rc) {
374005d
+		log_error("Unable to fill CHAP record");
374005d
+		goto free_info_rec;
374005d
+	}
374005d
+
374005d
+	/* +2 for event and nlmsghdr */
374005d
+	param_count += 2;
374005d
+	iovs = calloc((param_count * sizeof(struct iovec)),
374005d
+		       sizeof(char));
374005d
+	if (!iovs) {
374005d
+		log_error("Out of Memory.");
374005d
+		rc = ISCSI_ERR_NOMEM;
374005d
+		goto free_info_rec;
374005d
+	}
374005d
+
374005d
+	/* param_used gives actual number of iovecs used for chap */
374005d
+	param_used = chap_build_config(&crec, iovs);
374005d
+	if (!param_used) {
374005d
+		log_error("Build chap config failed.");
374005d
+		rc = ISCSI_ERR;
374005d
+		goto free_iovec;
374005d
+	}
374005d
+
374005d
+	fd = ipc->ctldev_open();
374005d
+	if (fd < 0) {
374005d
+		rc = ISCSI_ERR_INTERNAL;
374005d
+		log_error("Netlink open failed.");
374005d
+		goto free_iovec;
374005d
+	}
374005d
+
374005d
+	rc = ipc->set_chap(t->handle, host_no, iovs, param_count);
374005d
+	if (rc < 0) {
374005d
+		log_error("CHAP setting failed");
374005d
+		if (rc == -EBUSY) {
374005d
+			rc = ISCSI_ERR_BUSY;
374005d
+			log_error("CHAP index %d is in use.",
374005d
+				  crec.chap_tbl_idx);
374005d
+		} else {
374005d
+			rc = ISCSI_ERR;
374005d
+		}
374005d
+
374005d
+		goto exit_set_chap;
374005d
+	}
374005d
+
374005d
+	ipc->ctldev_close();
374005d
+
374005d
+free_iovec:
374005d
+	/* start at 2, because 0 is for nlmsghdr and 1 for event */
374005d
+	iov = iovs + 2;
374005d
+	for (i = 0; i < param_used; i++, iov++) {
374005d
+		if (iov->iov_base)
374005d
+			free(iov->iov_base);
374005d
+	}
374005d
+
374005d
+	free(iovs);
374005d
+
374005d
+free_info_rec:
374005d
+	if (chap_info)
374005d
+		free(chap_info);
374005d
+
374005d
+exit_set_chap:
374005d
+	return rc;
374005d
+}
374005d
+
374005d
 static int delete_host_chap_info(uint32_t host_no, uint16_t chap_tbl_idx)
374005d
 {
374005d
 	struct iscsi_transport *t = NULL;
374005d
 	int fd, rc = 0;
374005d
 
374005d
+	if (chap_tbl_idx > MAX_CHAP_ENTRIES) {
374005d
+		log_error("Invalid chap table index.");
374005d
+		goto exit_delete_chap;
374005d
+	}
374005d
+
374005d
 	t = iscsi_sysfs_get_transport_by_hba(host_no);
374005d
 	if (!t) {
374005d
 		log_error("Could not match hostno %d to "
374005d
@@ -1464,19 +1646,18 @@ exit_delete_chap:
374005d
 }
374005d
 
374005d
 static int exec_host_chap_op(int op, int info_level, uint32_t host_no,
374005d
-			     uint64_t chap_index)
374005d
+			     uint64_t chap_index, struct list_head *params)
374005d
 {
374005d
 	int rc = ISCSI_ERR_INVAL;
374005d
 
374005d
-	if (op != OP_SHOW && (chap_index > (uint64_t)MAX_CHAP_ENTRIES)) {
374005d
-		log_error("Invalid chap table index.");
374005d
-		goto exit_chap_op;
374005d
-	}
374005d
-
374005d
 	switch (op) {
374005d
 	case OP_SHOW:
374005d
 		rc = get_host_chap_info(host_no);
374005d
 		break;
374005d
+	case OP_NEW:
374005d
+	case OP_UPDATE:
374005d
+		rc = set_host_chap_info(host_no, chap_index, params);
374005d
+		break;
374005d
 	case OP_DELETE:
374005d
 		rc = delete_host_chap_info(host_no, chap_index);
374005d
 		break;
374005d
@@ -1485,7 +1666,6 @@ static int exec_host_chap_op(int op, int info_level, uint32_t host_no,
374005d
 		break;
374005d
 	}
374005d
 
374005d
-exit_chap_op:
374005d
 	return rc;
374005d
 }
374005d
 
374005d
@@ -2816,7 +2996,7 @@ main(int argc, char **argv)
374005d
 	struct iface_rec *iface = NULL, *tmp;
374005d
 	struct node_rec *rec = NULL;
374005d
 	uint64_t host_no =  (uint64_t)MAX_HOST_NO + 1;
374005d
-	uint64_t index = (uint64_t)MAX_FLASHNODE_IDX + 1;
374005d
+	uint64_t index = ULLONG_MAX;
374005d
 	struct user_param *param;
374005d
 	struct list_head params;
374005d
 
374005d
@@ -3038,8 +3218,12 @@ main(int argc, char **argv)
374005d
 					rc = ISCSI_ERR_INVAL;
374005d
 					break;
374005d
 				}
374005d
+
374005d
+				if (index == ULLONG_MAX)
374005d
+					index = (uint64_t)MAX_CHAP_ENTRIES + 1;
374005d
+
374005d
 				rc = exec_host_chap_op(op, info_level, host_no,
374005d
-						       index);
374005d
+						       index, &params);
374005d
 				break;
374005d
 			case MODE_FLASHNODE:
374005d
 				if (host_no > MAX_HOST_NO) {
374005d
@@ -3048,6 +3232,9 @@ main(int argc, char **argv)
374005d
 					break;
374005d
 				}
374005d
 
374005d
+				if (index == ULLONG_MAX)
374005d
+					index = (uint64_t)MAX_FLASHNODE_IDX + 1;
374005d
+
374005d
 				rc = exec_flashnode_op(op, info_level, host_no,
374005d
 						       index, portal_type,
374005d
 						       &params);
374005d
diff --git a/usr/netlink.c b/usr/netlink.c
374005d
index c07fe3c..151b56d 100644
374005d
--- a/usr/netlink.c
374005d
+++ b/usr/netlink.c
374005d
@@ -1228,6 +1228,30 @@ static int kget_chap(uint64_t transport_handle, uint32_t host_no,
374005d
 	return rc;
374005d
 }
374005d
 
374005d
+static int kset_chap(uint64_t transport_handle, uint32_t host_no,
374005d
+			struct iovec *iovs, uint32_t param_count)
374005d
+{
374005d
+	int rc, ev_len;
374005d
+	struct iscsi_uevent ev;
374005d
+	struct iovec *iov = iovs + 1;
374005d
+
374005d
+	log_debug(8, "in %s", __func__);
374005d
+
374005d
+	ev_len = sizeof(ev);
374005d
+	ev.type = ISCSI_UEVENT_SET_CHAP;
374005d
+	ev.transport_handle = transport_handle;
374005d
+	ev.u.set_path.host_no = host_no;
374005d
+
374005d
+	iov->iov_base = &ev;
374005d
+	iov->iov_len = sizeof(ev);
374005d
+
374005d
+	rc = __kipc_call(iovs, param_count);
374005d
+	if (rc < 0)
374005d
+		return rc;
374005d
+
374005d
+	return 0;
374005d
+}
374005d
+
374005d
 static int kdelete_chap(uint64_t transport_handle, uint32_t host_no,
374005d
 			uint16_t chap_tbl_idx)
374005d
 {
374005d
@@ -1705,6 +1729,7 @@ struct iscsi_ipc nl_ipc = {
374005d
 	.recv_conn_state        = krecv_conn_state,
374005d
 	.exec_ping		= kexec_ping,
374005d
 	.get_chap		= kget_chap,
374005d
+	.set_chap		= kset_chap,
374005d
 	.delete_chap		= kdelete_chap,
374005d
 	.set_flash_node_params	= kset_flashnode_params,
374005d
 	.new_flash_node		= knew_flashnode,
374005d
-- 
374005d
1.8.3.1
374005d