diff --git a/include/iscsi_err.h b/include/iscsi_err.h index e038f1c78..aabea4ea8 100644 --- a/include/iscsi_err.h +++ b/include/iscsi_err.h @@ -60,6 +60,8 @@ enum { ISCSI_ERR_ISNS_REG_FAILED = 26, /* operation not supported */ ISCSI_ERR_OP_NOT_SUPP = 27, + /* device or resource in use */ + ISCSI_ERR_BUSY = 28, /* Always last. Indicates end of error code space */ ISCSI_MAX_ERR_VAL, diff --git a/include/iscsi_if.h b/include/iscsi_if.h index 26182aa7e..ce9ba15d0 100644 --- a/include/iscsi_if.h +++ b/include/iscsi_if.h @@ -66,8 +66,10 @@ enum iscsi_uevent_e { ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20, ISCSI_UEVENT_SET_IFACE_PARAMS = UEVENT_BASE + 21, ISCSI_UEVENT_PING = UEVENT_BASE + 22, + ISCSI_UEVENT_GET_CHAP = UEVENT_BASE + 23, + ISCSI_UEVENT_DELETE_CHAP = UEVENT_BASE + 24, - ISCSI_UEVENT_MAX = ISCSI_UEVENT_PING, + ISCSI_UEVENT_MAX = ISCSI_UEVENT_DELETE_CHAP, /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, @@ -205,6 +207,18 @@ struct iscsi_uevent { uint32_t pid; /* unique ping id associated with each ping request */ } iscsi_ping; + struct msg_get_chap { + uint32_t host_no; + uint32_t num_entries; /* number of CHAP entries + * on request, number of + * valid CHAP entries on + * response */ + uint16_t chap_tbl_idx; + } get_chap; + struct msg_delete_chap { + uint32_t host_no; + uint16_t chap_tbl_idx; + } delete_chap; } u; union { /* messages k -> u */ @@ -583,4 +597,20 @@ struct iscsi_stats { __attribute__ ((aligned (sizeof(uint64_t)))); }; +enum chap_type_e { + CHAP_TYPE_OUT, + CHAP_TYPE_IN, +}; + +#define ISCSI_CHAP_AUTH_NAME_MAX_LEN 256 +#define ISCSI_CHAP_AUTH_SECRET_MAX_LEN 256 + +struct iscsi_chap_rec { + uint16_t chap_tbl_idx; + enum chap_type_e chap_type; + char username[ISCSI_CHAP_AUTH_NAME_MAX_LEN]; + uint8_t password[ISCSI_CHAP_AUTH_SECRET_MAX_LEN]; + uint8_t password_length; +}; + #endif diff --git a/usr/host.h b/usr/host.h index a38efc9b1..894ab911d 100644 --- a/usr/host.h +++ b/usr/host.h @@ -5,6 +5,9 @@ #include "types.h" #include "config.h" +#define MAX_CHAP_BUF_SZ 4096 +#define REQ_CHAP_BUF_SZ (MAX_CHAP_BUF_SZ + sizeof(struct iscsi_uevent)) + struct host_info { struct iface_rec iface; uint32_t host_no; diff --git a/usr/idbm.c b/usr/idbm.c index 7ebd3d519..3f531157c 100644 --- a/usr/idbm.c +++ b/usr/idbm.c @@ -445,6 +445,30 @@ void idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri) __recinfo_uint16(IFACE_PORT, ri, r, port, IDBM_SHOW, num, 1); } +static void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri) +{ + int num = 0; + + __recinfo_uint16(HOST_AUTH_INDEX, ri, r, chap_tbl_idx, IDBM_SHOW, + num, 1); + + if (r->chap_type == CHAP_TYPE_OUT) { + __recinfo_str(HOST_AUTH_USERNAME, ri, r, username, IDBM_SHOW, + num, 0); + __recinfo_str(HOST_AUTH_PASSWORD, ri, r, password, IDBM_MASKED, + num, 1); + __recinfo_int(HOST_AUTH_PASSWORD_LEN, ri, r, password_length, + IDBM_HIDE, num, 1); + } else { + __recinfo_str(HOST_AUTH_USERNAME_IN, ri, r, username, IDBM_SHOW, + num, 0); + __recinfo_str(HOST_AUTH_PASSWORD_IN, ri, r, password, + IDBM_MASKED, num, 1); + __recinfo_int(HOST_AUTH_PASSWORD_IN_LEN, ri, r, password_length, + IDBM_HIDE, num, 1); + } +} + recinfo_t *idbm_recinfo_alloc(int max_keys) { recinfo_t *info; @@ -475,6 +499,9 @@ void idbm_print(int type, void *rec, int show, FILE *f) case IDBM_PRINT_TYPE_IFACE: idbm_recinfo_iface((struct iface_rec *)rec, info); break; + case IDBM_PRINT_TYPE_HOST_CHAP: + idbm_recinfo_host_chap((struct iscsi_chap_rec *)rec, info); + break; } fprintf(f, "%s\n", ISCSI_BEGIN_REC); @@ -845,6 +872,13 @@ int idbm_print_iface_info(void *data, struct iface_rec *iface) return 0; } +int idbm_print_host_chap_info(struct iscsi_chap_rec *chap) +{ + /* User only calls this to print chap so always print */ + idbm_print(IDBM_PRINT_TYPE_HOST_CHAP, chap, 1, stdout); + return 0; +} + int idbm_print_node_flat(void *data, node_rec_t *rec) { if (strchr(rec->conn[0].address, '.')) diff --git a/usr/idbm.h b/usr/idbm.h index ced224d2b..0f8767a71 100644 --- a/usr/idbm.h +++ b/usr/idbm.h @@ -162,6 +162,7 @@ enum { IDBM_PRINT_TYPE_DISCOVERY, IDBM_PRINT_TYPE_NODE, IDBM_PRINT_TYPE_IFACE, + IDBM_PRINT_TYPE_HOST_CHAP, }; extern void idbm_print(int type, void *rec, int show, FILE *f); @@ -174,4 +175,6 @@ extern struct node_rec *idbm_create_rec(char *targetname, int tpgt, extern struct node_rec * idbm_create_rec_from_boot_context(struct boot_context *context); +extern int idbm_print_host_chap_info(struct iscsi_chap_rec *chap); + #endif /* IDBM_H */ diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h index 269d87cb7..358d01432 100644 --- a/usr/idbm_fields.h +++ b/usr/idbm_fields.h @@ -116,4 +116,14 @@ #define DISC_ISNS_ADDR "discovery.sendtargets.address" #define DISC_ISNS_PORT "discovery.sendtargets.port" +/* host auth fields */ +#define HOST_AUTH_INDEX "host.auth.tbl_idx" +#define HOST_AUTH_METHOD "host.auth.authmethod" +#define HOST_AUTH_USERNAME "host.auth.username" +#define HOST_AUTH_PASSWORD "host.auth.password" +#define HOST_AUTH_PASSWORD_LEN "host.auth.password_length" +#define HOST_AUTH_USERNAME_IN "host.auth.username_in" +#define HOST_AUTH_PASSWORD_IN "host.auth.password_in" +#define HOST_AUTH_PASSWORD_IN_LEN "host.auth.password_in_length" + #endif diff --git a/usr/iscsi_err.c b/usr/iscsi_err.c index 4936e451d..4fe1c53ad 100644 --- a/usr/iscsi_err.c +++ b/usr/iscsi_err.c @@ -50,6 +50,7 @@ static char *iscsi_err_msgs[] = { /* 25 */ "iSNS query failed", /* 26 */ "iSNS registration failed", /* 27 */ "operation not supported", + /* 28 */ "device or resource in use", }; char *iscsi_err_to_str(int err) diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h index a00135f4b..08cf6f968 100644 --- a/usr/iscsi_ipc.h +++ b/usr/iscsi_ipc.h @@ -138,6 +138,13 @@ struct iscsi_ipc { int (*exec_ping) (uint64_t transport_handle, uint32_t host_no, struct sockaddr *addr, uint32_t iface_num, uint32_t iface_type, uint32_t size); + + int (*get_chap) (uint64_t transport_handle, uint32_t host_no, + uint16_t chap_tbl_idx, uint32_t num_entries, + char *chap_buf, uint32_t *valid_chap_entries); + + int (*delete_chap) (uint64_t transport_handle, uint32_t host_no, + uint16_t chap_tbl_idx); }; #endif /* ISCSI_IPC_H */ diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c index 7ff8728c0..85b67740d 100644 --- a/usr/iscsiadm.c +++ b/usr/iscsiadm.c @@ -67,6 +67,7 @@ enum iscsiadm_mode { MODE_IFACE, MODE_FW, MODE_PING, + MODE_CHAP }; enum iscsiadm_op { @@ -191,6 +192,8 @@ str_to_submode(char *str) if (!strcmp("ping", str)) sub_mode = MODE_PING; + else if (!strcmp("chap", str)) + sub_mode = MODE_CHAP; else sub_mode = -1; @@ -1297,6 +1300,146 @@ static int iface_apply_net_config(struct iface_rec *iface, int op) return ISCSI_SUCCESS; } +static int get_host_chap_info(uint32_t host_no) +{ + struct iscsi_transport *t = NULL; + struct iscsi_chap_rec *crec = NULL; + char *req_buf = NULL; + uint32_t valid_chap_entries; + uint32_t num_entries; + uint16_t chap_tbl_idx = 0; + int rc = 0; + int fd, i = 0; + + t = iscsi_sysfs_get_transport_by_hba(host_no); + if (!t) { + log_error("Could not match hostno %d to " + "transport.", host_no); + rc = ISCSI_ERR_TRANS_NOT_FOUND; + goto exit_chap_info; + } + + num_entries = MAX_CHAP_BUF_SZ / sizeof(*crec); + + req_buf = calloc(1, REQ_CHAP_BUF_SZ); + if (!req_buf) { + log_error("Could not allocate memory for CHAP request."); + rc = ISCSI_ERR_NOMEM; + goto exit_chap_info; + } + + fd = ipc->ctldev_open(); + if (fd < 0) { + rc = ISCSI_ERR_INTERNAL; + log_error("Netlink open failed."); + goto exit_chap_info; + } + +get_chap: + memset(req_buf, 0, REQ_CHAP_BUF_SZ); + + rc = ipc->get_chap(t->handle, host_no, chap_tbl_idx, num_entries, + req_buf, &valid_chap_entries); + if (rc < 0) { + log_error("get_chap_info failed. errno=%d", errno); + rc = ISCSI_ERR; + goto exit_chap_info; + } + + log_info("Valid CHAP Entries = %d\n", valid_chap_entries); + + crec = (struct iscsi_chap_rec *) (req_buf + + sizeof(struct iscsi_uevent)); + + if (valid_chap_entries) + chap_tbl_idx = + (crec + (valid_chap_entries - 1))->chap_tbl_idx + 1; + + /* print chap info */ + for (i = 0; i < valid_chap_entries; i++) { + idbm_print_host_chap_info(crec); + crec++; + } + + if (valid_chap_entries != num_entries) + goto exit_chap_info; + else + goto get_chap; + + ipc->ctldev_close(); + +exit_chap_info: + if (req_buf) + free(req_buf); + + return rc; +} + +static int delete_host_chap_info(uint32_t host_no, char *value) +{ + struct iscsi_transport *t = NULL; + int fd, rc = 0; + uint16_t chap_tbl_idx; + + if (!value) { + log_error("CHAP deletion requires --value=table_index."); + return ISCSI_ERR_INVAL; + } + + chap_tbl_idx = (uint16_t)atoi(value); + + t = iscsi_sysfs_get_transport_by_hba(host_no); + if (!t) { + log_error("Could not match hostno %d to " + "transport.", host_no); + rc = ISCSI_ERR_TRANS_NOT_FOUND; + goto exit_delete_chap; + } + + fd = ipc->ctldev_open(); + if (fd < 0) { + log_error("Netlink open failed."); + rc = ISCSI_ERR_INTERNAL; + goto exit_delete_chap; + } + + log_info("Deleteing CHAP index: %d\n", chap_tbl_idx); + rc = ipc->delete_chap(t->handle, host_no, chap_tbl_idx); + if (rc < 0) { + log_error("CHAP Delete failed."); + if (rc == -EBUSY) { + rc = ISCSI_ERR_BUSY; + log_error("CHAP index %d is in use.", chap_tbl_idx); + } else + rc = ISCSI_ERR; + } + + ipc->ctldev_close(); + +exit_delete_chap: + return rc; +} + +static int exec_host_chap_op(int op, int info_level, uint32_t host_no, + char *value) +{ + int rc = ISCSI_ERR_INVAL; + + switch (op) { + case OP_SHOW: + rc = get_host_chap_info(host_no); + break; + case OP_DELETE: + rc = delete_host_chap_info(host_no, value); + break; + default: + log_error("Invalid operation."); + break; + } + + return rc; +} + /* TODO: merge iter helpers and clean them up, so we can use them here */ static int exec_iface_op(int op, int do_show, int info_level, struct iface_rec *iface, uint32_t host_no, @@ -2393,14 +2536,30 @@ main(int argc, char **argv) switch (mode) { case MODE_HOST: - if ((rc = verify_mode_params(argc, argv, "HdmP", 0))) { + if ((rc = verify_mode_params(argc, argv, "CHdmPov", 0))) { log_error("host mode: option '-%c' is not " "allowed/supported", rc); rc = ISCSI_ERR_INVAL; goto out; } - - rc = host_info_print(info_level, host_no); + if (sub_mode != -1) { + switch (sub_mode) { + case MODE_CHAP: + if (!op || !host_no) { + log_error("CHAP mode requires host " + "no and valid operation"); + rc = ISCSI_ERR_INVAL; + break; + } + rc = exec_host_chap_op(op, info_level, host_no, + value); + break; + default: + log_error("Invalid Sub Mode"); + break; + } + } else + rc = host_info_print(info_level, host_no); break; case MODE_IFACE: iface_setup_host_bindings(); diff --git a/usr/netlink.c b/usr/netlink.c index f680b3169..2556f1fe4 100644 --- a/usr/netlink.c +++ b/usr/netlink.c @@ -336,6 +336,9 @@ __kipc_call(struct iovec *iovp, int count) } else if (ev->type == ISCSI_UEVENT_GET_STATS) { /* kget_stats() will read */ return 0; + } else if (ev->type == ISCSI_UEVENT_GET_CHAP) { + /* kget_chap() will read */ + return 0; } else { if ((rc = nlpayload_read(ctrl_fd, (void*)ev, sizeof(*ev), 0)) < 0) { @@ -1180,6 +1183,75 @@ static int kexec_ping(uint64_t transport_handle, uint32_t host_no, return rc; } +static int kget_chap(uint64_t transport_handle, uint32_t host_no, + uint16_t chap_tbl_idx, uint32_t num_entries, + char *chap_buf, uint32_t *valid_chap_entries) +{ + int rc = 0; + int ev_size; + struct iscsi_uevent ev; + struct iovec iov[2]; + char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))]; + struct nlmsghdr *nlh; + + memset(&ev, 0, sizeof(struct iscsi_uevent)); + + ev.type = ISCSI_UEVENT_GET_CHAP; + ev.transport_handle = transport_handle; + ev.u.get_chap.host_no = host_no; + ev.u.get_chap.chap_tbl_idx = chap_tbl_idx; + ev.u.get_chap.num_entries = num_entries; + + iov[1].iov_base = &ev; + iov[1].iov_len = sizeof(ev); + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + if ((rc = nl_read(ctrl_fd, nlm_ev, + NLMSG_SPACE(sizeof(struct iscsi_uevent)), + MSG_PEEK)) < 0) { + log_error("can not read nlm_ev, error %d", rc); + return rc; + } + + nlh = (struct nlmsghdr *)nlm_ev; + ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + + if ((rc = nlpayload_read(ctrl_fd, (void *)chap_buf, ev_size, 0)) < 0) { + log_error("can not read from NL socket, error %d", rc); + return rc; + } + + *valid_chap_entries = ev.u.get_chap.num_entries; + + return rc; +} + +static int kdelete_chap(uint64_t transport_handle, uint32_t host_no, + uint16_t chap_tbl_idx) +{ + int rc = 0; + struct iscsi_uevent ev; + struct iovec iov[2]; + + memset(&ev, 0, sizeof(struct iscsi_uevent)); + + ev.type = ISCSI_UEVENT_DELETE_CHAP; + ev.transport_handle = transport_handle; + ev.u.delete_chap.host_no = host_no; + ev.u.delete_chap.chap_tbl_idx = chap_tbl_idx; + + iov[1].iov_base = &ev; + iov[1].iov_len = sizeof(ev); + + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + return rc; +} + static void drop_data(struct nlmsghdr *nlh) { int ev_size; @@ -1216,11 +1288,17 @@ static int ctldev_handle(void) /* old kernels sent ISCSI_UEVENT_CREATE_SESSION on creation */ case ISCSI_UEVENT_CREATE_SESSION: drop_data(nlh); + if (!ipc_ev_clbk) + return 0; + if (ipc_ev_clbk->create_session) ipc_ev_clbk->create_session(ev->r.c_session_ret.host_no, ev->r.c_session_ret.sid); return 0; case ISCSI_KEVENT_DESTROY_SESSION: + if (!ipc_ev_clbk) + return 0; + drop_data(nlh); if (ipc_ev_clbk->destroy_session) ipc_ev_clbk->destroy_session(ev->r.d_session.host_no, @@ -1464,6 +1542,8 @@ struct iscsi_ipc nl_ipc = { .set_net_config = kset_net_config, .recv_conn_state = krecv_conn_state, .exec_ping = kexec_ping, + .get_chap = kget_chap, + .delete_chap = kdelete_chap, }; struct iscsi_ipc *ipc = &nl_ipc;