From: Adheer Chandravanshi <adheer.chandravan...@qlogic.com>

This adds the support of ping from drivers like bnx2i
that use iscsiuio. It supports both IPv4 and IPv6 ping.

Two new files are added: ping.c and ping.h

Signed-off-by: Adheer Chandravanshi <adheer.chandravan...@qlogic.com>
---
 iscsiuio/src/uip/ipv6.c        |    9 +
 iscsiuio/src/uip/uip.c         |    7 +
 iscsiuio/src/uip/uip.h         |    5 +
 iscsiuio/src/unix/Makefile.am  |    3 +-
 iscsiuio/src/unix/iscsid_ipc.c |  136 ++++++++++-
 iscsiuio/src/unix/libs/cnic.c  |   12 +-
 iscsiuio/src/unix/nic.c        |   20 +-
 iscsiuio/src/unix/nic.h        |   20 ++
 iscsiuio/src/unix/ping.c       |  518 ++++++++++++++++++++++++++++++++++++++++
 iscsiuio/src/unix/ping.h       |   73 ++++++
 10 files changed, 779 insertions(+), 24 deletions(-)
 create mode 100644 iscsiuio/src/unix/ping.c
 create mode 100644 iscsiuio/src/unix/ping.h

diff --git a/iscsiuio/src/uip/ipv6.c b/iscsiuio/src/uip/ipv6.c
index 5c627ac..ced98a6 100644
--- a/iscsiuio/src/uip/ipv6.c
+++ b/iscsiuio/src/uip/ipv6.c
@@ -47,6 +47,7 @@
 #include "icmpv6.h"
 #include "uipopt.h"
 #include "dhcpv6.h"
+#include "ping.h"
 
 inline int best_match_bufcmp(u8_t *a, u8_t *b, int len)
 {
@@ -811,6 +812,9 @@ static void ipv6_icmp_rx(struct ipv6_context *context)
                        (struct ipv6_hdr *)context->ustack->network_layer;
        struct icmpv6_hdr *icmp = (struct icmpv6_hdr *)((u8_t *)ipv6 +
                                                sizeof(struct ipv6_hdr));
+       uip_icmp_echo_hdr_t *icmp_echo_hdr =
+                               (uip_icmp_echo_hdr_t *)((u8_t *)ipv6 +
+                                               sizeof(struct ipv6_hdr));
 
        switch (icmp->icmpv6_type) {
        case ICMPV6_RTR_ADV:
@@ -830,6 +834,11 @@ static void ipv6_icmp_rx(struct ipv6_context *context)
                ipv6_icmp_handle_echo_request(context);
                break;
 
+       case ICMPV6_ECHO_REPLY:
+               /* Handle ICMP reply */
+               process_icmp_packet(icmp_echo_hdr, context->ustack);
+               break;
+
        default:
                break;
        }
diff --git a/iscsiuio/src/uip/uip.c b/iscsiuio/src/uip/uip.c
index ec3d6ce..178d7ac 100644
--- a/iscsiuio/src/uip/uip.c
+++ b/iscsiuio/src/uip/uip.c
@@ -7,6 +7,7 @@
 #include "dhcpc.h"
 #include "ipv6_ndpc.h"
 #include "brcm_iscsi.h"
+#include "ping.h"
 
 /**
  * \defgroup uip The uIP TCP/IP stack
@@ -441,6 +442,7 @@ void uip_init(struct uip_stack *ustack, uint8_t 
ipv6_enabled)
 
        ustack->dhcpc = NULL;
        ustack->ndpc = NULL;
+       ustack->ping_conf = NULL;
 }
 void uip_reset(struct uip_stack *ustack)
 {
@@ -1425,6 +1427,11 @@ icmp_input:
 #endif /* UIP_PINGADDRCONF */
                ++ustack->stats.icmp.recv;
 
+               if (icmpv4_hdr->type == ICMP_ECHO_REPLY) {
+                       if (process_icmp_packet(icmpv4_hdr, ustack) == 0)
+                               goto drop;
+               }
+
                /* ICMP echo (i.e., ping) processing. This is simple, we only
                   change the ICMP type from ECHO to ECHO_REPLY and adjust the
                   ICMP checksum before we return the packet. */
diff --git a/iscsiuio/src/uip/uip.h b/iscsiuio/src/uip/uip.h
index 0225f6a..1180ab5 100644
--- a/iscsiuio/src/uip/uip.h
+++ b/iscsiuio/src/uip/uip.h
@@ -1283,6 +1283,8 @@ struct __attribute__ ((__packed__)) uip_icmpv4_hdr {
        u16_t id, seqno;
 };
 
+typedef struct uip_icmpv4_hdr uip_icmp_echo_hdr_t;
+
 /* The ICMPv6 */
 struct __attribute__ ((__packed__)) uip_icmpv6_hdr {
        /* ICMP (echo) header. */
@@ -1554,6 +1556,8 @@ struct uip_stack {
 
        /* NDP client */
        void *ndpc;
+
+       void *ping_conf;
 };
 
 
/*******************************************************************************
@@ -1563,6 +1567,7 @@ int set_ipv6_link_local_address(struct uip_stack *ustack);
 int is_ipv6_link_local_address(uip_ip6addr_t *addr);
 
 void dump_uip_packet(struct uip_stack *ustack);
+u16_t uip_icmp6chksum(struct uip_stack *ustack);
 
 #endif /* __UIP_H__ */
 
diff --git a/iscsiuio/src/unix/Makefile.am b/iscsiuio/src/unix/Makefile.am
index 898691d..71d5463 100644
--- a/iscsiuio/src/unix/Makefile.am
+++ b/iscsiuio/src/unix/Makefile.am
@@ -19,7 +19,8 @@ iscsiuio_SOURCES =    build_date.c            \
                        nic_nl.c                \
                        nic_utils.c             \
                        packet.c                \
-                       iscsid_ipc.c
+                       iscsid_ipc.c            \
+                       ping.c
 
 iscsiuio_CFLAGS =      $(AM_CFLAGS)            \
                        $(LIBNL_CFLAGS)         \
diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c
index 3e92d90..01eba6c 100644
--- a/iscsiuio/src/unix/iscsid_ipc.c
+++ b/iscsiuio/src/unix/iscsid_ipc.c
@@ -64,6 +64,7 @@
 
 #include "logger.h"
 #include "uip.h"
+#include "ping.h"
 
 /*  private iscsid options stucture */
 struct iscsid_options {
@@ -323,7 +324,45 @@ static int decode_iface(struct iface_rec_decode *ird, 
struct iface_rec *rec)
        return rc;
 }
 
-static int parse_iface(void *arg)
+static void *perform_ping(void *arg)
+{
+       struct ping_conf *png_c = (struct ping_conf *)arg;
+       nic_interface_t *nic_iface = png_c->nic_iface;
+       nic_t *nic = nic_iface->parent;
+       iscsid_uip_broadcast_t *data;
+       struct sockaddr_in *addr;
+       struct sockaddr_in6 *addr6;
+       uip_ip6addr_t dst_addr;
+       int rc = 0;
+       int datalen;
+
+       data = (iscsid_uip_broadcast_t *)png_c->data;
+       datalen = data->u.ping_rec.datalen;
+
+       memset(dst_addr, 0, sizeof(uip_ip6addr_t));
+       if (nic_iface->protocol == AF_INET) {
+               /* IPv4 */
+               addr = (struct sockaddr_in *)&data->u.ping_rec.ipaddr;
+               memcpy(dst_addr, &addr->sin_addr.s_addr, sizeof(uip_ip4addr_t));
+       } else {
+               /* IPv6 */
+               addr6 = (struct sockaddr_in6 *)&data->u.ping_rec.ipaddr;
+               memcpy(dst_addr, &addr6->sin6_addr.s6_addr,
+                      sizeof(uip_ip6addr_t));
+       }
+
+       ping_init(png_c, dst_addr, nic_iface->protocol, datalen);
+
+       rc = do_ping_from_nic_iface(png_c);
+       if (png_c->state == -1)
+               png_c->state = rc;
+
+       LOG_INFO(PFX "ping thread end");
+       nic->ping_thread = INVALID_THREAD;
+       pthread_exit(NULL);
+}
+
+static int parse_iface(void *arg, int do_ping)
 {
        int rc, i;
        nic_t *nic = NULL;
@@ -338,9 +377,14 @@ static int parse_iface(void *arg)
        struct iface_rec_decode ird;
        struct in_addr src_match, dst_match;
        pthread_attr_t attr;
+       struct ping_conf *png_c;
 
        data = (iscsid_uip_broadcast_t *) arg;
-       rec = &data->u.iface_rec.rec;
+       if (do_ping)
+               rec = &data->u.ping_rec.ifrec;
+       else
+               rec = &data->u.iface_rec.rec;
+
        LOG_INFO(PFX "Received request for '%s' to set IP address: '%s' "
                 "VLAN: '%d'",
                 rec->netdev,
@@ -786,6 +830,54 @@ eagain:
                                             ipv6_buf_str,
                 ird.vlan_id, rec->transport_name);
 
+       if (do_ping) {
+               if ((nic->flags & NIC_GOING_DOWN) ||
+                     (nic->state != NIC_RUNNING) ||
+                     !(nic->flags & NIC_ENABLED)) {
+                       LOG_INFO(PFX "%s: Device is not ready for ping",
+                                nic->log_name);
+                       rc = -EAGAIN;
+                       goto done;
+               }
+
+               if (nic->ping_thread != INVALID_THREAD) {
+                       rc = pthread_cancel(nic->ping_thread);
+                       if (rc != 0) {
+                               LOG_INFO(PFX "%s: failed to cancel ping thread",
+                                        nic->log_name);
+                               rc = -EAGAIN;
+                               goto done;
+                       }
+               }
+
+               png_c = malloc(sizeof(struct ping_conf));
+               if (!png_c) {
+                       LOG_ERR(PFX "Memory alloc failed for ping conf");
+                       rc = -ENOMEM;
+                       goto done;
+               }
+
+               memset(png_c, 0, sizeof(struct ping_conf));
+               png_c->nic_iface = nic_iface;
+               png_c->data = arg;
+               nic_iface->ustack.ping_conf = png_c;
+
+               /* Spawn a thread to perform ping operation.
+                * This thread will exit when done.
+                */
+               rc = pthread_create(&nic->ping_thread, NULL,
+                                   perform_ping, (void *)png_c);
+               if (rc != 0) {
+                       LOG_WARN(PFX "%s: failed starting ping thread\n",
+                                nic->log_name);
+               } else {
+                       pthread_join(nic->ping_thread, NULL);
+                       rc = png_c->state;
+               }
+               free(png_c);
+               nic_iface->ustack.ping_conf = NULL;
+       }
+
 done:
        pthread_mutex_unlock(&nic_list_mutex);
 
@@ -837,16 +929,16 @@ int process_iscsid_broadcast(int s2)
        LOG_DEBUG(PFX "recv iscsid request: cmd: %d, payload_len: %d",
                  cmd, payload_len);
 
-       size = fread(&data->u.iface_rec, payload_len, 1, fd);
-       if (!size) {
-               LOG_ERR(PFX "Could not read data: %d(%s)",
-                       errno, strerror(errno));
-               goto error;
-       }
-
        switch (cmd) {
        case ISCSID_UIP_IPC_GET_IFACE:
-               rc = parse_iface(data);
+               size = fread(&data->u.iface_rec, payload_len, 1, fd);
+               if (!size) {
+                       LOG_ERR(PFX "Could not read data: %d(%s)",
+                               errno, strerror(errno));
+                       goto error;
+               }
+
+               rc = parse_iface(data, 0);
                switch (rc) {
                case 0:
                        rsp.command = cmd;
@@ -862,6 +954,30 @@ int process_iscsid_broadcast(int s2)
                }
 
                break;
+       case ISCSID_UIP_IPC_PING:
+               size = fread(&data->u.ping_rec, payload_len, 1, fd);
+               if (!size) {
+                       LOG_ERR(PFX "Could not read data: %d(%s)",
+                               errno, strerror(errno));
+                       goto error;
+               }
+
+               rc = parse_iface(data, 1);
+               rsp.command = cmd;
+               rsp.ping_sc = rc;
+
+               switch (rc) {
+               case 0:
+                       rsp.err = ISCSID_UIP_MGMT_IPC_DEVICE_UP;
+                       break;
+               case -EAGAIN:
+                       rsp.err = ISCSID_UIP_MGMT_IPC_DEVICE_INITIALIZING;
+                       break;
+               default:
+                       rsp.err = ISCSID_UIP_MGMT_IPC_ERR;
+               }
+
+               break;
        default:
                LOG_WARN(PFX "Unknown iscsid broadcast command: %x",
                         data->header.command);
diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c
index 7f473c4..228c4b9 100644
--- a/iscsiuio/src/unix/libs/cnic.c
+++ b/iscsiuio/src/unix/libs/cnic.c
@@ -427,8 +427,10 @@ done:
        if (status != 0 || rc != 0)
                pthread_mutex_unlock(&nic->xmit_mutex);
 
-       cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr,
-                         nic_iface, status, AF_INET);
+       if (ev) {
+               cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr,
+                                 nic_iface, status, AF_INET);
+       }
 
        return rc;
 }
@@ -624,8 +626,10 @@ done:
        if (status != 0 || rc != 0)
                pthread_mutex_unlock(&nic->xmit_mutex);
 
-       cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr,
-                         nic_iface, status, AF_INET6);
+       if (ev) {
+               cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr,
+                                 nic_iface, status, AF_INET6);
+       }
        return rc;
 }
 
diff --git a/iscsiuio/src/unix/nic.c b/iscsiuio/src/unix/nic.c
index 38a5776..cd0c80a 100644
--- a/iscsiuio/src/unix/nic.c
+++ b/iscsiuio/src/unix/nic.c
@@ -424,6 +424,8 @@ nic_t *nic_init()
        nic->nl_process_tail = 0;
        memset(&nic->nl_process_ring, 0, sizeof(nic->nl_process_ring));
 
+       nic->ping_thread = INVALID_THREAD;
+
        return nic;
 }
 
@@ -789,9 +791,9 @@ int nic_process_intr(nic_t *nic, int discard_check)
        return ret;
 }
 
-static void prepare_ipv4_packet(nic_t *nic,
-                               nic_interface_t *nic_iface,
-                               struct uip_stack *ustack, packet_t *pkt)
+void prepare_ipv4_packet(nic_t *nic,
+                        nic_interface_t *nic_iface,
+                        struct uip_stack *ustack, packet_t *pkt)
 {
        u16_t ipaddr[2];
        arp_table_query_t arp_query;
@@ -835,9 +837,9 @@ static void prepare_ipv4_packet(nic_t *nic,
        }
 }
 
-static void prepare_ipv6_packet(nic_t *nic,
-                               nic_interface_t *nic_iface,
-                               struct uip_stack *ustack, packet_t *pkt)
+void prepare_ipv6_packet(nic_t *nic,
+                        nic_interface_t *nic_iface,
+                        struct uip_stack *ustack, packet_t *pkt)
 {
        struct uip_eth_hdr *eth;
        struct uip_vlan_eth_hdr *eth_vlan;
@@ -860,9 +862,9 @@ static void prepare_ipv6_packet(nic_t *nic,
        }
 }
 
-static void prepare_ustack(nic_t *nic,
-                          nic_interface_t *nic_iface,
-                          struct uip_stack *ustack, struct packet *pkt)
+void prepare_ustack(nic_t *nic,
+                   nic_interface_t *nic_iface,
+                   struct uip_stack *ustack, struct packet *pkt)
 {
        struct ether_header *eth = NULL;
        ustack->uip_buf = pkt->buf;
diff --git a/iscsiuio/src/unix/nic.h b/iscsiuio/src/unix/nic.h
index 8484032..40ca546 100644
--- a/iscsiuio/src/unix/nic.h
+++ b/iscsiuio/src/unix/nic.h
@@ -52,6 +52,7 @@
 #include "nic_nl.h"
 #include "packet.h"
 #include "uip.h"
+#include "timer.h"
 
 #include "iscsi_if.h"
 
@@ -330,6 +331,9 @@ typedef struct nic {
 #define NIC_NL_PROCESS_LAST_ENTRY           (NIC_NL_PROCESS_MAX_RING_SIZE - 1)
 #define NIC_NL_PROCESS_NEXT_ENTRY(x) ((x + 1) & NIC_NL_PROCESS_MAX_RING_SIZE)
        void *nl_process_ring[NIC_NL_PROCESS_MAX_RING_SIZE];
+
+       /* The thread used to perform ping */
+       pthread_t ping_thread;
 } nic_t;
 
 /******************************************************************************
@@ -381,4 +385,20 @@ void *nic_loop(void *arg);
 
 int nic_packet_capture(struct nic *, struct packet *pkt);
 
+int process_packets(nic_t *nic,
+                   struct timer *periodic_timer,
+                   struct timer *arp_timer, nic_interface_t *nic_iface);
+
+void prepare_ustack(nic_t *nic,
+                   nic_interface_t *nic_iface,
+                   struct uip_stack *ustack, struct packet *pkt);
+
+void prepare_ipv4_packet(nic_t *nic,
+                        nic_interface_t *nic_iface,
+                        struct uip_stack *ustack, struct packet *pkt);
+
+void prepare_ipv6_packet(nic_t *nic,
+                        nic_interface_t *nic_iface,
+                        struct uip_stack *ustack, struct packet *pkt);
+
 #endif /* __NIC_H__ */
diff --git a/iscsiuio/src/unix/ping.c b/iscsiuio/src/unix/ping.c
new file mode 100644
index 0000000..58a6d14
--- /dev/null
+++ b/iscsiuio/src/unix/ping.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright (c) 2015, QLogic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ping.c - Ping implementation for iscsiuio using ICMP/ICMPv6
+ *
+ */
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "iscsi_if.h"
+
+#include "uip.h"
+#include "uip_arp.h"
+#include "uip_eth.h"
+#include "dhcpc.h"
+#include "ipv6_ndpc.h"
+#include "ipv6.h"
+
+#include "logger.h"
+#include "nic.h"
+#include "nic_utils.h"
+#include "options.h"
+#include "packet.h"
+#include "bnx2.h"
+#include "bnx2x.h"
+#include "cnic.h"
+#include "ping.h"
+
+#define PFX "ping "
+
+static void fill_payload_data(struct uip_stack *ustack)
+{
+       if (ustack->uip_slen)
+               memset(ustack->uip_appdata, 'A', ustack->uip_slen);
+}
+
+static int prepare_icmpv4_req_pkt(struct ping_conf *png_c, struct packet *pkt,
+                                 uip_ip4addr_t *dst_addr)
+{
+       nic_interface_t *nic_iface = png_c->nic_iface;
+       struct uip_stack *ustack = &nic_iface->ustack;
+       struct uip_ipv4_hdr *ipv4_hdr = NULL;
+       struct uip_icmpv4_hdr *icmpv4_hdr = NULL;
+       u16_t uip_iph_len = 0;
+       u16_t icmpv4_hdr_len = 0;
+       u16_t uip_ip_icmph_len = 0;
+       int mtu = 1500;
+       int rc = 0;
+
+       uip_iph_len = UIP_IPv4_H_LEN;
+       icmpv4_hdr_len = sizeof(*icmpv4_hdr);
+       uip_ip_icmph_len = uip_iph_len + icmpv4_hdr_len;
+
+       ipv4_hdr = (struct uip_ipv4_hdr *)ustack->network_layer;
+
+       icmpv4_hdr = (struct uip_icmpv4_hdr *) (ustack->network_layer +
+                                               sizeof(struct uip_ipv4_hdr));
+
+       /* fill IP header */
+       ipv4_hdr->vhl = 0x45;
+       ipv4_hdr->tos = 0;
+       ++ustack->ipid;
+       ipv4_hdr->ipid[0] = ustack->ipid >> 8;
+       ipv4_hdr->ipid[1] = ustack->ipid & 0xff;
+       ipv4_hdr->ipoffset[0] = 0;
+       ipv4_hdr->ipoffset[1] = 0;
+       ipv4_hdr->ttl = UIP_TTL;
+       ipv4_hdr->proto = UIP_PROTO_ICMP;
+       uip_ip4addr_copy(ipv4_hdr->srcipaddr, ustack->hostaddr);
+       uip_ip4addr_copy(ipv4_hdr->destipaddr, dst_addr);
+
+       LOG_INFO(PFX "src ipaddr: %d.%d.%d.%d",
+                uip_ipaddr1(ipv4_hdr->srcipaddr),
+                uip_ipaddr2(ipv4_hdr->srcipaddr),
+                uip_ipaddr3(ipv4_hdr->srcipaddr),
+                uip_ipaddr4(ipv4_hdr->srcipaddr));
+
+       LOG_INFO(PFX "dest ipaddr: %d.%d.%d.%d",
+                uip_ipaddr1(ipv4_hdr->destipaddr),
+                uip_ipaddr2(ipv4_hdr->destipaddr),
+                uip_ipaddr3(ipv4_hdr->destipaddr),
+                uip_ipaddr4(ipv4_hdr->destipaddr));
+
+       /* fill ICMP header */
+       icmpv4_hdr->type = ICMP_ECHO;
+       icmpv4_hdr->icode = 0;
+       icmpv4_hdr->id = getpid() & 0xffff;
+       png_c->id = icmpv4_hdr->id;
+       icmpv4_hdr->seqno = ustack->ipid;
+       png_c->seqno =icmpv4_hdr->seqno;
+
+       /* appdata and sappdata point to the icmp payload */
+       ustack->uip_appdata = ustack->network_layer + uip_ip_icmph_len;
+       ustack->uip_sappdata = ustack->uip_appdata;
+
+       if (nic_iface->mtu)
+               mtu = nic_iface->mtu;
+
+       if ((mtu - uip_ip_icmph_len) > png_c->datalen) {
+               ustack->uip_slen = png_c->datalen;
+       } else {
+               png_c->state = ISCSI_PING_OVERSIZE_PACKET;
+               LOG_ERR(PFX "MTU=%d, payload=%d\n",
+                       mtu, png_c->datalen);
+               rc = -EINVAL;
+               goto done;
+       }
+
+       fill_payload_data(ustack);
+
+       /* Calculate ICMP checksum. */
+       icmpv4_hdr->icmpchksum = 0;
+       icmpv4_hdr->icmpchksum = ~(uip_chksum((u16_t *)icmpv4_hdr,
+                                             icmpv4_hdr_len +
+                                             ustack->uip_slen));
+       if (icmpv4_hdr->icmpchksum == 0)
+               icmpv4_hdr->icmpchksum = 0xffff;
+
+       /* IPv4 total length = IPv4 HLEN + ICMP HLEN + Payload len */
+       ustack->uip_len = uip_ip_icmph_len + ustack->uip_slen;
+       ipv4_hdr->len[0] = (ustack->uip_len >> 8);
+       ipv4_hdr->len[1] = (ustack->uip_len & 0xff);
+
+       /* Calculate IP checksum. */
+       ipv4_hdr->ipchksum = 0;
+       ipv4_hdr->ipchksum = ~(uip_ipchksum(ustack));
+       if (ipv4_hdr->ipchksum == 0)
+               ipv4_hdr->ipchksum = 0xffff;
+
+        ++ustack->stats.ip.sent;
+        /* Return and let the caller do the actual transmission. */
+        ustack->uip_flags = 0;
+
+done:
+       return rc;
+}
+
+static void prepare_icmpv6_req_pkt(struct ping_conf *png_c, struct packet *pkt,
+                                  uip_ip6addr_t *dst_addr,
+                                  uip_ip6addr_t *src_addr)
+{
+       nic_interface_t *nic_iface = png_c->nic_iface;
+       struct uip_stack *ustack = &nic_iface->ustack;
+       struct uip_ipv6_hdr *ipv6_hdr = NULL;
+       uip_icmp_echo_hdr_t *icmp_echo_hdr = NULL;
+       u16_t uip_iph_len = 0;
+       u16_t icmp_echo_hdr_len = 0;
+       u16_t uip_ip_icmph_len = 0;
+       char ipbuf[INET6_ADDRSTRLEN] = {0};
+
+       uip_iph_len = UIP_IPv6_H_LEN;
+       icmp_echo_hdr_len = sizeof(*icmp_echo_hdr);
+       uip_ip_icmph_len = uip_iph_len + icmp_echo_hdr_len;
+
+       ipv6_hdr = (struct uip_ipv6_hdr *)ustack->network_layer;
+
+       icmp_echo_hdr = (uip_icmp_echo_hdr_t *) (ustack->network_layer +
+                                                sizeof(struct uip_ipv6_hdr));
+
+       /* fill IPv6 header */
+       ipv6_hdr->vtc = 0x60;
+       ipv6_hdr->tcflow = 0;
+       ipv6_hdr->flow = 0;
+       ipv6_hdr->proto = UIP_PROTO_ICMP6;
+       ipv6_hdr->ttl = UIP_TTL;
+       uip_ip6addr_copy(ipv6_hdr->srcipaddr, src_addr);
+       uip_ip6addr_copy(ipv6_hdr->destipaddr, dst_addr);
+
+       memset(ipbuf, 0, sizeof(ipbuf));
+       if (inet_ntop(AF_INET6, &ipv6_hdr->srcipaddr, ipbuf, INET6_ADDRSTRLEN))
+               LOG_INFO(PFX "src ipaddr=%s", ipbuf);
+
+       memset(ipbuf, 0, sizeof(ipbuf));
+       if (inet_ntop(AF_INET6, &ipv6_hdr->destipaddr, ipbuf, INET6_ADDRSTRLEN))
+               LOG_INFO(PFX "dest ipaddr=%s", ipbuf);
+
+       /* fill ICMP header */
+       icmp_echo_hdr->type = ICMPV6_ECHO_REQ;
+       icmp_echo_hdr->icode = 0;
+       icmp_echo_hdr->id = HOST_TO_NET16(getpid() & 0xffff);
+       png_c->id = icmp_echo_hdr->id;
+       ++ustack->ipid;
+       icmp_echo_hdr->seqno = HOST_TO_NET16(ustack->ipid);
+       png_c->seqno = icmp_echo_hdr->seqno;
+
+       /* appdata and sappdata point to the icmp payload */
+       ustack->uip_appdata = ustack->network_layer + uip_ip_icmph_len;
+       ustack->uip_sappdata = ustack->uip_appdata;
+       ustack->uip_slen = png_c->datalen;
+
+       fill_payload_data(ustack);
+
+       /* Total length = ETH HLEN + IPv6 HLEN + ICMP HLEN + Data len */
+       ustack->uip_len =  UIP_LLH_LEN + uip_ip_icmph_len + ustack->uip_slen;
+       /* IPv6 payload len */
+       ipv6_hdr->len = HOST_TO_NET16(icmp_echo_hdr_len + ustack->uip_slen);
+
+       /* Calculate ICMP checksum. */
+       icmp_echo_hdr->icmpchksum = 0;
+       icmp_echo_hdr->icmpchksum = ~(uip_icmp6chksum(ustack));
+
+        ++ustack->stats.ip.sent;
+        /* Return and let the caller do the actual transmission. */
+        ustack->uip_flags = 0;
+       return;
+}
+
+static int chk_arp_entry_for_dst_addr(nic_t *nic, nic_interface_t *nic_iface,
+                                     void *addr)
+{
+       struct iscsi_path path;
+       uip_ip4addr_t dst_addr4;
+       uip_ip6addr_t dst_addr6;
+
+       if (nic_iface->protocol == AF_INET) {
+               memcpy(dst_addr4, addr, sizeof(uip_ip4addr_t));
+               memcpy(&path.dst.v4_addr, dst_addr4, sizeof(struct in_addr));
+               path.ip_addr_len = 4;
+       } else {
+               memcpy(dst_addr6, addr, sizeof(uip_ip6addr_t));
+               memcpy(&path.dst.v6_addr, dst_addr6, sizeof(struct in6_addr));
+               path.ip_addr_len = 16;
+       }
+
+       return cnic_handle_iscsi_path_req(nic, 0, NULL, &path, nic_iface);
+}
+
+static int fill_icmpv6_eth_hdr(struct uip_stack *ustack,
+                               uip_ip6addr_t *dst_addr6)
+{
+       struct uip_eth_hdr *eth;
+       __u8 mac_addr[6];
+       struct ndpc_reqptr req_ptr;
+       int rc = 0;
+       int ret = 0;
+
+       eth = (struct uip_eth_hdr *)ustack->data_link_layer;
+       memcpy(eth->src.addr, ustack->uip_ethaddr.addr, sizeof(eth->src.addr));
+
+       memset(mac_addr, 0, sizeof(mac_addr));
+       req_ptr.eth = (void *)mac_addr;
+       req_ptr.ipv6 = (void *)dst_addr6;
+
+       ret = ndpc_request(ustack, &req_ptr, &rc, CHECK_ARP_TABLE);
+       if (ret) {
+               LOG_DEBUG(PFX "ndpc request failed");
+               rc = ret;
+       } else if (rc) {
+               memcpy(eth->dest.addr, mac_addr, sizeof(eth->dest.addr));
+               LOG_DEBUG(PFX "ipv6 arp entry present");
+               rc = 0;
+       } else {
+               LOG_DEBUG(PFX "ipv6 arp entry not present");
+               rc = -EAGAIN;
+       }
+
+       return rc;
+}
+
+static int determine_src_ipv6_addr(nic_interface_t *nic_iface,
+                                  uip_ip6addr_t *dst_addr6,
+                                  uip_ip6addr_t *src_addr6)
+{
+       struct in6_addr *addr;
+       int rc = 0;
+       int ret = 0;
+
+       if (nic_iface->ustack.ip_config == IPV6_CONFIG_STATIC) {
+               memcpy(src_addr6, &nic_iface->ustack.hostaddr6,
+                      sizeof(uip_ip6addr_t));
+               goto done;
+       }
+
+       ret = ndpc_request(&nic_iface->ustack, dst_addr6,
+                        &rc, CHECK_LINK_LOCAL_ADDR);
+       if (ret) {
+               LOG_DEBUG(PFX "Check LL failed");
+               rc = ret;
+               goto done;
+       }
+
+       if (rc) {
+               LOG_DEBUG(PFX "Use LL");
+               /* Get link local IPv6 address */
+               addr = (struct in6_addr *)&nic_iface->ustack.linklocal6;
+               rc = 0;
+       } else {
+               LOG_DEBUG(PFX "Use Best matched");
+               ret = ndpc_request(&nic_iface->ustack,
+                                dst_addr6,
+                                &addr, GET_HOST_ADDR);
+               if (ret) {
+                       LOG_DEBUG(PFX "Use Best matched failed");
+                       rc = ret;
+                       goto done;
+               }
+               if (addr == NULL) {
+                       LOG_DEBUG(PFX "No Best matched found");
+                       rc = -EINVAL;
+                       goto done;
+               }
+       }
+
+       /* Got the best matched src IP address */
+       memcpy(src_addr6, addr, sizeof(struct in6_addr));
+
+done:
+       return rc;
+}
+
+void ping_init(struct ping_conf *png_c, void *addr, u16_t type, int datalen)
+{
+       png_c->dst_addr = addr;
+       png_c->proto = type;
+       png_c->state = PING_INIT_STATE;
+       png_c->datalen = datalen;
+       return;
+}
+
+int do_ping_from_nic_iface(struct ping_conf *png_c)
+{
+       packet_t *pkt;
+       nic_interface_t *nic_iface = png_c->nic_iface;
+       nic_t *nic = nic_iface->parent;
+       struct uip_stack *ustack = &nic_iface->ustack;
+       uip_ip4addr_t dst_addr4;
+       uip_ip6addr_t dst_addr6;
+       uip_ip6addr_t src_addr6;
+       struct timer ping_timer;
+       int rc = 0;
+
+       memset(dst_addr4, 0, sizeof(uip_ip4addr_t));
+       memset(dst_addr6, 0, sizeof(uip_ip6addr_t));
+       memset(src_addr6, 0, sizeof(uip_ip6addr_t));
+
+       if (nic_iface->protocol == AF_INET)
+               memcpy(dst_addr4, png_c->dst_addr, sizeof(uip_ip4addr_t));
+       else
+               memcpy(dst_addr6, png_c->dst_addr, sizeof(uip_ip6addr_t));
+
+       rc = chk_arp_entry_for_dst_addr(nic, nic_iface, png_c->dst_addr);
+
+       if (rc && (nic_iface->protocol == AF_INET)) {
+               png_c->state = ISCSI_PING_NO_ARP_RECEIVED;
+               LOG_ERR(PFX "ARP failure for IPv4 dest addr");
+               goto done;
+       } else if ((rc < 1) && (nic_iface->protocol == AF_INET6)) {
+               png_c->state = ISCSI_PING_NO_ARP_RECEIVED;
+               LOG_ERR(PFX "ARP failure for IPv6 dest addr");
+               goto done;
+       } else if (rc < 0) {
+               LOG_ERR(PFX "ARP failure");
+               goto done;
+       }
+
+       pthread_mutex_lock(&nic->nic_mutex);
+       pkt = get_next_free_packet(nic);
+       if (pkt == NULL) {
+               pthread_mutex_unlock(&nic->nic_mutex);
+               LOG_ERR(PFX "Unable to get a free packet buffer");
+               rc = -EIO;
+               goto done;
+       }
+
+       prepare_ustack(nic, nic_iface, ustack, pkt);
+
+       if (nic_iface->protocol == AF_INET) {
+               rc = prepare_icmpv4_req_pkt(png_c, pkt, &dst_addr4);
+               if (rc)
+                       goto put_pkt;
+
+               /* If the above function invocation resulted
+                * in data that should be sent out on the
+                * network, the global variable uip_len is
+                * set to a value > 0. */
+               if (ustack->uip_len > 0) {
+                       pkt->buf_size = ustack->uip_len;
+
+                       prepare_ipv4_packet(nic, nic_iface, ustack, pkt);
+
+                       LOG_DEBUG(PFX "Send ICMP echo request");
+                       (*nic->ops->write) (nic, nic_iface, pkt);
+                       ustack->uip_len = 0;
+               }
+       } else {
+               rc = determine_src_ipv6_addr(nic_iface, &dst_addr6, &src_addr6);
+               if (rc)
+                       goto put_pkt;
+
+               prepare_icmpv6_req_pkt(png_c, pkt, &dst_addr6, &src_addr6);
+
+               /* If the above function invocation resulted
+                * in data that should be sent out on the
+                * network, the global variable uip_len is
+                * set to a value > 0. */
+               if (ustack->uip_len > 0) {
+                       pkt->buf_size = ustack->uip_len;
+
+                       prepare_ipv6_packet(nic, nic_iface, ustack, pkt);
+                       rc = fill_icmpv6_eth_hdr(ustack, &dst_addr6);
+                       if (rc) {
+                               ustack->uip_len = 0;
+                               goto put_pkt;
+                       }
+
+                       LOG_DEBUG(PFX "Send ICMPv6 echo request");
+                       (*nic->ops->write) (nic, nic_iface, pkt);
+                       ustack->uip_len = 0;
+               }
+       }
+
+put_pkt:
+       put_packet_in_free_queue(pkt, nic);
+       pthread_mutex_unlock(&nic->nic_mutex);
+
+       if (rc) {
+               LOG_DEBUG(PFX "Ping request not transmitted");
+               goto done;
+       }
+
+       timer_set(&ping_timer, CLOCK_SECOND * 10);
+
+       while ((event_loop_stop == 0) &&
+              (nic->flags & NIC_ENABLED) && !(nic->flags & NIC_GOING_DOWN)) {
+
+               rc = nic_process_intr(nic, 1);
+
+               while ((rc > 0) && (!(nic->flags & NIC_GOING_DOWN))) {
+                       rc = process_packets(nic, NULL, NULL, nic_iface);
+               }
+
+               if (!rc && (png_c->state == ISCSI_PING_SUCCESS)) {
+                       LOG_INFO(PFX "PING successful!");
+                       break;
+               }
+
+               if (timer_expired(&ping_timer)) {
+                       png_c->state = ISCSI_PING_TIMEOUT;
+                       LOG_ERR(PFX "PING timeout");
+                       rc = -EIO;
+                       break;
+               }
+       }
+
+done:
+       return rc;
+}
+
+int process_icmp_packet(uip_icmp_echo_hdr_t *icmp_hdr,
+                       struct uip_stack *ustack)
+{
+       struct ping_conf *png_c = (struct ping_conf *)ustack->ping_conf;
+       int rc = 0;
+
+       LOG_INFO(PFX "Verify ICMP echo reply");
+
+       if ((icmp_hdr->type == ICMPV6_ECHO_REPLY &&
+            png_c->proto == AF_INET6) ||
+            (icmp_hdr->type == ICMP_ECHO_REPLY &&
+             png_c->proto == AF_INET)) {
+
+               if ((icmp_hdr->icode == 0) &&
+                   (icmp_hdr->id == png_c->id) &&
+                   (icmp_hdr->seqno == png_c->seqno)) {
+                               png_c->state = ISCSI_PING_SUCCESS;
+               } else {
+                       rc = 1;
+               }
+       } else {
+               rc = 1;
+       }
+
+       if (rc) {
+               LOG_INFO(PFX "ICMP echo reply verification failed!");
+       } else {
+               LOG_INFO(PFX "ICMP echo reply OK");
+       }
+
+       return rc;
+}
diff --git a/iscsiuio/src/unix/ping.h b/iscsiuio/src/unix/ping.h
new file mode 100644
index 0000000..82ace6f
--- /dev/null
+++ b/iscsiuio/src/unix/ping.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, QLogic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ping.h - PING header file
+ *
+ */
+
+#ifndef __PING_H__
+#define __PING_H__
+
+#include "nic_nl.h"
+#include "uip.h"
+
+#define ICMP_ECHO_REPLY 0
+#define ICMP_ECHO       8
+
+#define ICMPV6_ECHO_REQ                128
+#define ICMPV6_ECHO_REPLY      129
+
+#define DEF_ICMP_PAYLOAD       32
+#define DEF_ICMPV6_PAYLOAD     16
+
+#define PING_INIT_STATE (-1)
+
+struct ping_conf
+{
+       nic_t *nic;
+       nic_interface_t *nic_iface;
+       void *data;
+       int state;
+       void *dst_addr;
+       u16_t proto;
+       u16_t id;
+       u16_t seqno;
+       u16_t datalen;
+};
+
+void ping_init(struct ping_conf *png_c, void *addr, u16_t type, int datalen);
+
+int do_ping_from_nic_iface(struct ping_conf *png_c);
+
+int process_icmp_packet(uip_icmp_echo_hdr_t *icmp_hdr,
+                       struct uip_stack *ustack);
+
+#endif /* __PING_H__ */
-- 
1.7.1

-- 
You received this message because you are subscribed to the Google Groups 
"open-iscsi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to open-iscsi+unsubscr...@googlegroups.com.
To post to this group, send email to open-iscsi@googlegroups.com.
Visit this group at http://groups.google.com/group/open-iscsi.
For more options, visit https://groups.google.com/d/optout.

Reply via email to