You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/01/07 14:43:36 UTC

[incubator-nuttx-apps] branch master updated: netutils/iperf: Add iperf example.

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new 756dc32  netutils/iperf: Add iperf example.
756dc32 is described below

commit 756dc32fd093ca4285f3cd88c5561f41be651cf3
Author: chenwen <ch...@espressif.com>
AuthorDate: Fri Dec 4 16:16:12 2020 +0800

    netutils/iperf: Add iperf example.
    
    system/argtable3: Add an ANSI C library for command-line parsing.
---
 netutils/iperf/Kconfig      |  37 +++
 netutils/iperf/Make.defs    |  24 ++
 netutils/iperf/Makefile     |  35 +++
 netutils/iperf/README.md    |  87 ++++++
 netutils/iperf/iperf.c      | 722 ++++++++++++++++++++++++++++++++++++++++++++
 netutils/iperf/iperf.h      |  97 ++++++
 netutils/iperf/iperf_main.c | 230 ++++++++++++++
 system/argtable3/.gitignore |   2 +
 system/argtable3/Kconfig    |  18 ++
 system/argtable3/Make.defs  |  27 ++
 system/argtable3/Makefile   |  54 ++++
 11 files changed, 1333 insertions(+)

diff --git a/netutils/iperf/Kconfig b/netutils/iperf/Kconfig
new file mode 100644
index 0000000..0c4f008
--- /dev/null
+++ b/netutils/iperf/Kconfig
@@ -0,0 +1,37 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+		
+config EXAMPLES_IPERF
+	bool "iperf example"
+	default n
+	depends on NET
+	select NETUTILS_NETLIB
+	select LIBC_FLOATINGPOINT
+	select SYSTEM_ARGTABLE3
+	---help---
+		Enable the \"iperf example\"
+
+if EXAMPLES_IPERF
+		
+config EXAMPLES_IPERF_PROGNAME
+	string "Program name"
+	default "iperf"
+	---help---
+		This is the name of the program that will be used when the NSH ELF
+		program is installed.
+
+config EXAMPLES_IPERF_PRIORITY
+	int "iperf task priority"
+	default 100
+
+config EXAMPLES_IPERF_STACKSIZE
+	int "iperf stack size"
+	default DEFAULT_TASK_STACKSIZE
+
+config EXAMPLES_IPERFTEST_DEVNAME
+	string "Wi-Fi Network device"
+	default "wlan0"
+
+endif
diff --git a/netutils/iperf/Make.defs b/netutils/iperf/Make.defs
new file mode 100644
index 0000000..13effb7
--- /dev/null
+++ b/netutils/iperf/Make.defs
@@ -0,0 +1,24 @@
+############################################################################
+# apps/netutils/iperf/Make.defs
+# Adds selected applications to apps/ build
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you 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.
+#
+############################################################################
+
+ifneq ($(CONFIG_EXAMPLES_IPERF),)
+CONFIGURED_APPS += $(APPDIR)/netutils/iperf
+endif
diff --git a/netutils/iperf/Makefile b/netutils/iperf/Makefile
new file mode 100644
index 0000000..437675c
--- /dev/null
+++ b/netutils/iperf/Makefile
@@ -0,0 +1,35 @@
+############################################################################
+# apps/netutils/iperf/Makefile
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you 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 $(APPDIR)/Make.defs
+
+# iperf example! built-in application info
+
+PROGNAME  = $(CONFIG_EXAMPLES_IPERF_PROGNAME)
+PRIORITY  = $(CONFIG_EXAMPLES_IPERF_PRIORITY)
+STACKSIZE = $(CONFIG_EXAMPLES_IPERF_STACKSIZE)
+MODULE    = $(CONFIG_EXAMPLES_IPERF)
+
+# Iperf Example
+
+CSRCS += iperf.c
+MAINSRC = iperf_main.c
+
+include $(APPDIR)/Application.mk
diff --git a/netutils/iperf/README.md b/netutils/iperf/README.md
new file mode 100644
index 0000000..ea95f13
--- /dev/null
+++ b/netutils/iperf/README.md
@@ -0,0 +1,87 @@
+Configuring NuttX to use your Wireless Router (aka Access Point)
+================================================================
+
+    Since you are already in the root of NuttX/ repository, execute
+    make menuconfig to define your Wireless Router and your password:
+
+    $ make menuconfig
+
+    Browser the menus this way:
+
+    Application Configuration  --->
+        Network Utilities  --->
+            Networking Configuration  --->
+                WAPI Configuration  --->
+                    (myApSSID) SSID
+                    (mySSIDpassphrase) Passprhase
+
+    Replace the SSID from myApSSID with your wireless router name and
+    the Passprhase with your WiFi password.
+
+    Exit and save your configuration.
+
+iperf Test Example
+===================================
+
+To set up, do `make menuconfig` and select the Apps > netutils > iperf example. By default, NuttX will the be the client
+which sends data; and the host computer (Linux, macOS, or Windows) will be the server.
+
+Set up networking so the NuttX computer can ping the host, and the host can ping NuttX. Now you are ready to run the
+test.
+
+If you are using a wireless network card, you must first connect to the router:
+
+On host:
+
+    $ iperf -s -p 5471 -i 1 -w 416K
+    ------------------------------------------------------------
+    Server listening on TCP port 5471
+    TCP window size:  416 KByte
+    ------------------------------------------------------------
+
+On NuttX:
+
+    nsh> iperf -c 192.168.1.181 -p 5471 -i 1 -t 10
+    mode=tcp-client sip=192.168.1.198:5001, dip=192.168.1.181:5471, interval=1, time=10
+
+            Interval Bandwidth
+
+    0-   1 sec,  0.39 Mbits/sec
+    1-   2 sec,  0.26 Mbits/sec
+    2-   3 sec,  0.39 Mbits/sec
+    3-   4 sec,  0.26 Mbits/sec
+    4-   5 sec,  0.26 Mbits/sec
+    5-   6 sec,  0.26 Mbits/sec
+    6-   7 sec,  0.26 Mbits/sec
+    7-   8 sec,  0.26 Mbits/sec
+    8-   9 sec,  0.26 Mbits/sec
+    9-  10 sec,  0.26 Mbits/sec
+    0-  10 sec,  0.28 Mbits/sec
+
+Now on the host you should see something like:
+
+    $ iperf -s -p 5471 -i 1 -w 416K
+    ------------------------------------------------------------
+    Server listening on TCP port 5471
+    TCP window size:  416 KByte
+    ------------------------------------------------------------
+    [  5] local 192.168.1.181 port 5471 connected with 192.168.1.198 port 4210
+    [  5]  0.0- 1.0 sec  60.8 KBytes   498 Kbits/sec
+    [  5]  1.0- 2.0 sec  34.9 KBytes   286 Kbits/sec
+    [  5]  2.0- 3.0 sec  33.7 KBytes   276 Kbits/sec
+    [  5]  3.0- 4.0 sec  33.4 KBytes   274 Kbits/sec
+    [  5]  4.0- 5.0 sec  32.0 KBytes   262 Kbits/sec
+    [  5]  5.0- 6.0 sec  32.0 KBytes   262 Kbits/sec
+    [  5]  6.0- 7.0 sec  33.4 KBytes   274 Kbits/sec
+    [  5]  7.0- 8.0 sec  32.0 KBytes   262 Kbits/sec
+    [  5]  8.0- 9.0 sec  32.0 KBytes   262 Kbits/sec
+    [  5]  9.0-10.0 sec  33.4 KBytes   274 Kbits/sec
+    [  5]  0.0-10.3 sec   368 KBytes   292 Kbits/sec
+
+
+This will tell you the link speed in Kbits/sec – kilobits per second. If you want kilobytes, divide by 8.
+
+
+
+
+
diff --git a/netutils/iperf/iperf.c b/netutils/iperf/iperf.c
new file mode 100644
index 0000000..b24764d
--- /dev/null
+++ b/netutils/iperf/iperf.c
@@ -0,0 +1,722 @@
+/****************************************************************************
+ * apps/netutils/iperf/iperf.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include "iperf.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define IPERF_TRAFFIC_TASK_NAME      "iperf_traffic"
+#define IPERF_TRAFFIC_TASK_PRIORITY  100
+#define IPERF_TRAFFIC_TASK_STACK     4096
+#define IPERF_REPORT_TASK_NAME       "iperf_report"
+#define IPERF_REPORT_TASK_PRIORITY   100
+#define IPERF_REPORT_TASK_STACK      4096
+#define IPERF_REPORT_TASK_NAME       "iperf_report"
+
+#define IPERF_UDP_TX_LEN             (1472)
+#define IPERF_UDP_RX_LEN             (16 << 10)
+#define IPERF_TCP_TX_LEN             (16 << 10)
+#define IPERF_TCP_RX_LEN             (16 << 10)
+
+#define IPERF_MAX_DELAY              64
+#define IPERF_SOCKET_RX_TIMEOUT      10
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct iperf_ctrl_t
+{
+  struct iperf_cfg_t cfg;
+  bool finish;
+  uint32_t total_len;
+  uint32_t buffer_len;
+  uint8_t *buffer;
+  uint32_t sockfd;
+};
+
+struct iperf_udp_pkt_t
+{
+  int32_t id;
+  uint32_t sec;
+  uint32_t usec;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static bool s_iperf_is_running = false;
+static struct iperf_ctrl_t s_iperf_ctrl;
+
+/****************************************************************************
+ * Private Functions Prototypes
+ ****************************************************************************/
+
+inline static bool iperf_is_udp_client(void);
+inline static bool iperf_is_udp_server(void);
+inline static bool iperf_is_tcp_client(void);
+inline static bool iperf_is_tcp_server(void);
+static int iperf_get_socket_error_code(int sockfd);
+static int iperf_show_socket_error_reason(const char *str, int sockfd);
+static void iperf_report_task(void *arg);
+static int iperf_start_report(void);
+static int iperf_run_tcp_server(void);
+static int iperf_run_udp_server(void);
+static int iperf_run_udp_client(void);
+static int iperf_run_tcp_client(void);
+static void iperf_task_traffic(void *arg);
+static uint32_t iperf_get_buffer_len(void);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: iperf_is_udp_client
+ *
+ * Description:
+ *   Check if it is a udp client
+ *
+ ****************************************************************************/
+
+inline static bool iperf_is_udp_client(void)
+{
+  return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT)
+         && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
+}
+
+/****************************************************************************
+ * Name: iperf_is_udp_server
+ *
+ * Description:
+ *   Check if it is a udp server
+ *
+ ****************************************************************************/
+
+inline static bool iperf_is_udp_server(void)
+{
+  return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER)
+         && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
+}
+
+/****************************************************************************
+ * Name: iperf_is_tcp_client
+ *
+ * Description:
+ *   Check if it is a tcp client
+ *
+ ****************************************************************************/
+
+inline static bool iperf_is_tcp_client(void)
+{
+  return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT)
+         && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
+}
+
+/****************************************************************************
+ * Name: iperf_is_tcp_server
+ *
+ * Description:
+ *   Check if it is a tcp server
+ *
+ ****************************************************************************/
+
+inline static bool iperf_is_tcp_server(void)
+{
+  return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER)
+         && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
+}
+
+/****************************************************************************
+ * Name: iperf_get_socket_error_code
+ *
+ * Description:
+ *   Get reason of socket error.
+ *
+ ****************************************************************************/
+
+static int iperf_get_socket_error_code(int sockfd)
+{
+  return errno;
+}
+
+/****************************************************************************
+ * Name: iperf_show_socket_error_reason
+ *
+ * Description:
+ *   Show reason of socket error.
+ *
+ ****************************************************************************/
+
+static int iperf_show_socket_error_reason(const char *str, int sockfd)
+{
+  int err = errno;
+  if (err != 0)
+    {
+      printf("%s error, error code: %d, reason: %s",
+             str, err, strerror(err));
+    }
+
+  return err;
+}
+
+/****************************************************************************
+ * Name: iperf_report_task
+ *
+ * Description:
+ *   Start iperf report task
+ *
+ ****************************************************************************/
+
+static void iperf_report_task(void *arg)
+{
+  uint32_t interval = s_iperf_ctrl.cfg.interval;
+  uint32_t time = s_iperf_ctrl.cfg.time;
+  uint32_t last_len = 0;
+  uint32_t cur = 0;
+
+  printf("\n%16s %s\n", "Interval", "Bandwidth\n");
+  while (!s_iperf_ctrl.finish)
+    {
+      sleep(interval);
+      printf("%4d-%4d sec,  %.2f Mbits/sec\n", cur, cur + interval,
+        (double)((s_iperf_ctrl.total_len - last_len) * 8) / interval / 1e6);
+      cur += interval;
+      last_len = s_iperf_ctrl.total_len;
+      if (cur >= time)
+        {
+          break;
+        }
+    }
+
+  if (cur != 0)
+    {
+      printf("%4d-%4d sec,  %.2f Mbits/sec\n", 0, time,
+            (double)(s_iperf_ctrl.total_len * 8) / cur / 1e6);
+    }
+
+  s_iperf_ctrl.finish = true;
+
+  pthread_exit(NULL);
+}
+
+/****************************************************************************
+ * Name: iperf_start_report
+ *
+ * Description:
+ *   Start iperf report
+ *
+ ****************************************************************************/
+
+static int iperf_start_report(void)
+{
+  int ret;
+  pthread_t thread;
+  struct sched_param param;
+  pthread_attr_t attr;
+
+  pthread_attr_init(&attr);
+  param.sched_priority = IPERF_REPORT_TASK_PRIORITY;
+  pthread_attr_setschedparam(&attr, &param);
+  pthread_attr_setstacksize(&attr, IPERF_REPORT_TASK_STACK);
+
+  ret = pthread_create(&thread, &attr, (void *)iperf_report_task,
+                       IPERF_REPORT_TASK_NAME);
+  if (ret != 0)
+    {
+      printf("iperf_thread: pthread_create failed: %d, %s\n",
+             ret, IPERF_REPORT_TASK_NAME);
+      return -1;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: iperf_run_tcp_server
+ *
+ * Description:
+ *   Start tcp server
+ *
+ ****************************************************************************/
+
+static int iperf_run_tcp_server(void)
+{
+  socklen_t addr_len = sizeof(struct sockaddr);
+  struct sockaddr_in remote_addr;
+  struct sockaddr_in addr;
+  int actual_recv = 0;
+  int want_recv = 0;
+  uint8_t *buffer;
+  int listen_socket;
+  struct timeval t;
+  int sockfd;
+  int opt;
+
+  listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (listen_socket < 0)
+    {
+      iperf_show_socket_error_reason("tcp server create", listen_socket);
+      return -1;
+    }
+
+  setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
+  addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
+  if (bind(listen_socket, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+    {
+      iperf_show_socket_error_reason("tcp server bind", listen_socket);
+      close(listen_socket);
+      return -1;
+    }
+
+  if (listen(listen_socket, 5) < 0)
+    {
+      iperf_show_socket_error_reason("tcp server listen", listen_socket);
+      close(listen_socket);
+      return -1;
+    }
+
+  buffer = s_iperf_ctrl.buffer;
+  want_recv = s_iperf_ctrl.buffer_len;
+  while (!s_iperf_ctrl.finish)
+    {
+      /* TODO need to change to non-block mode */
+
+      sockfd = accept(listen_socket, (struct sockaddr *)&remote_addr,
+                      &addr_len);
+      if (sockfd < 0)
+        {
+          iperf_show_socket_error_reason("tcp server listen", listen_socket);
+          close(listen_socket);
+          return -1;
+        }
+      else
+        {
+          printf("accept: %s,%d\n", inet_ntoa(remote_addr.sin_addr),
+                 htons(remote_addr.sin_port));
+          iperf_start_report();
+
+          t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
+          setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
+        }
+
+      while (!s_iperf_ctrl.finish)
+        {
+          actual_recv = recv(sockfd, buffer, want_recv, 0);
+          if (actual_recv < 0)
+            {
+              iperf_show_socket_error_reason("tcp server recv",
+                                             listen_socket);
+              s_iperf_ctrl.finish = true;
+              break;
+            }
+          else
+            {
+              s_iperf_ctrl.total_len += actual_recv;
+            }
+        }
+
+      close(sockfd);
+    }
+
+  s_iperf_ctrl.finish = true;
+  close(listen_socket);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: iperf_run_udp_server
+ *
+ * Description:
+ *   Start udp server
+ *
+ ****************************************************************************/
+
+static int iperf_run_udp_server(void)
+{
+  socklen_t addr_len = sizeof(struct sockaddr_in);
+  struct sockaddr_in addr;
+  int actual_recv = 0;
+  struct timeval t;
+  int want_recv = 0;
+  uint8_t *buffer;
+  int sockfd;
+  int opt;
+  bool udp_recv_start = true;
+
+  sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (sockfd < 0)
+    {
+      iperf_show_socket_error_reason("udp server create", sockfd);
+      return -1;
+    }
+
+  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
+  addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
+  if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+    {
+      iperf_show_socket_error_reason("udp server bind", sockfd);
+      return -1;
+    }
+
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
+  addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
+
+  buffer = s_iperf_ctrl.buffer;
+  want_recv = s_iperf_ctrl.buffer_len;
+  printf("want recv=%d", want_recv);
+
+  t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
+  setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
+
+  while (!s_iperf_ctrl.finish)
+    {
+      actual_recv = recvfrom(sockfd, buffer, want_recv, 0,
+                             (struct sockaddr *)&addr, &addr_len);
+      if (actual_recv < 0)
+        {
+          iperf_show_socket_error_reason("udp server recv", sockfd);
+        }
+      else
+        {
+          if (udp_recv_start == true)
+            {
+              iperf_start_report();
+              udp_recv_start = false;
+            }
+
+          s_iperf_ctrl.total_len += actual_recv;
+        }
+    }
+
+  s_iperf_ctrl.finish = true;
+  close(sockfd);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: iperf_run_udp_client
+ *
+ * Description:
+ *   Start udp client
+ *
+ ****************************************************************************/
+
+static int iperf_run_udp_client(void)
+{
+  struct sockaddr_in addr;
+  struct iperf_udp_pkt_t *udp;
+  int actual_send = 0;
+  bool retry = false;
+  uint32_t delay = 1;
+  int want_send = 0;
+  uint8_t *buffer;
+  int sockfd;
+  int opt;
+  int err;
+  int id;
+
+  sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (sockfd < 0)
+    {
+      iperf_show_socket_error_reason("udp client create", sockfd);
+      return -1;
+    }
+
+  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
+  addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
+
+  iperf_start_report();
+  buffer = s_iperf_ctrl.buffer;
+  udp = (struct iperf_udp_pkt_t *)buffer;
+  want_send = s_iperf_ctrl.buffer_len;
+  id = 0;
+
+  while (!s_iperf_ctrl.finish)
+    {
+      if (false == retry)
+        {
+          id++;
+          udp->id = htonl(id);
+          delay = 1;
+        }
+
+      retry = false;
+      actual_send = sendto(sockfd, buffer, want_send, 0,
+                           (struct sockaddr *)&addr, sizeof(addr));
+
+      if (actual_send != want_send)
+        {
+          err = iperf_get_socket_error_code(sockfd);
+          if (err == ENOMEM)
+            {
+              usleep(delay * 10000);
+              if (delay < IPERF_MAX_DELAY)
+                {
+                  delay <<= 1;
+                }
+
+              retry = true;
+              continue;
+            }
+          else
+            {
+              printf("udp client send abort: err=%d", err);
+              break;
+            }
+        }
+      else
+        {
+          s_iperf_ctrl.total_len += actual_send;
+        }
+    }
+
+  s_iperf_ctrl.finish = true;
+  close(sockfd);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: iperf_run_tcp_client
+ *
+ * Description:
+ *   Start tcp client
+ *
+ ****************************************************************************/
+
+static int iperf_run_tcp_client(void)
+{
+  struct sockaddr_in remote_addr;
+  int actual_send = 0;
+  int want_send = 0;
+  uint8_t *buffer;
+  int sockfd;
+
+  sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (sockfd < 0)
+    {
+      iperf_show_socket_error_reason("tcp client create", sockfd);
+      return -1;
+    }
+
+  memset(&remote_addr, 0, sizeof(remote_addr));
+  remote_addr.sin_family = AF_INET;
+  remote_addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
+  remote_addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
+  if (connect(sockfd, (struct sockaddr *)&remote_addr,
+              sizeof(remote_addr)) < 0)
+    {
+      iperf_show_socket_error_reason("tcp client connect", sockfd);
+      return -1;
+    }
+
+  iperf_start_report();
+  buffer = s_iperf_ctrl.buffer;
+  want_send = s_iperf_ctrl.buffer_len;
+
+  while (!s_iperf_ctrl.finish)
+    {
+      actual_send = send(sockfd, buffer, want_send, 0);
+      if (actual_send <= 0)
+        {
+          iperf_show_socket_error_reason("tcp client send", sockfd);
+          break;
+        }
+      else
+        {
+          s_iperf_ctrl.total_len += actual_send;
+        }
+    }
+
+  s_iperf_ctrl.finish = true;
+  close(sockfd);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: iperf_task_traffic
+ *
+ * Description:
+ *   Select to run tcp or udp.
+ *
+ ****************************************************************************/
+
+static void iperf_task_traffic(void *arg)
+{
+  if (iperf_is_udp_client())
+    {
+      iperf_run_udp_client();
+    }
+  else if (iperf_is_udp_server())
+    {
+      iperf_run_udp_server();
+    }
+  else if (iperf_is_tcp_client())
+    {
+      iperf_run_tcp_client();
+    }
+  else
+    {
+      iperf_run_tcp_server();
+    }
+
+  if (s_iperf_ctrl.buffer)
+    {
+      free(s_iperf_ctrl.buffer);
+      s_iperf_ctrl.buffer = NULL;
+    }
+
+  printf("iperf exit");
+  s_iperf_is_running = false;
+
+  pthread_exit(NULL);
+}
+
+static uint32_t iperf_get_buffer_len(void)
+{
+  if (iperf_is_udp_client())
+    {
+      return IPERF_UDP_TX_LEN;
+    }
+  else if (iperf_is_udp_server())
+    {
+      return IPERF_UDP_RX_LEN;
+    }
+  else if (iperf_is_tcp_client())
+    {
+      return IPERF_TCP_TX_LEN;
+    }
+  else
+    {
+      return IPERF_TCP_RX_LEN;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: iperf_start
+ *
+ * Description:
+ *   Start iperf task.
+ *
+ ****************************************************************************/
+
+int iperf_start(struct iperf_cfg_t *cfg)
+{
+  int ret;
+  struct sched_param param;
+  pthread_t thread;
+  pthread_attr_t attr;
+
+  if (!cfg)
+    {
+      return -1;
+    }
+
+  if (s_iperf_is_running)
+    {
+      printf("iperf is running\n");
+      return -1;
+    }
+
+  memset(&s_iperf_ctrl, 0, sizeof(s_iperf_ctrl));
+  memcpy(&s_iperf_ctrl.cfg, cfg, sizeof(*cfg));
+  s_iperf_is_running = true;
+  s_iperf_ctrl.finish = false;
+  s_iperf_ctrl.buffer_len = iperf_get_buffer_len();
+  s_iperf_ctrl.buffer = (uint8_t *)malloc(s_iperf_ctrl.buffer_len);
+  if (!s_iperf_ctrl.buffer)
+    {
+      printf("create buffer: not enough memory\n");
+      return -1;
+    }
+
+  memset(s_iperf_ctrl.buffer, 0, s_iperf_ctrl.buffer_len);
+  pthread_attr_init(&attr);
+  param.sched_priority = IPERF_TRAFFIC_TASK_PRIORITY;
+  pthread_attr_setschedparam(&attr, &param);
+  pthread_attr_setstacksize(&attr, IPERF_TRAFFIC_TASK_STACK);
+  ret = pthread_create(&thread, &attr, (void *)iperf_task_traffic,
+                       IPERF_TRAFFIC_TASK_NAME);
+
+  if (ret != 0)
+    {
+      printf("iperf_task_traffic: create task failed: %d\n", ret);
+      free(s_iperf_ctrl.buffer);
+      s_iperf_ctrl.buffer = NULL;
+      return -1;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: iperf_stop
+ *
+ * Description:
+ *   Stop iperf task.
+ *
+ ****************************************************************************/
+
+int iperf_stop(void)
+{
+  if (s_iperf_is_running)
+    {
+      s_iperf_ctrl.finish = true;
+    }
+
+  while (s_iperf_is_running)
+    {
+      printf("wait current iperf to stop ...\n");
+      usleep(300 * 1000);
+    }
+
+  return 0;
+}
diff --git a/netutils/iperf/iperf.h b/netutils/iperf/iperf.h
new file mode 100644
index 0000000..7a9dbfd
--- /dev/null
+++ b/netutils/iperf/iperf.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+ * apps/netutils/iperf/iperf.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+#ifndef __IPERF_H_
+#define __IPERF_H_
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define IPERF_FLAG_CLIENT (1)
+#define IPERF_FLAG_SERVER (1 << 1)
+#define IPERF_FLAG_TCP (1 << 2)
+#define IPERF_FLAG_UDP (1 << 3)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct iperf_cfg_t
+{
+  uint32_t flag;
+  uint32_t dip;
+  uint32_t sip;
+  uint16_t dport;
+  uint16_t sport;
+  uint32_t interval;
+  uint32_t time;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: iperf_start
+ *
+ * Description:
+ *   Start iperf task.
+ *
+ ****************************************************************************/
+
+int iperf_start(struct iperf_cfg_t *cfg);
+
+/****************************************************************************
+ * Name: iperf_stop
+ *
+ * Description:
+ *   Stop iperf task.
+ *
+ ****************************************************************************/
+
+int iperf_stop(void);
+
+#ifdef __cplusplus
+}
+#endif
+#undef EXTERN
+
+#endif /* __ASSEMBLY__ */
+#endif
diff --git a/netutils/iperf/iperf_main.c b/netutils/iperf/iperf_main.c
new file mode 100644
index 0000000..7e29314
--- /dev/null
+++ b/netutils/iperf/iperf_main.c
@@ -0,0 +1,230 @@
+/****************************************************************************
+ * apps/netutils/iperf/iperf_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include "netutils/netlib.h"
+#include "argtable3.h"
+#include "iperf.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_EXAMPLES_IPERFTEST_DEVNAME
+#  define DEVNAME CONFIG_EXAMPLES_IPERFTEST_DEVNAME
+#else
+#  define DEVNAME "wlan0"
+#endif
+
+#define IPERF_DEFAULT_PORT     5001
+#define IPERF_DEFAULT_INTERVAL 3
+#define IPERF_DEFAULT_TIME     30
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct wifi_iperf_t
+{
+  struct arg_str *ip;
+  struct arg_lit *server;
+  struct arg_lit *udp;
+  struct arg_int *port;
+  struct arg_int *interval;
+  struct arg_int *time;
+  struct arg_lit *abort;
+  struct arg_end *end;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: iperf_showusage
+ *
+ * Description:
+ *   Show usage of the demo program and exit
+ *
+ ****************************************************************************/
+
+static void iperf_showusage(FAR const char *progname, int exitcode)
+{
+  printf("USAGE: %s [-sua] [-c <ip>] [-p <port>]\
+         [-i <interval>] [-t <time>]\n", progname);
+  printf("iperf command:\n");
+  printf("  -c, --client=<ip>  run in client mode,\
+         connecting to <host>\n");
+  printf("  -s, --server  run in server mode\n");
+  printf("  -u, --udp  use UDP rather than TCP\n");
+  printf("  -p, --port=<port>  server port to listen on/connect to\n");
+  printf("  -i, --interval=<interval>\
+         seconds between periodic bandwidth reports\n");
+  printf("  -t, --time=<time>\
+         time in seconds to transmit for (default 10 secs)\n");
+  printf("  -a, --abort  abort running iperf\n");
+  printf("\n");
+  exit(exitcode);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+  struct wifi_iperf_t iperf_args;
+  struct iperf_cfg_t cfg;
+  struct in_addr addr;
+  int nerrors;
+
+  bzero(&addr, sizeof(struct in_addr));
+  bzero(&cfg, sizeof(cfg));
+
+  iperf_args.ip = arg_str0("c", "client", "<ip>",
+                           "run in client mode, connecting to <host>");
+  iperf_args.server = arg_lit0("s", "server", "run in server mode");
+  iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
+  iperf_args.port = arg_int0("p", "port", "<port>",
+                             "server port to listen on/connect to");
+  iperf_args.interval = arg_int0("i", "interval", "<interval>",
+                            "seconds between periodic bandwidth reports");
+  iperf_args.time = arg_int0("t", "time", "<time>",
+                        "time in seconds to transmit for (default 10 secs)");
+  iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
+  iperf_args.end = arg_end(1);
+
+  nerrors = arg_parse(argc, argv, (void**) &iperf_args);
+  if (nerrors != 0)
+    {
+      arg_print_errors(stderr, iperf_args.end, argv[0]);
+      iperf_showusage(argv[0], 0);
+    }
+
+  if (iperf_args.abort->count != 0)
+    {
+      iperf_stop();
+      printf("ERROR: abort->count: %d\n", iperf_args.abort->count);
+      iperf_showusage(argv[0], 0);
+    }
+
+  if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
+         ((iperf_args.ip->count != 0) && (iperf_args.server->count != 0)))
+    {
+      printf("ERROR: should specific client/server mode\n");
+      iperf_showusage(argv[0], 0);
+    }
+
+  if (iperf_args.ip->count == 0)
+    {
+      cfg.flag |= IPERF_FLAG_SERVER;
+    }
+  else
+    {
+      cfg.dip = inet_addr(iperf_args.ip->sval[0]);
+      cfg.flag |= IPERF_FLAG_CLIENT;
+    }
+
+  addr.s_addr = 0;
+  netlib_get_ipv4addr(DEVNAME, &addr);
+  if (addr.s_addr == 0)
+    {
+      printf("ERROR: access IP is 0x00 \n");
+      return -1;
+    }
+
+  printf("       IP: %s\n", inet_ntoa(addr));
+
+  cfg.sip = addr.s_addr;
+
+  if (iperf_args.udp->count == 0)
+    {
+      cfg.flag |= IPERF_FLAG_TCP;
+    }
+  else
+    {
+      cfg.flag |= IPERF_FLAG_UDP;
+    }
+
+  if (iperf_args.port->count == 0)
+    {
+      cfg.sport = IPERF_DEFAULT_PORT;
+      cfg.dport = IPERF_DEFAULT_PORT;
+    }
+  else
+    {
+      if (cfg.flag & IPERF_FLAG_SERVER)
+        {
+          cfg.sport = iperf_args.port->ival[0];
+          cfg.dport = IPERF_DEFAULT_PORT;
+        }
+      else
+        {
+          cfg.sport = IPERF_DEFAULT_PORT;
+          cfg.dport = iperf_args.port->ival[0];
+        }
+    }
+
+  if (iperf_args.interval->count == 0)
+    {
+      cfg.interval = IPERF_DEFAULT_INTERVAL;
+    }
+  else
+    {
+      cfg.interval = iperf_args.interval->ival[0];
+      if (cfg.interval <= 0)
+        {
+          cfg.interval = IPERF_DEFAULT_INTERVAL;
+        }
+    }
+
+  if (iperf_args.time->count == 0)
+    {
+      cfg.time = IPERF_DEFAULT_TIME;
+    }
+  else
+    {
+      cfg.time = iperf_args.time->ival[0];
+      if (cfg.time <= cfg.interval)
+        {
+          cfg.time = cfg.interval;
+        }
+    }
+
+  printf("\n mode=%s-%s sip=%d.%d.%d.%d:%d,\
+         dip=%d.%d.%d.%d:%d, interval=%d, time=%d\n",
+         cfg.flag & IPERF_FLAG_TCP ?"tcp":"udp",
+         cfg.flag & IPERF_FLAG_SERVER ?"server":"client",
+         cfg.sip & 0xff, (cfg.sip >> 8) & 0xff, (cfg.sip >> 16) & 0xff,
+         (cfg.sip >> 24) & 0xff, cfg.sport,
+         cfg.dip & 0xff, (cfg.dip >> 8) & 0xff, (cfg.dip >> 16) & 0xff,
+         (cfg.dip >> 24) & 0xff, cfg.dport,
+         cfg.interval, cfg.time);
+  iperf_start(&cfg);
+
+  return 0;
+}
diff --git a/system/argtable3/.gitignore b/system/argtable3/.gitignore
new file mode 100644
index 0000000..71fd725
--- /dev/null
+++ b/system/argtable3/.gitignore
@@ -0,0 +1,2 @@
+/argtable3*
+/v*.tar.gz
diff --git a/system/argtable3/Kconfig b/system/argtable3/Kconfig
new file mode 100644
index 0000000..acaa3ca
--- /dev/null
+++ b/system/argtable3/Kconfig
@@ -0,0 +1,18 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config SYSTEM_ARGTABLE3
+	bool "ARGTABLE3 library"
+	default n
+	---help---
+		Enables the ARGTABLE3 library.
+
+if SYSTEM_ARGTABLE3
+
+config SYSTEM_ARGTABLE3_VERSION
+	string "ARGTABLE3 Version"
+	default "3.0.0"
+
+endif
diff --git a/system/argtable3/Make.defs b/system/argtable3/Make.defs
new file mode 100644
index 0000000..5c7a759
--- /dev/null
+++ b/system/argtable3/Make.defs
@@ -0,0 +1,27 @@
+############################################################################
+# apps/system/argtable3/Make.defs
+# Adds selected applications to apps/ build
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you 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.
+#
+############################################################################
+
+ifneq ($(CONFIG_SYSTEM_ARGTABLE3),)
+CONFIGURED_APPS += $(APPDIR)/system/argtable3
+
+CFLAGS   += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" "$(APPDIR)/system/argtable3/argtable3/"}
+CXXFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" "$(APPDIR)/system/argtable3/argtable3/"}
+endif
diff --git a/system/argtable3/Makefile b/system/argtable3/Makefile
new file mode 100644
index 0000000..c696ed6
--- /dev/null
+++ b/system/argtable3/Makefile
@@ -0,0 +1,54 @@
+############################################################################
+# apps/system/argtable3/Makefile
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you 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 $(APPDIR)/Make.defs
+
+ARGTABLE3_VERSION := $(patsubst "%",%,$(CONFIG_SYSTEM_ARGTABLE3_VERSION))
+ARGTABLE3_TARBALL  = v$(ARGTABLE3_VERSION).tar.gz
+ARGTABLE3_UNPACK   = argtable3
+ARGTABLE3_SRCDIR   = $(ARGTABLE3_UNPACK)
+
+DEPPATH += --dep-path $(ARGTABLE3_SRCDIR)
+VPATH   += :$(ARGTABLE3_SRCDIR)
+
+CSRCS := $(notdir $(wildcard $(ARGTABLE3_SRCDIR)$(DELIM)argtable3.c))
+
+$(ARGTABLE3_TARBALL):
+	$(Q) echo "Downloading argtable3-$(ARGTABLE3_VERSION)"
+	$(Q) echo "$(ARGTABLE3_SRCDIR)"
+	$(Q) echo "$(ARGTABLE3_TARBALL)--$(ARGTABLE3_UNPACK)"
+	$(Q) wget https://github.com/argtable/argtable3/archive/$(ARGTABLE3_TARBALL)
+
+$(ARGTABLE3_UNPACK): $(ARGTABLE3_TARBALL)
+	$(Q) tar zxf $(ARGTABLE3_TARBALL)
+	$(Q) mv argtable3-$(ARGTABLE3_VERSION) $(ARGTABLE3_UNPACK)
+	$(Q) echo "Patching $(ARGTABLE3_UNPACK)"
+	$(Q) patch -p0 < argtable3.patch
+
+$(ARGTABLE3_UNPACK)/.patch: $(ARGTABLE3_UNPACK)
+	$(Q) touch $(ARGTABLE3_UNPACK)/.patch
+
+context:: $(ARGTABLE3_UNPACK)/.patch
+
+distclean::
+	$(call DELFILE, $(ARGTABLE3_TARBALL))
+	$(call DELDIR, $(ARGTABLE3_UNPACK))
+
+include $(APPDIR)/Application.mk