From: Vikas Chaudhary <vikas.chaudh...@qlogic.com>

Added ping support for network connection diagnostics.

Signed-off-by: Vikas Chaudhary <vikas.chaudh...@qlogic.com>
---
 include/iscsi_if.h |   21 +++++-
 usr/initiator.h    |    9 ++
 usr/iscsi_ipc.h    |    4 +
 usr/iscsiadm.c     |  225 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 usr/netlink.c      |   49 +++++++++++
 5 files changed, 299 insertions(+), 9 deletions(-)

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/initiator.h b/usr/initiator.h
index b45caab..43a98f9 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -180,6 +180,15 @@ struct iscsi_ev_context {
        void *data;
 };
 
+struct iscsi_ping_event {
+       uint32_t host_no;
+       uint32_t pid;
+       int32_t status;
+       int active;
+};
+
+struct iscsi_ping_event ping_event;
+
 typedef struct queue_task {
        iscsi_conn_t *conn;
        iscsiadm_req_t req;
diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h
index fc67c4a..72992ec 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 (*send_ping) (uint64_t transport_handle, uint32_t host_no,
+                         struct sockaddr *addr, uint32_t iface_num,
+                         uint32_t iface_type, uint32_t pid, int size);
 };
 
 #endif /* ISCSI_IPC_H */
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
index 03b779e..4adcb1a 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -27,7 +27,9 @@
 #include <unistd.h>
 #include <string.h>
 #include <signal.h>
+#include <time.h>
 #include <sys/stat.h>
+#include <sys/poll.h>
 
 #include "initiator.h"
 #include "discovery.h"
@@ -51,6 +53,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 +67,7 @@ enum iscsiadm_mode {
        MODE_HOST,
        MODE_IFACE,
        MODE_FW,
+       MODE_PING,
 };
 
 enum iscsiadm_op {
@@ -102,9 +106,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 +124,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 +186,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 +2103,174 @@ 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, pid;
+       struct sockaddr_storage addr;
+       struct pollfd pfd;
+       struct timeval ping_timer;
+       int i, timeout, fd;
+
+       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;
+       }
+
+       if (strcmp(iface->bootproto, "dhcp") && !strstr(iface->ipaddress, "."))
+               iface_type = ISCSI_IFACE_TYPE_IPV6;
+
+       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;
+       }
+
+       fd = ipc->ctldev_open();
+       if (fd < 0) {
+               log_error("Netlink open failed.");
+               rc = ISCSI_ERR;
+               goto ping_exit;
+       }
+
+       /* prepare to poll */
+       memset(&pfd, 0, sizeof(pfd));
+       pfd.fd = fd;
+       pfd.events = POLLIN | POLLPRI;
+
+       srand(time(NULL));
+
+       for (i = 1; i <= count; i++) {
+               pid = rand();   /* get unique ping id */
+               rc = ipc->send_ping(t->handle, host_no,
+                                   (struct sockaddr *)&addr, iface->iface_num,
+                                   iface_type, pid, size);
+               if (rc != 0) {
+                       log_error("Diagnostic Ping iteration %d failed.", i);
+                       goto ping_done;
+               }
+
+               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);
+ping_repoll:
+               pfd.revents = 0;
+               rc = poll(&pfd, 1, timeout);
+
+               if (iscsi_timer_expired(&ping_timer)) {
+                       log_error("Diagnostic Ping iteration %d time out.", i);
+                       rc = ISCSI_ERR_INTERNAL;
+                       goto ping_done;
+               }
+
+               if (rc > 0) {
+                       if (pfd.revents & (POLLIN | POLLPRI)) {
+                               timeout = iscsi_timer_msecs_until(&ping_timer);
+                               rc = ipc->ctldev_handle();
+
+                               if (ping_event.active != 1)
+                                       goto ping_repoll;
+
+                               if (pid != ping_event.pid)
+                                       goto ping_repoll;
+                               else {
+                                       if (ping_event.status == 0)
+                                               printf("Diagnostic Ping"
+                                                      " iteration %d"
+                                                      " successful.\n", i);
+                                       else
+                                               log_error("Diagnostic Ping"
+                                                         " iteration %d"
+                                                         " failed.", i);
+                                       goto ping_done;
+                               }
+                       }
+
+                       if (pfd.revents & POLLHUP) {
+                               log_warning("Ping terminating after hangup");
+                               rc = ISCSI_ERR_TRANS;
+                               goto ping_done;
+                       }
+
+                       if (pfd.revents & POLLNVAL) {
+                               log_warning("Ping POLLNVAL");
+                               rc = ISCSI_ERR_INTERNAL;
+                               goto ping_done;
+                       }
+
+                       if (pfd.revents & POLLERR) {
+                               log_warning("Ping POLLERR");
+                               rc = ISCSI_ERR_INTERNAL;
+                               goto ping_done;
+                       }
+               } else if (rc < 0) {
+                       log_error("Ping completion event poll error");
+                       rc = ISCSI_ERR_INTERNAL;
+                       goto ping_done;
+               }
+ping_done:
+               if (i < count)
+                       sleep(interval);
+       }
+       ipc->ctldev_close();
+ping_exit:
+       return rc;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -2091,7 +2280,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 +2386,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 +2479,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 +2494,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 625cb58..9aba9ef 100644
--- a/usr/netlink.c
+++ b/usr/netlink.c
@@ -1024,6 +1024,46 @@ 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, int 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 = (uint32_t)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 void drop_data(struct nlmsghdr *nlh)
 {
        int ev_size;
@@ -1094,6 +1134,14 @@ static int ctldev_handle(void)
                          ev->r.host_event.host_no);
                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))
@@ -1287,6 +1335,7 @@ struct iscsi_ipc nl_ipc = {
        .recv_pdu_end           = krecv_pdu_end,
        .set_net_config         = kset_net_config,
        .recv_conn_state        = krecv_conn_state,
+       .send_ping              = ksend_ping,
 };
 struct iscsi_ipc *ipc = &nl_ipc;
 
-- 
1.7.8.247.g10f4e

-- 
You received this message because you are subscribed to the Google Groups 
"open-iscsi" group.
To post to this group, send email to open-iscsi@googlegroups.com.
To unsubscribe from this group, send email to 
open-iscsi+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/open-iscsi?hl=en.

Reply via email to