aboutsummaryrefslogtreecommitdiffstats
path: root/stacks/lwip_stack/tools/nping.c
diff options
context:
space:
mode:
Diffstat (limited to 'stacks/lwip_stack/tools/nping.c')
-rw-r--r--stacks/lwip_stack/tools/nping.c627
1 files changed, 627 insertions, 0 deletions
diff --git a/stacks/lwip_stack/tools/nping.c b/stacks/lwip_stack/tools/nping.c
new file mode 100644
index 0000000..09e604f
--- /dev/null
+++ b/stacks/lwip_stack/tools/nping.c
@@ -0,0 +1,627 @@
+/*
+*
+* Copyright (c) 2018 Huawei Technologies Co.,Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at:
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <strings.h>
+#include <limits.h>
+#include <getopt.h>
+#include "nstack_securec.h"
+#include "tool_common.h"
+
+/*======== for global variables =======*/
+
+NSTACK_STATIC input_info g_input_info = { 0 };
+static stat_info g_stat_info = { 0 };
+
+static char *g_nping_short_options = "c:r:I:";
+
+int g_exit = 0;
+void
+user_exit (int sig)
+{
+ g_exit = 1;
+}
+
+NSTACK_STATIC inline double
+get_cost_time (struct timespec *pstart, struct timespec *pend)
+{
+ double sec = (double) (pend->tv_sec - pstart->tv_sec);
+ double nsec = (double) (pend->tv_nsec - pstart->tv_nsec);
+
+ return (sec * 1000 + (nsec / 1000000));
+}
+
+NSTACK_STATIC inline double
+get_lost_rate (unsigned int lost_count, unsigned int send_count)
+{
+ if (0 == send_count)
+ {
+ return 0;
+ }
+
+ return ((double) lost_count / send_count);
+}
+
+void
+print_stat (stat_info * info, const char *remote_ip)
+{
+ unsigned int send_count = info->send_seq;
+ unsigned int recv_count = info->recv_ok;
+ unsigned int lost_count = send_count - recv_count;
+ double lost_rate = 100 * get_lost_rate (lost_count, send_count);
+ double cost_time = get_cost_time (&info->start_time, &info->end_time);
+
+ printf ("\n------ %s ping statistics ------\n", remote_ip);
+
+ printf
+ ("%u packets transmitted, %u received, %.2f%% packet loss, time %.2fms\n",
+ send_count, recv_count, lost_rate, cost_time);
+
+ if (0 != recv_count)
+ {
+ double min_interval = info->min_interval;
+ double max_interval = info->max_interval;
+ double avg_interval = info->all_interval / recv_count;
+ printf ("rtt min/avg/max = %.3f/%.3f/%.3f ms\n", min_interval,
+ avg_interval, max_interval);
+ }
+}
+
+NSTACK_STATIC inline u16
+get_chksum (icmp_head * pmsg)
+{
+ int len = sizeof (icmp_head);
+ u32 sum = 0;
+ u16 *msg_stream = (u16 *) pmsg;
+ u16 check_sum = 0;
+
+ while (len > 1)
+ {
+ sum += *msg_stream;
+ len -= 2;
+ msg_stream++;
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ check_sum = ~sum;
+
+ return check_sum;
+}
+
+#ifndef MAX_SHORT
+#define MAX_SHORT 0xFFFF
+#endif
+
+NSTACK_STATIC inline void
+code_icmp_req (icmp_head * pmsg, u32 send_seq, pid_t my_pid)
+{
+ struct timespec cur_time;
+
+ pmsg->icmp_type = ICMP_ECHO;
+ pmsg->icmp_code = 0;
+ pmsg->icmp_cksum = 0;
+ pmsg->icmp_seq = send_seq % (MAX_SHORT + 1);
+ pmsg->icmp_id = my_pid;
+ pmsg->timestamp = 0;
+
+ if (0 != clock_gettime (CLOCK_MONOTONIC, &cur_time))
+ {
+ printf ("Failed to get time, errno = %d\n", errno);
+ }
+
+ pmsg->icmp_sec = cur_time.tv_sec;
+ pmsg->icmp_nsec = cur_time.tv_nsec;
+
+ pmsg->icmp_cksum = get_chksum (pmsg);
+ return;
+}
+
+NSTACK_STATIC int
+send_icmp_req (int socket, pid_t my_pid, stat_info * stat,
+ struct sockaddr_in *remote_addr)
+{
+ int send_ret;
+ icmp_head icmp_req;
+
+ stat->send_seq++;
+ code_icmp_req (&icmp_req, stat->send_seq, my_pid);
+
+ send_ret = sendto (socket, &icmp_req, sizeof (icmp_head), 0,
+ (struct sockaddr *) remote_addr,
+ sizeof (struct sockaddr_in));
+
+ if (send_ret < 0)
+ {
+ printf ("send icmp request failed.\n");
+ return -1;
+ }
+
+ return send_ret;
+}
+
+NSTACK_STATIC inline double
+cal_interval (struct timespec *psend, struct timespec *precv,
+ stat_info * stat)
+{
+ double interval = get_cost_time (psend, precv);
+
+ stat->all_interval += interval;
+
+ if (interval < stat->min_interval)
+ {
+ stat->min_interval = interval;
+ }
+
+ if (interval > stat->max_interval)
+ {
+ stat->max_interval = interval;
+ }
+
+ return interval;
+}
+
+NSTACK_STATIC inline void
+print_info_on_reply (long send_sec, long send_usec, u32 send_seq,
+ stat_info * stat, struct sockaddr_in *dst_addr)
+{
+ struct timespec send_time;
+ struct timespec recv_time;
+ if (0 != clock_gettime (CLOCK_MONOTONIC, &recv_time))
+ {
+ printf ("Failed to get time, errno = %d\n", errno);
+ }
+
+ send_time.tv_sec = send_sec;
+ send_time.tv_nsec = send_usec;
+
+ double interval = cal_interval (&send_time, &recv_time, stat);
+ const char *remote_ip = inet_ntoa (dst_addr->sin_addr);
+ printf ("%lu bytes from %s: icmp_seq=%u, time=%.3f ms\n",
+ sizeof (icmp_head), remote_ip, send_seq % (MAX_SHORT + 1),
+ interval);
+}
+
+NSTACK_STATIC inline void
+print_info_on_no_reply (u32 send_seq, const char *remote_ip)
+{
+ printf ("No data from %s, icmp_seq=%u, Destination Host Unreachable\n",
+ remote_ip, send_seq);
+}
+
+static inline int
+expect_addr (int expect, int addr)
+{
+ return (expect == addr);
+}
+
+NSTACK_STATIC inline int
+parse_icmp_reply (char *buf, unsigned int buf_len, u32 my_pid, u32 send_seq,
+ stat_info * stat, struct sockaddr_in *dst_addr)
+{
+ unsigned int ip_head_len;
+ ip_head *recv_ip_head;
+ icmp_head *recv_icmp_head;
+
+ // parse all received ip package
+ while (buf_len > sizeof (ip_head))
+ {
+ recv_ip_head = (ip_head *) buf;
+
+ ip_head_len = recv_ip_head->ihl << 2;
+ recv_icmp_head = (icmp_head *) (buf + ip_head_len);
+ buf_len -= htons (recv_ip_head->tot_len);
+
+ if (!expect_addr (dst_addr->sin_addr.s_addr, recv_ip_head->local_ip))
+ {
+ return 0;
+ }
+
+ if ((recv_icmp_head->icmp_type == ICMP_REPLY)
+ && (recv_icmp_head->icmp_id == my_pid)
+ && (recv_icmp_head->icmp_seq == send_seq % (MAX_SHORT + 1)))
+ {
+ print_info_on_reply (recv_icmp_head->icmp_sec,
+ recv_icmp_head->icmp_nsec, send_seq, stat,
+ dst_addr);
+
+ return 1;
+ }
+
+ buf += ip_head_len;
+ }
+
+ return 0;
+}
+
+NSTACK_STATIC inline int
+recv_icmp_reply_ok (int socket, int my_pid, u32 send_seq, stat_info * stat,
+ struct sockaddr_in *dst_addr)
+{
+#define MAX_RECV_BUFF_SIZE (200 * sizeof(icmp_head))
+
+ struct sockaddr_in remote_addr;
+ unsigned int addr_len = sizeof (remote_addr);
+ char recv_buf[MAX_RECV_BUFF_SIZE];
+
+ int recv_ret = recvfrom (socket, recv_buf, MAX_RECV_BUFF_SIZE, 0,
+ (struct sockaddr *) &remote_addr, &addr_len);
+
+ if (recv_ret < 0)
+ {
+ return 0;
+ }
+
+ if (!parse_icmp_reply
+ (recv_buf, (unsigned int) recv_ret, my_pid, send_seq, stat, dst_addr))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+// check recv msg in 2 us
+/* BEGIN: Added for PN:CODEDEX by l00351127, 2017/11/14 CID:50811*/
+NSTACK_STATIC void
+recv_icmp_reply (int socket, pid_t my_pid, input_info * input,
+ stat_info * stat, struct sockaddr_in *dst_addr)
+/* END: Added for PN:CODEDEX by l00351127, 2017/11/14 */
+{
+#define MAX_SLEEP_TIME (2 * US_TO_NS)
+#define MAX_WAIT_TIME (1000 * MS_TO_NS)
+
+ int recv_ok = 0;
+ int retry = 0;
+ long sleep_all = 0;
+ long sleep_time = 0;
+
+ u32 expect_seq = stat->send_seq;
+
+ while (retry < input->retry_count)
+ {
+ if (recv_icmp_reply_ok (socket, my_pid, expect_seq, stat, dst_addr))
+ {
+ recv_ok = 1;
+ stat->recv_ok++;
+ break;
+ }
+
+ sleep_all += MAX_SLEEP_TIME;
+ sys_sleep_ns (0, MAX_SLEEP_TIME);
+ retry++;
+ }
+
+ if (!recv_ok)
+ {
+ print_info_on_no_reply (expect_seq, input->dst_ip);
+ }
+
+ if (sleep_all < MAX_WAIT_TIME)
+ {
+ sleep_time = MAX_WAIT_TIME - sleep_all;
+ sys_sleep_ns (0, sleep_time);
+ }
+}
+
+NSTACK_STATIC inline int
+is_digit_nping (char *str)
+{
+ if (NULL == str || '\0' == str[0])
+ {
+ return 0;
+ }
+
+ while (*str)
+ {
+ if (*str > '9' || *str < '0')
+ {
+ return 0;
+ }
+
+ str++;
+ }
+
+ return 1;
+}
+
+#define MIN_IP_LEN_NPING 7 //the length of string ip addr x.x.x.x is 7, 4 numbers + 3 dots = 7
+#define MAX_IP_LEN_NPING 15 //the length of string ip addr xxx.xxx.xxx.xxx is 15, 12 numbers + 3 dots = 15
+
+NSTACK_STATIC inline int
+is_ip_addr_nping (char *param_addr)
+{
+ int ipseg1;
+ int ipseg2;
+ int ipseg3;
+ int ipseg4;
+
+ if (NULL == param_addr)
+ {
+ return 0;
+ }
+
+ size_t len = strlen (param_addr);
+ if (len < MIN_IP_LEN_NPING || len > MAX_IP_LEN_NPING) // valid ip MIN_IP_LEN=7, MAX_IP_LEN=15
+ {
+ printf ("Input IP format error.\n");
+ return 0;
+ }
+
+ if (SSCANF_S (param_addr, "%d.%d.%d.%d", &ipseg1, &ipseg2, &ipseg3, &ipseg4)
+ != 4)
+ {
+ return 0;
+ }
+
+ if ((ipseg1 & 0xffffff00)
+ || (ipseg2 & 0xffffff00)
+ || (ipseg3 & 0xffffff00) || (ipseg4 & 0xffffff00))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+NSTACK_STATIC inline bool
+check_nping_input_para (input_info * input)
+{
+ if (input->send_count < 1)
+ {
+ return false;
+ }
+
+ if (input->retry_count < NPING_RETRY_COUNT
+ || input->retry_count > MAX_NPING_RETRY_COUNT)
+ {
+ return false;
+ }
+
+ if (0 == input->src_ip[0] || 0 == input->dst_ip[0])
+ {
+ return false;
+ }
+
+ return true;
+}
+
+NSTACK_STATIC inline bool
+get_nping_input_para (int argc, char **argv, input_info * input)
+{
+ int opt = 0;
+ bool arg_invalid = false;
+
+ if (argc < 2)
+ {
+ return false;
+ }
+
+ if (!is_ip_addr_nping (argv[1]))
+ {
+ return false;
+ }
+
+ /* CID 36687 (#1 of 2): Unchecked return value (CHECKED_RETURN) */
+ if (EOK != STRCPY_S (input->dst_ip, sizeof (input->dst_ip), argv[1]))
+ {
+ printf ("STRCPY_S failed.\n");
+ return false;
+ }
+
+ while (1)
+ {
+ opt = getopt (argc - 1, &argv[1], g_nping_short_options);
+ if (-1 == opt)
+ {
+ break;
+ }
+
+ switch (opt)
+ {
+ // for short options
+ case 'c':
+ input->send_count = atoi (optarg);
+ break;
+ case 'r':
+ input->retry_count = atoi (optarg);
+ break;
+ case 'I':
+ /* CID 36687 (#2 of 2): Unchecked return value (CHECKED_RETURN) */
+ if (!is_ip_addr_nping (optarg))
+ {
+ return false;
+ }
+ if (EOK != STRCPY_S (input->src_ip, sizeof (input->src_ip), optarg))
+ {
+ printf ("STRCPY_S failed.\n");
+ return false;
+ }
+ break;
+ default:
+ arg_invalid = true;
+ break;
+ }
+
+ if (arg_invalid)
+ {
+ return false;
+ }
+ }
+
+ if (!check_nping_input_para (input))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void
+print_help_nping ()
+{
+ printf
+ ("usage:nping destination [-c send_count -I src_addr -r retry_count]\n");
+ printf ("send count range:1-2147483647\n");
+ printf ("retry count range:1000-20000\n");
+}
+
+NSTACK_STATIC inline void
+init_input_info (input_info * input)
+{
+ if (EOK != MEMSET_S (input, sizeof (input_info), 0, sizeof (input_info)))
+ {
+ printf ("MEMSET_S failed.\n");
+ return;
+ }
+
+ input->send_count = 100000;
+ input->retry_count = NPING_RETRY_COUNT;
+}
+
+NSTACK_STATIC inline void
+init_stat_info (stat_info * stat)
+{
+ stat->max_interval = 0;
+ stat->min_interval = 0xFFFFFFFF;
+}
+
+#ifndef NSTACK_STATIC_CHECK
+int
+main (int argc, char *argv[])
+#else
+int
+nping_main (int argc, char *argv[])
+#endif
+{
+ struct sockaddr_in local_addr;
+ struct sockaddr_in remote_addr;
+ int send_ret;
+ int ret = -1;
+ int icmp_sock;
+
+ pid_t my_pid;
+
+ if (EOK !=
+ MEMSET_S (&local_addr, sizeof (local_addr), 0, sizeof (local_addr)))
+ {
+ printf ("MEMSET_S failed.\n");
+ return 0;
+ }
+ local_addr.sin_family = AF_INET;
+
+ init_input_info (&g_input_info);
+ init_stat_info (&g_stat_info);
+
+ if (!get_nping_input_para (argc, argv, &g_input_info))
+ {
+ print_help_nping ();
+ return 0;
+ }
+
+ if ((icmp_sock = socket (AF_INET, CUSTOM_SOCK_TYPE, IPPROTO_ICMP)) < 0)
+ {
+ printf ("create socket failed.\n");
+ return 0;
+ }
+
+ local_addr.sin_addr.s_addr = inet_addr (g_input_info.src_ip);
+
+ if (0 !=
+ bind (icmp_sock, (struct sockaddr *) &local_addr,
+ sizeof (struct sockaddr)))
+ {
+ printf ("bind failed, src ip %s\n", g_input_info.src_ip);
+ close (icmp_sock);
+ return 0;
+ }
+
+ int opt = 1;
+ ret = ioctl (icmp_sock, FIONBIO, &opt);
+ if (-1 == ret)
+ {
+ printf ("fcntl O_NONBLOCK fail\n");
+ close (icmp_sock);
+ return 0;
+ }
+
+ if (EOK !=
+ MEMSET_S (&remote_addr, sizeof (remote_addr), 0, sizeof (remote_addr)))
+ {
+ printf ("MEMSET_S failed.\n");
+ close (icmp_sock);
+ return 0;
+ }
+
+ remote_addr.sin_family = AF_INET;
+ remote_addr.sin_addr.s_addr = inet_addr (g_input_info.dst_ip);
+
+ my_pid = getpid ();
+ printf ("nping %s %lu bytes of data, pid:%d.\n", g_input_info.dst_ip,
+ sizeof (icmp_head), my_pid);
+
+ signal (SIGINT, user_exit);
+
+ int loop_count = 0;
+ if (0 != clock_gettime (CLOCK_MONOTONIC, &g_stat_info.start_time))
+ {
+ printf ("Failed to get time, errno = %d\n", errno);
+ }
+
+/* BEGIN: Added for PN:CODEDEX by l00351127, 2017/11/14 CID:50811*/
+ i32 send_count = g_input_info.send_count;
+/* END: Added for PN:CODEDEX by l00351127, 2017/11/14 */
+
+ while (!g_exit && (loop_count < send_count))
+ {
+ send_ret =
+ send_icmp_req (icmp_sock, my_pid, &g_stat_info, &remote_addr);
+ if (send_ret < 0)
+ {
+ break;
+ }
+
+ recv_icmp_reply (icmp_sock, my_pid, &g_input_info, &g_stat_info,
+ &remote_addr);
+
+ loop_count++;
+ }
+
+ close (icmp_sock);
+
+ if (0 != clock_gettime (CLOCK_MONOTONIC, &g_stat_info.end_time))
+ {
+ printf ("Failed to get time, errno = %d\n", errno);
+ }
+
+ print_stat (&g_stat_info, g_input_info.dst_ip);
+
+ return 0;
+}