diff options
-rw-r--r-- | include/iscsi_err.h | 2 | ||||
-rw-r--r-- | include/iscsi_if.h | 21 | ||||
-rw-r--r-- | usr/iface.c | 21 | ||||
-rw-r--r-- | usr/iface.h | 1 | ||||
-rw-r--r-- | usr/iscsi_err.c | 1 | ||||
-rw-r--r-- | usr/iscsi_ipc.h | 4 | ||||
-rw-r--r-- | usr/iscsiadm.c | 151 | ||||
-rw-r--r-- | usr/netlink.c | 165 |
8 files changed, 352 insertions, 14 deletions
diff --git a/include/iscsi_err.h b/include/iscsi_err.h index d1d94b5..e038f1c 100644 --- a/include/iscsi_err.h +++ b/include/iscsi_err.h @@ -58,6 +58,8 @@ enum { ISCSI_ERR_ISNS_QUERY = 25, /* iSNS registration/deregistration failed */ ISCSI_ERR_ISNS_REG_FAILED = 26, + /* operation not supported */ + ISCSI_ERR_OP_NOT_SUPP = 27, /* 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 540cd8d..26182aa 100644 --- a/include/iscsi_if.h +++ b/include/iscsi_if.h @@ -65,8 +65,9 @@ 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_MAX = ISCSI_UEVENT_SET_IFACE_PARAMS, + ISCSI_UEVENT_MAX = ISCSI_UEVENT_PING, /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, @@ -80,8 +81,9 @@ enum iscsi_uevent_e { ISCSI_KEVENT_IF_DOWN = KEVENT_BASE + 8, ISCSI_KEVENT_CONN_LOGIN_STATE = KEVENT_BASE + 9, ISCSI_KEVENT_HOST_EVENT = KEVENT_BASE + 10, + ISCSI_KEVENT_PING_COMP = KEVENT_BASE + 11, - ISCSI_KEVENT_MAX = ISCSI_KEVENT_HOST_EVENT, + ISCSI_KEVENT_MAX = ISCSI_KEVENT_PING_COMP, }; enum iscsi_tgt_dscvr { @@ -195,6 +197,14 @@ struct iscsi_uevent { uint32_t host_no; uint32_t count; } set_iface_params; + struct msg_iscsi_ping { + uint32_t host_no; + uint32_t iface_num; + uint32_t iface_type; + uint32_t payload_size; + uint32_t pid; /* unique ping id associated + with each ping request */ + } iscsi_ping; } u; union { /* messages k -> u */ @@ -244,6 +254,13 @@ struct iscsi_uevent { uint32_t data_size; enum iscsi_host_event_code code; } host_event; + struct msg_ping_comp { + uint32_t host_no; + uint32_t status; + uint32_t pid; /* unique ping id associated + with each ping request */ + uint32_t data_size; + } ping_comp; } r; } __attribute__ ((aligned (sizeof(uint64_t)))); diff --git a/usr/iface.c b/usr/iface.c index 730820c..1e7f46a 100644 --- a/usr/iface.c +++ b/usr/iface.c @@ -425,12 +425,23 @@ int iface_get_by_net_binding(struct iface_rec *pattern, return ISCSI_ERR_NO_OBJS_FOUND; } -static int iface_get_iptype(struct iface_rec *iface) +int iface_get_iptype(struct iface_rec *iface) { - if (strcmp(iface->bootproto, "dhcp") && !strstr(iface->ipaddress, ".")) - return ISCSI_IFACE_TYPE_IPV6; - else - return ISCSI_IFACE_TYPE_IPV4; + /* address might not be set if user config with another tool */ + if (!strlen(iface->ipaddress) || + !strcmp(UNKNOWN_VALUE, iface->ipaddress)) { + /* try to figure out by name */ + if (strstr(iface->name, "ipv4")) + return ISCSI_IFACE_TYPE_IPV4; + else + return ISCSI_IFACE_TYPE_IPV6; + } else { + if (strcmp(iface->bootproto, "dhcp") && + !strstr(iface->ipaddress, ".")) + return ISCSI_IFACE_TYPE_IPV6; + else + return ISCSI_IFACE_TYPE_IPV4; + } } static int iface_setup_binding_from_kern_iface(void *data, diff --git a/usr/iface.h b/usr/iface.h index 3ba2a4e..f6c2c33 100644 --- a/usr/iface.h +++ b/usr/iface.h @@ -58,6 +58,7 @@ extern int iface_get_param_count(struct iface_rec *iface_primary, int iface_all); extern int iface_build_net_config(struct iface_rec *iface_primary, int iface_all, struct iovec *iovs); +extern int iface_get_iptype(struct iface_rec *iface); #define iface_fmt "[hw=%s,ip=%s,net_if=%s,iscsi_if=%s]" #define iface_str(_iface) \ diff --git a/usr/iscsi_err.c b/usr/iscsi_err.c index 9b27239..4936e45 100644 --- a/usr/iscsi_err.c +++ b/usr/iscsi_err.c @@ -49,6 +49,7 @@ static char *iscsi_err_msgs[] = { /* 24 */ "iSCSI login failed due to authorization failure", /* 25 */ "iSNS query failed", /* 26 */ "iSNS registration failed", + /* 27 */ "operation not supported", }; char *iscsi_err_to_str(int err) diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h index fc67c4a..a00135f 100644 --- a/usr/iscsi_ipc.h +++ b/usr/iscsi_ipc.h @@ -134,6 +134,10 @@ struct iscsi_ipc { struct iovec *iovs, uint32_t param_count); int (*recv_conn_state) (struct iscsi_conn *conn, uint32_t *state); + + 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); }; #endif /* ISCSI_IPC_H */ diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c index 03b779e..7ff8728 100644 --- a/usr/iscsiadm.c +++ b/usr/iscsiadm.c @@ -27,6 +27,7 @@ #include <unistd.h> #include <string.h> #include <signal.h> +#include <time.h> #include <sys/stat.h> #include "initiator.h" @@ -51,6 +52,7 @@ #include "isns-proto.h" #include "iscsi_err.h" #include "iscsi_ipc.h" +#include "iscsi_timer.h" static char program_name[] = "iscsiadm"; static char config_file[TARGET_NAME_MAXLEN]; @@ -64,6 +66,7 @@ enum iscsiadm_mode { MODE_HOST, MODE_IFACE, MODE_FW, + MODE_PING, }; enum iscsiadm_op { @@ -102,9 +105,13 @@ static struct option const long_options[] = {"show", no_argument, NULL, 'S'}, {"version", no_argument, NULL, 'V'}, {"help", no_argument, NULL, 'h'}, + {"submode", required_argument, NULL, 'C'}, + {"ip", required_argument, NULL, 'a'}, + {"packetsize", required_argument, NULL, 'b'}, + {"count", required_argument, NULL, 'c'}, {NULL, 0, NULL, 0}, }; -static char *short_options = "RlDVhm:p:P:T:H:I:U:k:L:d:r:n:v:o:sSt:u"; +static char *short_options = "RlDVhm:a:b:c:C:p:P:T:H:i:I:U:k:L:d:r:n:v:o:sSt:u"; static void usage(int status) { @@ -116,10 +123,10 @@ static void usage(int status) iscsiadm -m discoverydb [ -hV ] [ -d debug_level ] [-P printlevel] [ -t type -p ip:port -I ifaceN ... [ -Dl ] ] | [ [ -p ip:port -t type] \ [ -o operation ] [ -n name ] [ -v value ] [ -lD ] ] \n\ iscsiadm -m discovery [ -hV ] [ -d debug_level ] [-P printlevel] [ -t type -p ip:port -I ifaceN ... [ -l ] ] | [ [ -p ip:port ] [ -l | -D ] ] \n\ -iiscsiadm -m node [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port -I ifaceN ] [ -l | -u | -R | -s] ] \ +iscsiadm -m node [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port -I ifaceN ] [ -l | -u | -R | -s] ] \ [ [ -o operation ] [ -n name ] [ -v value ] ]\n\ iscsiadm -m session [ -hV ] [ -d debug_level ] [ -P printlevel] [ -r sessionid | sysfsdir [ -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n\ -iscsiadm -m iface [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -I ifacename | -H hostno|MAC ] [ [ -o operation ] [ -n name ] [ -v value ] ]\n\ +iscsiadm -m iface [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -I ifacename | -H hostno|MAC ] [ [ -o operation ] [ -n name ] [ -v value ] ] [ -C ping [ -a ip ] [ -b packetsize ] [ -c count ] [ -i interval ] ]\n\ iscsiadm -m fw [ -l ]\n\ iscsiadm -m host [ -P printlevel ] [ -H hostno|MAC ]\n\ iscsiadm -k priority\n"); @@ -178,6 +185,19 @@ str_to_mode(char *str) } static int +str_to_submode(char *str) +{ + int sub_mode; + + if (!strcmp("ping", str)) + sub_mode = MODE_PING; + else + sub_mode = -1; + + return sub_mode; +} + +static int str_to_type(char *str) { int type; @@ -2082,6 +2102,101 @@ static uint32_t parse_host_info(char *optarg, int *rc) return host_no; } +static int exec_ping_op(struct iface_rec *iface, char *ip, int size, int count, + int interval) +{ + int rc = ISCSI_ERR; + uint32_t iface_type = ISCSI_IFACE_TYPE_IPV4; + struct iscsi_transport *t = NULL; + uint32_t host_no; + struct sockaddr_storage addr; + int i; + + if (!iface) { + log_error("Ping requires iface."); + rc = ISCSI_ERR_INVAL; + goto ping_exit; + } + + if (!ip) { + log_error("Ping requires destination ipaddress."); + rc = ISCSI_ERR_INVAL; + goto ping_exit; + } + + if (size <= 0) { + log_error("Invalid packet size: %d.", size); + rc = ISCSI_ERR_INVAL; + goto ping_exit; + } + + if (count <= 0) { + log_error("Invalid number of packets to transmit: %d.", count); + rc = ISCSI_ERR_INVAL; + goto ping_exit; + } + + if (interval < 0) { + log_error("Invalid timing interval: %d.", interval); + rc = ISCSI_ERR_INVAL; + goto ping_exit; + } + + rc = iface_conf_read(iface); + if (rc) { + log_error("Could not read iface %s (%d).", iface->name, rc); + goto ping_exit; + } + + + iface_type = iface_get_iptype(iface); + + t = iscsi_sysfs_get_transport_by_name(iface->transport_name); + if (!t) { + log_error("Can't find transport."); + rc = ISCSI_ERR_INVAL; + goto ping_exit; + } + + host_no = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc); + if (host_no == -1) { + log_error("Can't find host_no."); + rc = ISCSI_ERR_INVAL; + goto ping_exit; + } + + rc = resolve_address(ip, NULL, &addr); + if (rc) { + log_error("Invalid IP address."); + rc = ISCSI_ERR_INVAL; + goto ping_exit; + } + + /* TODO: move this. It is needed by interface for pid */ + srand(time(NULL)); + + for (i = 1; i <= count; i++) { + /* + * To support drivers like bnx2i that do not use + * the iscsi if to send a ping, we can add a transport + * callout here. + */ + rc = ipc->exec_ping(t->handle, host_no, + (struct sockaddr *)&addr, iface->iface_num, + iface_type, size); + if (!rc) + printf("Ping %d completed\n", i); + else + printf("Ping %d failed: %s\n", i, iscsi_err_to_str(rc)); + + if (i < count) + sleep(interval); + } + +ping_exit: + return rc; +} + int main(int argc, char **argv) { @@ -2091,7 +2206,8 @@ main(int argc, char **argv) int rc=0, sid=-1, op=OP_NOOP, type=-1, do_logout=0, do_stats=0; int do_login_all=0, do_logout_all=0, info_level=-1, num_ifaces = 0; int tpgt = PORTAL_GROUP_TAG_UNKNOWN, killiscsid=-1, do_show=0; - int do_discover = 0; + int packet_size=32, ping_count=1, ping_interval=0; + int do_discover = 0, sub_mode = -1; struct sigaction sa_old; struct sigaction sa_new; struct list_head ifaces; @@ -2196,12 +2312,27 @@ main(int argc, char **argv) case 'm': mode = str_to_mode(optarg); break; + case 'C': + sub_mode = str_to_submode(optarg); + break; case 'T': targetname = optarg; break; case 'p': ip = str_to_ipport(optarg, &port, &tpgt); break; + case 'a': + ip = optarg; + break; + case 'b': + packet_size = atoi(optarg); + break; + case 'c': + ping_count = atoi(optarg); + break; + case 'i': + ping_interval = atoi(optarg); + break; case 'I': iface = iface_alloc(optarg, &rc); if (rc == ISCSI_ERR_INVAL) { @@ -2274,7 +2405,7 @@ main(int argc, char **argv) case MODE_IFACE: iface_setup_host_bindings(); - if ((rc = verify_mode_params(argc, argv, "HIdnvmPo", 0))) { + if ((rc = verify_mode_params(argc, argv, "HIdnvmPoCabci", 0))) { log_error("iface mode: option '-%c' is not " "allowed/supported", rc); rc = ISCSI_ERR_INVAL; @@ -2289,8 +2420,14 @@ main(int argc, char **argv) "interface. Using the first one " "%s.", iface->name); } - rc = exec_iface_op(op, do_show, info_level, iface, host_no, - name, value); + + if (sub_mode == MODE_PING) + rc = exec_ping_op(iface, ip, packet_size, ping_count, + ping_interval); + else + rc = exec_iface_op(op, do_show, info_level, iface, + host_no, name, value); + break; case MODE_DISCOVERYDB: if ((rc = verify_mode_params(argc, argv, "DSIPdmntplov", 0))) { diff --git a/usr/netlink.c b/usr/netlink.c index 0e842b5..f680b31 100644 --- a/usr/netlink.c +++ b/usr/netlink.c @@ -25,10 +25,12 @@ #include <unistd.h> #include <stdint.h> #include <errno.h> +#include <time.h> #include <inttypes.h> #include <asm/types.h> #include <sys/socket.h> #include <sys/types.h> +#include <sys/poll.h> #include <linux/netlink.h> #include "types.h" @@ -39,6 +41,8 @@ #include "iscsi_sysfs.h" #include "transport.h" #include "iscsi_netlink.h" +#include "iscsi_err.h" +#include "iscsi_timer.h" static int ctrl_fd; static struct sockaddr_nl src_addr, dest_addr; @@ -64,6 +68,15 @@ static int ctldev_handle(void); #define NLM_SETPARAM_DEFAULT_MAX (NI_MAXHOST + 1 + sizeof(struct iscsi_uevent)) +struct iscsi_ping_event { + uint32_t host_no; + uint32_t pid; + int32_t status; + int active; +}; + +struct iscsi_ping_event ping_event; + struct nlattr *iscsi_nla_alloc(uint16_t type, uint16_t len) { struct nlattr *attr; @@ -1024,6 +1037,149 @@ exit: return rc; } + + + +static int +ksend_ping(uint64_t transport_handle, uint32_t host_no, struct sockaddr *addr, + uint32_t iface_num, uint32_t iface_type, uint32_t pid, uint32_t size) +{ + int rc, addrlen; + struct iscsi_uevent *ev; + struct iovec iov[2]; + + log_debug(8, "in %s", __FUNCTION__); + + memset(setparam_buf, 0, NLM_SETPARAM_DEFAULT_MAX); + ev = (struct iscsi_uevent *)setparam_buf; + ev->type = ISCSI_UEVENT_PING; + ev->transport_handle = transport_handle; + ev->u.iscsi_ping.host_no = host_no; + ev->u.iscsi_ping.iface_num = iface_num; + ev->u.iscsi_ping.iface_type = iface_type; + ev->u.iscsi_ping.payload_size = size; + ev->u.iscsi_ping.pid = pid; + + if (addr->sa_family == PF_INET) + addrlen = sizeof(struct sockaddr_in); + else if (addr->sa_family == PF_INET6) + addrlen = sizeof(struct sockaddr_in6); + else { + log_error("%s unknown addr family %d\n", + __FUNCTION__, addr->sa_family); + return -EINVAL; + } + memcpy(setparam_buf + sizeof(*ev), addr, addrlen); + + iov[1].iov_base = ev; + iov[1].iov_len = sizeof(*ev) + addrlen; + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + return 0; +} + +static int kexec_ping(uint64_t transport_handle, uint32_t host_no, + struct sockaddr *addr, uint32_t iface_num, + uint32_t iface_type, uint32_t size) +{ + struct pollfd pfd; + struct timeval ping_timer; + int timeout, fd, rc; + uint32_t pid; + + fd = ipc->ctldev_open(); + if (fd < 0) { + log_error("Could not open netlink socket."); + return ISCSI_ERR; + } + + /* prepare to poll */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN | POLLPRI; + + /* get unique ping id */ + pid = rand(); + + rc = ksend_ping(transport_handle, host_no, addr, iface_num, + iface_type, pid, size); + if (rc != 0) { + switch (rc) { + case -ENOSYS: + rc = ISCSI_ERR_OP_NOT_SUPP; + break; + case -EINVAL: + rc = ISCSI_ERR_INVAL; + break; + default: + rc = ISCSI_ERR; + } + goto close_nl; + } + + ping_event.host_no = -1; + ping_event.pid = -1; + ping_event.status = -1; + ping_event.active = -1; + + iscsi_timer_set(&ping_timer, 30); + + timeout = iscsi_timer_msecs_until(&ping_timer); + + while (1) { + pfd.revents = 0; + rc = poll(&pfd, 1, timeout); + + if (iscsi_timer_expired(&ping_timer)) { + rc = ISCSI_ERR_TRANS_TIMEOUT; + break; + } + + if (rc > 0) { + if (pfd.revents & (POLLIN | POLLPRI)) { + timeout = iscsi_timer_msecs_until(&ping_timer); + rc = ipc->ctldev_handle(); + + if (ping_event.active != 1) + continue; + + if (pid != ping_event.pid) + continue; + + if (ping_event.status == 0) + rc = 0; + else + rc = ISCSI_ERR; + break; + } + + if (pfd.revents & POLLHUP) { + rc = ISCSI_ERR_TRANS; + break; + } + + if (pfd.revents & POLLNVAL) { + rc = ISCSI_ERR_INTERNAL; + break; + } + + if (pfd.revents & POLLERR) { + rc = ISCSI_ERR_INTERNAL; + break; + } + } else if (rc < 0) { + rc = ISCSI_ERR_INTERNAL; + break; + } + } + +close_nl: + ipc->ctldev_close(); + return rc; +} + static void drop_data(struct nlmsghdr *nlh) { int ev_size; @@ -1106,6 +1262,14 @@ static int ctldev_handle(void) drop_data(nlh); return 0; + case ISCSI_KEVENT_PING_COMP: + ping_event.host_no = ev->r.ping_comp.host_no; + ping_event.pid = ev->r.ping_comp.pid; + ping_event.status = ev->r.ping_comp.status; + ping_event.active = 1; + + drop_data(nlh); + return 0; default: if ((ev->type > ISCSI_UEVENT_MAX && ev->type < KEVENT_BASE) || (ev->type > ISCSI_KEVENT_MAX)) @@ -1299,6 +1463,7 @@ struct iscsi_ipc nl_ipc = { .recv_pdu_end = krecv_pdu_end, .set_net_config = kset_net_config, .recv_conn_state = krecv_conn_state, + .exec_ping = kexec_ping, }; struct iscsi_ipc *ipc = &nl_ipc; |