diff options
author | Adheer Chandravanshi <adheer.chandravanshi@qlogic.com> | 2015-09-18 06:38:51 -0400 |
---|---|---|
committer | Mike Christie <mchristi@redhat.com> | 2015-12-28 14:03:35 -0600 |
commit | 1e692d5a31e0ae9741e040367a7bac50c19c71c6 (patch) | |
tree | 435f171b67b168b898128748055787510f88b2db /iscsiuio | |
parent | 74478b00226856ca00a4797c82df5266fd9b9d80 (diff) | |
download | open-iscsi-1e692d5a31e0ae9741e040367a7bac50c19c71c6.tar.gz |
iscsiuio: Add ping support through iscsiuio
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.chandravanshi@qlogic.com>
Diffstat (limited to 'iscsiuio')
-rw-r--r-- | iscsiuio/src/uip/ipv6.c | 9 | ||||
-rw-r--r-- | iscsiuio/src/uip/uip.c | 7 | ||||
-rw-r--r-- | iscsiuio/src/uip/uip.h | 5 | ||||
-rw-r--r-- | iscsiuio/src/unix/Makefile.am | 3 | ||||
-rw-r--r-- | iscsiuio/src/unix/iscsid_ipc.c | 136 | ||||
-rw-r--r-- | iscsiuio/src/unix/libs/cnic.c | 12 | ||||
-rw-r--r-- | iscsiuio/src/unix/nic.c | 20 | ||||
-rw-r--r-- | iscsiuio/src/unix/nic.h | 20 | ||||
-rw-r--r-- | iscsiuio/src/unix/ping.c | 518 | ||||
-rw-r--r-- | iscsiuio/src/unix/ping.h | 73 |
10 files changed, 779 insertions, 24 deletions
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__ */ |