Răsfoiți Sursa

Add ping, tftp, iperf, netio, ntp, telnet utilities.

armink 8 ani în urmă
comite
58226cece6

+ 340 - 0
LICENSE

@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

+ 26 - 0
README.md

@@ -0,0 +1,26 @@
+# RT-Thread 网络小工具集
+
+---
+
+## 1、介绍
+
+当 RT-Thread 接入网络后,可玩性大大增强。这里汇集了 RT-Thread 可用的全部网络小工具集合,你所需要的小工具都可以在这里找到。
+
+## 2、获取方式
+
+请使用 ENV 工具辅助下载:
+
+包的路径为:`RT-Thread online package` -> `IoT - internet of things` -> `netutils`
+
+## 3、使用说明
+
+每个小工具都提供了 Finsh/MSH 的使用命令,在其目录下都存有一份详细的使用文档。如需使用,请单独查看。下面是目前支持的小工具汇总:
+
+|名称|分类|功能简介|使用文档|
+|:--|:--:|:--|:--|
+|[Ping](https://baike.baidu.com/item/ping/6235)|调试测试|利用“ping”命令可以检查网络是否连通,可以很好地帮助我们分析和判定网络故障|[点击查看](ping/README.md)|
+|[TFTP](https://baike.baidu.com/item/TFTP)|文件传输|TFTP是一个传输文件的简单协议,比 FTP 还要轻量级|[点击查看](tftp/README.md)|
+|[iperf](https://baike.baidu.com/item/iperf)|性能测试|测试最大 TCP 和 UDP 带宽性能,可以报告带宽、延迟抖动和数据包丢失|[点击查看](iperf/README.md)|
+|[NetIO](http://www.nwlab.net/art/netio/netio.html)|性能测试|测试网络的吞吐量的工具|[点击查看](netio/README.md)|
+|[NTP](https://baike.baidu.com/item/NTP)|时间同步|网络时间协议|[点击查看](ntp/README.md)|
+|[Telnet](https://baike.baidu.com/item/Telnet)|远程访问|可以远程登录到 RT-Thread 的 Finsh/MSH Shell|[点击查看](telnet/README.md)|

+ 12 - 0
SConscript

@@ -0,0 +1,12 @@
+import os
+from building import *
+
+objs = []
+cwd  = GetCurrentDir()
+list = os.listdir(cwd)
+
+for item in list:
+    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
+        objs = objs + SConscript(os.path.join(item, 'SConscript'))
+
+Return('objs')

+ 1 - 0
iperf/README.md

@@ -0,0 +1 @@
+Coming soon...

+ 10 - 0
iperf/SConscript

@@ -0,0 +1,10 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+group = DefineGroup('NetUtils', src, depend = ['PKG_NETUTILS_IPERF'], CPPPATH = CPPPATH)
+
+Return('group')

+ 322 - 0
iperf/iperf.c

@@ -0,0 +1,322 @@
+/**
+* iperf-liked network performance tool
+*
+*/
+
+#include <rtdef.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include "netdb.h"
+
+#define IPERF_PORT          5001
+#define IPERF_BUFSZ         (4 * 1024)
+
+#define IPERF_MODE_STOP     0
+#define IPERF_MODE_SERVER   1
+#define IPERF_MODE_CLIENT   2
+
+typedef struct 
+{
+    int mode;
+
+    char *host;
+    int port;
+}IPERF_PARAM;
+static IPERF_PARAM param = {IPERF_MODE_STOP, NULL, IPERF_PORT};
+
+static void iperf_client(void* thread_param)
+{
+    int i;
+    int sock;
+    int ret;
+
+    uint8_t *send_buf;
+    int sentlen;
+    rt_tick_t tick1, tick2;
+    struct sockaddr_in addr;
+
+    send_buf = (uint8_t *) malloc (IPERF_BUFSZ);
+    if (!send_buf) return ;
+
+    for (i = 0; i < IPERF_BUFSZ; i ++)
+        send_buf[i] = i & 0xff;
+
+    while (param.mode != IPERF_MODE_STOP) 
+    {
+        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+        if (sock < 0) 
+        {
+            printf("create socket failed!\n");
+            rt_thread_delay(RT_TICK_PER_SECOND);
+            continue;
+        }
+
+        addr.sin_family = PF_INET;
+        addr.sin_port = htons(param.port);
+        addr.sin_addr.s_addr = inet_addr((char*)param.host);
+
+        ret = connect(sock, (const struct sockaddr*)&addr, sizeof(addr));
+        if (ret == -1) 
+        {
+            printf("Connect failed!\n");
+            closesocket(sock);
+
+            rt_thread_delay(RT_TICK_PER_SECOND);
+            continue;
+        }
+
+        printf("Connect to iperf server successful!\n");
+
+        {
+            int flag = 1;
+
+            setsockopt(sock,
+                IPPROTO_TCP,     /* set option at TCP level */
+                TCP_NODELAY,     /* name of option */
+                (void *) &flag,  /* the cast is historical cruft */
+                sizeof(int));    /* length of option value */
+        }
+
+        sentlen = 0;
+
+        tick1 = rt_tick_get();
+        while(param.mode != IPERF_MODE_STOP) 
+        {
+            tick2 = rt_tick_get();
+            if (tick2 - tick1 >= RT_TICK_PER_SECOND * 5)
+            {
+                float f;
+
+                f = sentlen*RT_TICK_PER_SECOND/125/(tick2-tick1);
+                f /= 1000;
+                printf("%2.2f Mbps!\n", f);
+                tick1 = tick2;
+                sentlen = 0;
+            }
+
+            ret = send(sock, send_buf, IPERF_BUFSZ, 0);
+            if (ret > 0) 
+            {
+                sentlen += ret;
+            }
+
+            if (ret < 0) break;
+        }
+
+        closesocket(sock);
+
+        rt_thread_delay(RT_TICK_PER_SECOND*2);
+        printf("disconnected!\n");
+    }
+}
+
+void iperf_server(void* thread_param)
+{
+    uint8_t *recv_data;
+    rt_uint32_t sin_size;
+    rt_tick_t tick1, tick2;
+    int sock = -1, connected, bytes_received, recvlen;
+    struct sockaddr_in server_addr, client_addr;
+
+    recv_data = (uint8_t *)malloc(IPERF_BUFSZ);
+    if (recv_data == RT_NULL)
+    {
+        printf("No memory\n");
+        goto __exit;
+    }
+
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+    if (sock < 0)
+    {
+        printf("Socket error\n");
+        goto __exit;
+    }
+
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_port = htons(param.port);
+    server_addr.sin_addr.s_addr = INADDR_ANY;
+    memset(&(server_addr.sin_zero), 0x0, sizeof(server_addr.sin_zero));
+
+    if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
+    {
+        printf("Unable to bind\n");
+        goto __exit;
+    }
+
+    if (listen(sock, 5) == -1)
+    {
+        printf("Listen error\n");
+        goto __exit;
+    }
+
+    while(param.mode != IPERF_MODE_STOP)
+    {
+        sin_size = sizeof(struct sockaddr_in);
+
+        connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
+
+        printf("new client connected from (%s, %d)\n",
+                  inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
+
+        {
+            int flag = 1;
+
+            setsockopt(connected,
+                IPPROTO_TCP,     /* set option at TCP level */
+                TCP_NODELAY,     /* name of option */
+                (void *) &flag,  /* the cast is historical cruft */
+                sizeof(int));    /* length of option value */
+        }
+
+        recvlen = 0;
+        tick1 = rt_tick_get();
+        while (param.mode != IPERF_MODE_STOP)
+        {
+            bytes_received = recv(connected, recv_data, IPERF_BUFSZ, 0);
+            if (bytes_received <= 0) break;
+
+            recvlen += bytes_received;
+
+            tick2 = rt_tick_get();
+            if (tick2 - tick1 >= RT_TICK_PER_SECOND * 5)
+            {
+                float f;
+
+                f = recvlen*RT_TICK_PER_SECOND/125/(tick2-tick1);
+                f /= 1000;
+                printf("%2.2f Mbps!\n", f);
+                tick1 = tick2;
+                recvlen = 0;
+            }
+        }
+
+        if (connected >= 0) closesocket(connected);
+        connected = -1;
+    }
+
+__exit:
+    if (sock >= 0) closesocket(sock);
+    if (recv_data) free(recv_data);
+}
+
+void iperf_usage(void)
+{
+    printf("Usage: iperf [-s|-c host] [options]\n");
+    printf("       iperf [-h|--stop]\n");
+    printf("\n");
+    printf("Client/Server:\n");
+    printf("  -p #         server port to listen on/connect to\n");
+    printf("\n");
+    printf("Server specific:\n");
+    printf("  -s           run in server mode\n");
+    printf("\n");
+    printf("Client specific:\n");
+    printf("  -c <host>    run in client mode, connecting to <host>\n");
+    printf("\n");
+    printf("Miscellaneous:\n");
+    printf("  -h           print this message and quit\n");
+    printf("  --stop       stop iperf program\n");
+
+    return ;
+}
+
+int iperf(int argc, char** argv)
+{
+    int mode = 0; /* server mode */
+    char *host = NULL;
+    int port = IPERF_PORT;
+
+    if (argc == 1) goto __usage;
+    else 
+    {
+        if (strcmp(argv[1], "-h") ==0) goto __usage;
+        else if (strcmp(argv[1], "--stop") ==0)
+        {
+            /* stop iperf */
+            param.mode = IPERF_MODE_STOP;
+            return 0;
+        }
+        else if (strcmp(argv[1], "-s") ==0)
+        {
+            mode = IPERF_MODE_SERVER; /* server mode */
+
+            /* iperf -s -p 5000 */
+            if (argc == 4)
+            {
+                if (strcmp(argv[2], "-p") == 0)
+                {
+                    port = atoi(argv[3]);
+                }
+                else goto __usage;
+            }
+        }
+        else if (strcmp(argv[1], "-c") ==0)
+        {
+            mode = IPERF_MODE_CLIENT; /* client mode */
+            if (argc < 3) goto __usage;
+
+            host = argv[2];
+            if (argc == 5)
+            {
+                /* iperf -c host -p port */
+                if (strcmp(argv[3], "-p") == 0)
+                {
+                    port = atoi(argv[4]);
+                }
+                else goto __usage;
+            }
+        }
+        else if (strcmp(argv[1], "-h") ==0)
+        {
+            goto __usage;
+        }
+        else goto __usage;
+    }
+
+    /* start iperf */
+    if (param.mode == IPERF_MODE_STOP)
+    {
+        rt_thread_t tid = RT_NULL;
+
+        param.mode = mode;
+        param.port = port;
+        if (param.host)
+        {
+            rt_free(param.host);
+            param.host = NULL;
+        }
+        if (host) param.host = rt_strdup(host);
+
+        if (mode == IPERF_MODE_CLIENT)
+            tid = rt_thread_create("iperfc", iperf_client, RT_NULL, 
+                2048, 20, 20);
+        else if (mode == IPERF_MODE_SERVER)
+            tid = rt_thread_create("iperfd", iperf_server, RT_NULL, 
+                2048, 20, 20);
+
+        if (tid) rt_thread_startup(tid);
+    }
+    else
+    {
+        printf("Please stop iperf firstly, by:\n");
+        printf("iperf --stop\n");
+    }
+
+    return 0;
+
+__usage:
+    iperf_usage();
+    return 0;
+}
+
+#ifdef RT_USING_FINSH
+#include <finsh.h>
+MSH_CMD_EXPORT(iperf, - the network bandwidth measurement tool);
+#endif

+ 1 - 0
netio/README.md

@@ -0,0 +1 @@
+Coming soon...

+ 10 - 0
netio/SConscript

@@ -0,0 +1,10 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+group = DefineGroup('NetUtils', src, depend = ['PKG_NETUTILS_NETIO'], CPPPATH = CPPPATH)
+
+Return('group')

+ 437 - 0
netio/netio.c

@@ -0,0 +1,437 @@
+/**
+ * @file
+ * NetIO Server
+ *
+ */
+
+/*
+ * 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. 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#include "lwip/opt.h"
+
+#if LWIP_TCP
+#include "lwip/tcp.h"
+
+/*
+ * This implements a netio server.
+ *  The client sends a command word (4 bytes) then a data length word (4 bytes).
+ *  If the command is "receive", the server is to consume "data length" bytes into
+ *   a circular buffer until the first byte is non-zero, then it is to consume
+ *   another command/data pair.
+ *  If the command is "send", the server is to send "data length" bytes from a circular
+ *   buffer with the first byte being zero, until "some time" (6 seconds in the
+ *   current netio126.zip download) has passed and then send one final buffer with
+ *   the first byte being non-zero. Then it is to consume another command/data pair.
+ */
+
+/* See http://www.nwlab.net/art/netio/netio.html to get the netio tool */
+
+/* implementation options */
+#define NETIO_BUF_SIZE              (4 * 1024)
+#define NETIO_USE_STATIC_BUF        0
+
+/* NetIO server state definition */
+#define NETIO_STATE_WAIT_FOR_CMD    0
+#define NETIO_STATE_RECV_DATA       1
+#define NETIO_STATE_SEND_DATA       2
+#define NETIO_STATE_SEND_DATA_LAST  3
+#define NETIO_STATE_DONE            4
+
+struct netio_state
+{
+    u32_t  state;
+    u32_t  cmd;
+    u32_t  data_len;
+    u32_t  cntr;
+    u8_t *buf_ptr;
+    u32_t  buf_pos;
+    u32_t  first_byte;
+    u32_t  time_stamp;
+};
+
+/* NetIO command protocol definition */
+#define NETIO_CMD_QUIT              0
+#define NETIO_CMD_C2S               1
+#define NETIO_CMD_S2C               2
+#define NETIO_CMD_RES               3
+
+static err_t netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
+
+static void
+netio_close(void *arg, struct tcp_pcb *pcb)
+{
+    err_t err;
+
+    struct netio_state *ns = arg;
+    ns->state = NETIO_STATE_DONE;
+    tcp_recv(pcb, NULL);
+    err = tcp_close(pcb);
+
+    if (err != ERR_OK)
+    {
+        /* closing failed, try again later */
+        tcp_recv(pcb, netio_recv);
+    }
+    else
+    {
+        /* closing succeeded */
+#if NETIO_USE_STATIC_BUF != 1
+        if (ns->buf_ptr != NULL)
+        {
+            mem_free(ns->buf_ptr);
+        }
+#endif
+        tcp_arg(pcb, NULL);
+        tcp_poll(pcb, NULL, 0);
+        tcp_sent(pcb, NULL);
+        if (arg != NULL)
+        {
+            mem_free(arg);
+        }
+    }
+}
+
+static err_t
+netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+    struct netio_state *ns = arg;
+    u8_t *data_ptr;
+    u32_t data_cntr;
+    struct pbuf *q = p;
+    u16_t len;
+
+    if (p != NULL)
+    {
+        tcp_recved(pcb, p->tot_len);
+    }
+
+    if (err == ERR_OK && q != NULL)
+    {
+
+        while (q != NULL)
+        {
+            data_cntr = q->len;
+            data_ptr = q->payload;
+            while (data_cntr--)
+            {
+                if (ns->state == NETIO_STATE_DONE)
+                {
+                    netio_close(ns, pcb);
+                    break;
+                }
+                else if (ns->state == NETIO_STATE_WAIT_FOR_CMD)
+                {
+                    if (ns->cntr < 4)
+                    {
+                        /* build up the CMD field */
+                        ns->cmd <<= 8;
+                        ns->cmd |= *data_ptr++;
+                        ns->cntr++;
+                    }
+                    else if (ns->cntr < 8)
+                    {
+                        /* build up the DATA field */
+                        ns->data_len <<= 8;
+                        ns->data_len |= *data_ptr++;
+                        ns->cntr++;
+
+                        if (ns->cntr == 8)
+                        {
+                            /* now we have full command and data words */
+                            ns->cntr = 0;
+                            ns->buf_pos = 0;
+                            ns->buf_ptr[0] = 0;
+                            if (ns->cmd == NETIO_CMD_C2S)
+                            {
+                                ns->state = NETIO_STATE_RECV_DATA;
+                            }
+                            else if (ns->cmd == NETIO_CMD_S2C)
+                            {
+                                ns->state = NETIO_STATE_SEND_DATA;
+                                /* start timer */
+                                ns->time_stamp = rt_tick_get();
+                                /* send first round of data */
+
+                                len = tcp_sndbuf(pcb);
+                                len = LWIP_MIN(len, ns->data_len - ns->cntr);
+                                len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos);
+
+                                do
+                                {
+                                    err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY);
+                                    if (err == ERR_MEM)
+                                    {
+                                        len /= 2;
+                                    }
+                                }
+                                while ((err == ERR_MEM) && (len > 1));
+
+                                ns->buf_pos += len;
+                                ns->cntr    += len;
+
+                            }
+                            else
+                            {
+                                /* unrecognized command, punt */
+                                ns->cntr = 0;
+                                ns->buf_pos = 0;
+                                ns->buf_ptr[0] = 0;
+                                netio_close(ns, pcb);
+                                break;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        /* in trouble... shouldn't be in this state! */
+                    }
+
+                }
+                else if (ns->state == NETIO_STATE_RECV_DATA)
+                {
+                    int chunk_length;
+
+                    if (ns->cntr == 0)
+                    {
+                        /* save the first byte of this new round of data
+                         * this will not match ns->buf_ptr[0] in the case that
+                         *   NETIO_BUF_SIZE is less than ns->data_len.
+                         */
+                        ns->first_byte = *data_ptr;
+                    }
+
+                    if (ns->cntr + (data_cntr + 1) < ns->data_len) chunk_length = data_cntr + 1;
+                    else chunk_length = (ns->data_len - ns->cntr);
+
+                    ns->buf_pos += chunk_length;
+                    data_ptr += chunk_length;
+                    ns->cntr += chunk_length;
+                    data_cntr -= (chunk_length - 1);
+
+                    if (ns->buf_pos >= NETIO_BUF_SIZE)
+                    {
+                        /* circularize the buffer */
+                        ns->buf_pos %= NETIO_BUF_SIZE;
+                    }
+
+                    if (ns->cntr == ns->data_len)
+                    {
+                        ns->cntr = 0;
+                        if (ns->first_byte != 0)
+                        {
+                            /* if this last round did not start with 0,
+                             *  go look for another command */
+                            ns->state = NETIO_STATE_WAIT_FOR_CMD;
+                            ns->data_len = 0;
+                            ns->cmd = 0;
+                            /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */
+                        }
+                        else
+                        {
+                            /* stay here and wait on more data */
+                        }
+                    }
+
+                }
+                else if (ns->state == NETIO_STATE_SEND_DATA
+                         || ns->state == NETIO_STATE_SEND_DATA_LAST)
+                {
+                    /* I don't think this should happen... */
+                }
+                else
+                {
+                    /* done / quit */
+                    netio_close(ns, pcb);
+                    break;
+                } /* end of ns->state condition */
+            } /* end of while data still in this pbuf */
+
+            q = q->next;
+        }
+
+        pbuf_free(p);
+
+    }
+    else
+    {
+
+        /* error or closed by other side */
+        if (p != NULL)
+        {
+            pbuf_free(p);
+        }
+
+        /* close the connection */
+        netio_close(ns, pcb);
+
+    }
+    return ERR_OK;
+
+}
+
+static err_t
+netio_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+    struct netio_state *ns = arg;
+    err_t err = ERR_OK;
+
+    if (ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA)
+    {
+        /* done with this round of sending */
+        ns->buf_pos = 0;
+        ns->cntr = 0;
+
+        /* check if timer expired */
+        if (rt_tick_get() - ns->time_stamp > 600)
+        {
+            ns->buf_ptr[0] = 1;
+            ns->state = NETIO_STATE_SEND_DATA_LAST;
+        }
+        else
+        {
+            ns->buf_ptr[0] = 0;
+        }
+    }
+
+    if (ns->state == NETIO_STATE_SEND_DATA_LAST || ns->state == NETIO_STATE_SEND_DATA)
+    {
+        len = tcp_sndbuf(pcb);
+        len = LWIP_MIN(len, ns->data_len - ns->cntr);
+        len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos);
+
+        if (ns->cntr < ns->data_len)
+        {
+            do
+            {
+                err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY);
+                if (err == ERR_MEM)
+                {
+                    len /= 2;
+                }
+            }
+            while ((err == ERR_MEM) && (len > 1));
+
+            ns->buf_pos += len;
+            if (ns->buf_pos >= NETIO_BUF_SIZE)
+            {
+                ns->buf_pos = 0;
+            }
+
+            ns->cntr += len;
+        }
+    }
+
+    if (ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA_LAST)
+    {
+        /* we have buffered up all our data to send this last round, go look for a command */
+        ns->state = NETIO_STATE_WAIT_FOR_CMD;
+        ns->cntr  = 0;
+        /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */
+    }
+
+    return ERR_OK;
+}
+
+static err_t
+netio_poll(void *arg, struct tcp_pcb *pcb)
+{
+    struct netio_state *ns = arg;
+    if (ns->state == NETIO_STATE_SEND_DATA)
+    {
+
+    }
+    else if (ns->state == NETIO_STATE_DONE)
+    {
+        netio_close(ns, pcb);
+    }
+
+    return ERR_OK;
+
+}
+
+#if NETIO_USE_STATIC_BUF == 1
+static u8_t netio_buf[NETIO_BUF_SIZE];
+#endif
+
+static err_t
+netio_accept(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+    struct netio_state *ns;
+
+    LWIP_UNUSED_ARG(err);
+
+    ns = mem_malloc(sizeof(struct netio_state));
+
+    if (ns == NULL)
+    {
+        return ERR_MEM;
+    }
+
+    ns->state = NETIO_STATE_WAIT_FOR_CMD;
+    ns->data_len = 0;
+    ns->cmd = 0;
+    ns->cntr = 0;
+    ns->buf_pos = 0;
+#if NETIO_USE_STATIC_BUF == 1
+    ns->buf_ptr = netio_buf;
+#else
+    ns->buf_ptr = mem_malloc(NETIO_BUF_SIZE);
+
+    if (ns->buf_ptr == NULL)
+    {
+        mem_free(ns);
+        return ERR_MEM;
+    }
+#endif
+
+    ns->buf_ptr[0] = 0;
+
+    tcp_arg(pcb, ns);
+    tcp_sent(pcb, netio_sent);
+    tcp_recv(pcb, netio_recv);
+    tcp_poll(pcb, netio_poll, 4); /* every 2 seconds */
+    return ERR_OK;
+}
+
+void netio_init(void)
+{
+    struct tcp_pcb *pcb;
+
+    pcb = tcp_new();
+    tcp_bind(pcb, IP_ADDR_ANY, 18767);
+    pcb = tcp_listen(pcb);
+    tcp_accept(pcb, netio_accept);
+}
+
+#endif /* LWIP_TCP */
+
+#ifdef RT_USING_FINSH
+#include <finsh.h>
+FINSH_FUNCTION_EXPORT(netio_init, netio server);
+#ifdef FINSH_USING_MSH
+MSH_CMD_EXPORT(netio_init, netio server);
+#endif /* FINSH_USING_MSH */
+#endif /* RT_USING_FINSH */

+ 1 - 0
ntp/README.md

@@ -0,0 +1 @@
+Coming soon...

+ 10 - 0
ntp/SConscript

@@ -0,0 +1,10 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+group = DefineGroup('NetUtils', src, depend = ['PKG_NETUTILS_NTP'], CPPPATH = CPPPATH)
+
+Return('group')

+ 211 - 0
ntp/ntp.c

@@ -0,0 +1,211 @@
+/*
+ *
+ * (C) 2014 David Lettier.
+ * (C) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * http://www.lettier.com/
+ *
+ * NTP client.
+ *
+ * Compiled with gcc version 4.7.2 20121109 (Red Hat 4.7.2-8) (GCC).
+ *
+ * Tested on Linux 3.8.11-200.fc18.x86_64 #1 SMP Wed May 1 19:44:27 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux.
+ * Tested on RT-Thread 3.0.0+
+ *
+ * To compile: $ gcc main.c -o ntpClient.out
+ *
+ * Usage: $ ./ntpClient.out
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <rtthread.h>
+
+#define NTP_TIMESTAMP_DELTA 2208988800ull
+#define NTP_GET_TIMEOUT     10
+
+#ifndef NTP_TIMEZONE_DEAULT
+#define NTP_TIMEZONE_DEAULT 8
+#endif
+
+#define LI(packet)   (uint8_t) ((packet.li_vn_mode & 0xC0) >> 6) // (li   & 11 000 000) >> 6
+#define VN(packet)   (uint8_t) ((packet.li_vn_mode & 0x38) >> 3) // (vn   & 00 111 000) >> 3
+#define MODE(packet) (uint8_t) ((packet.li_vn_mode & 0x07) >> 0) // (mode & 00 000 111) >> 0
+
+// Structure that defines the 48 byte NTP packet protocol.
+typedef struct {
+
+    uint8_t li_vn_mode;      // Eight bits. li, vn, and mode.
+                         // li.   Two bits.   Leap indicator.
+                         // vn.   Three bits. Version number of the protocol.
+                         // mode. Three bits. Client will pick mode 3 for client.
+
+    uint8_t stratum;         // Eight bits. Stratum level of the local clock.
+    uint8_t poll;            // Eight bits. Maximum interval between successive messages.
+    uint8_t precision;       // Eight bits. Precision of the local clock.
+
+    uint32_t rootDelay;      // 32 bits. Total round trip delay time.
+    uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
+    uint32_t refId;          // 32 bits. Reference clock identifier.
+
+    uint32_t refTm_s;        // 32 bits. Reference time-stamp seconds.
+    uint32_t refTm_f;        // 32 bits. Reference time-stamp fraction of a second.
+
+    uint32_t origTm_s;       // 32 bits. Originate time-stamp seconds.
+    uint32_t origTm_f;       // 32 bits. Originate time-stamp fraction of a second.
+
+    uint32_t rxTm_s;         // 32 bits. Received time-stamp seconds.
+    uint32_t rxTm_f;         // 32 bits. Received time-stamp fraction of a second.
+
+    uint32_t txTm_s;         // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
+    uint32_t txTm_f;         // 32 bits. Transmit time-stamp fraction of a second.
+
+} ntp_packet;              // Total: 384 bits or 48 bytes.
+
+static ntp_packet packet = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static void error(char* msg)
+{
+    printf("[NTP]: %s\n", msg); // Print the error message to stderr.
+}
+
+void ntp( int argc, char* argv[])
+{
+    int sockfd, n; // Socket file descriptor and the n return result from writing/reading from the socket.
+
+    int portno = 123; // NTP UDP port number.
+
+    char* host_name = "cn.pool.ntp.org"; // NTP server host-name.
+
+    struct tm time_new;
+
+    // Create and zero out the packet. All 48 bytes worth.
+
+    memset(&packet, 0, sizeof(ntp_packet));
+
+    // Set the first byte's bits to 00,011,011 for li = 0, vn = 3, and mode = 3. The rest will be left set to zero.
+
+    *((char *) &packet + 0) = 0x1b; // Represents 27 in base 10 or 00011011 in base 2.
+
+    // Create a UDP socket, convert the host-name to an IP address, set the port number,
+    // connect to the server, send the packet, and then read in the return packet.
+
+    struct sockaddr_in serv_addr; // Server address data structure.
+    struct hostent *server;      // Server data structure.
+
+    sockfd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Create a UDP socket.
+
+    if (sockfd < 0) {
+        error("ERROR opening socket");
+        goto __exit;
+    }
+
+    server = gethostbyname(host_name); // Convert URL to IP.
+
+    if (server == NULL) {
+        error("ERROR, no such host");
+        goto __exit;
+    }
+
+    // Zero out the server address structure.
+
+    memset((char *) &serv_addr, 0, sizeof(serv_addr));
+
+    serv_addr.sin_family = AF_INET;
+
+    // Copy the server's IP address to the server address structure.
+
+    memcpy((char *) &serv_addr.sin_addr.s_addr, (char *) server->h_addr, server->h_length);
+
+    // Convert the port number integer to network big-endian style and save it to the server address structure.
+
+    serv_addr.sin_port = htons(portno);
+
+    // Call up the server using its IP address and port number.
+
+    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
+        error("ERROR connecting");
+        goto __exit;
+    }
+
+    // Send it the NTP packet it wants. If n == -1, it failed.
+
+    n = write(sockfd, (char*) &packet, sizeof(ntp_packet));
+
+    if (n < 0) {
+        error("ERROR writing to socket");
+        goto __exit;
+    }
+
+    fd_set readset;
+    struct timeval timeout;
+    timeout.tv_sec = NTP_GET_TIMEOUT;
+    timeout.tv_usec = 0;
+
+    FD_ZERO(&readset);
+    FD_SET(sockfd, &readset);
+
+    if (select(sockfd + 1, &readset, RT_NULL, RT_NULL, &timeout) <= 0) {
+        error("ERROR select the socket timeout(10s)");
+        goto __exit;
+    }
+
+    // Wait and receive the packet back from the server. If n == -1, it failed.
+
+    n = read(sockfd, (char*) &packet, sizeof(ntp_packet));
+
+    if (n < 0) {
+        error("ERROR reading from socket");
+        goto __exit;
+    }
+
+    // These two fields contain the time-stamp seconds as the packet left the NTP server.
+    // The number of seconds correspond to the seconds passed since 1900.
+    // ntohl() converts the bit/byte order from the network's to host's "endianness".
+
+    packet.txTm_s = ntohl(packet.txTm_s); // Time-stamp seconds.
+    packet.txTm_f = ntohl(packet.txTm_f); // Time-stamp fraction of a second.
+
+    // Extract the 32 bits that represent the time-stamp seconds (since NTP epoch) from when the packet left the server.
+    // Subtract 70 years worth of seconds from the seconds since 1900.
+    // This leaves the seconds since the UNIX epoch of 1970.
+    // (1900)------------------(1970)**************************************(Time Packet Left the Server)
+
+    time_t txTm = (time_t) (packet.txTm_s - NTP_TIMESTAMP_DELTA);
+
+    // Print the time we got from the server, accounting for local timezone and conversion from UTC time.
+
+    printf("NTP Server Time: %s", ctime((const time_t*) &txTm));
+
+    /* add the timezone offset for set_time/set_date */
+    txTm += NTP_TIMEZONE_DEAULT * 3600;
+
+    localtime_r(&txTm, &time_new);
+
+#ifdef RT_USING_RTC
+    set_time(time_new.tm_hour, time_new.tm_min, time_new.tm_sec);
+    set_date(time_new.tm_year + 1900, time_new.tm_mon + 1, time_new.tm_mday);
+#endif
+
+    printf("The system time is updated. Timezone is %d.\n", NTP_TIMEZONE_DEAULT);
+
+__exit:
+
+    close(sockfd);
+}
+#ifdef RT_USING_FINSH
+#include <finsh.h>
+FINSH_FUNCTION_EXPORT(ntp, Update time by NTP(Network Time Protocol));
+#ifdef FINSH_USING_MSH
+MSH_CMD_EXPORT(ntp, Update time by NTP(Network Time Protocol));
+#endif /* FINSH_USING_MSH */
+#endif /* RT_USING_FINSH */
+

+ 1 - 0
ping/README.md

@@ -0,0 +1 @@
+Coming soon...

+ 10 - 0
ping/SConscript

@@ -0,0 +1,10 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+group = DefineGroup('NetUtils', src, depend = ['PKG_NETUTILS_PING'], CPPPATH = CPPPATH)
+
+Return('group')

+ 201 - 0
ping/ping.c

@@ -0,0 +1,201 @@
+/*
+ * netutils: ping implementation
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/mem.h"
+#include "lwip/icmp.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/sockets.h"
+#include "lwip/inet.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+
+/**
+ * PING_DEBUG: Enable debugging for PING.
+ */
+#ifndef PING_DEBUG
+#define PING_DEBUG     LWIP_DBG_ON
+#endif
+
+/** ping receive timeout - in milliseconds */
+#define PING_RCV_TIMEO rt_tick_from_millisecond(2000)
+/** ping delay - in milliseconds */
+#define PING_DELAY     rt_tick_from_millisecond(1000)
+
+/** ping identifier - must fit on a u16_t */
+#ifndef PING_ID
+#define PING_ID        0xAFAF
+#endif
+
+/** ping additional data size to include in the packet */
+#ifndef PING_DATA_SIZE
+#define PING_DATA_SIZE 32
+#endif
+
+/* ping variables */
+static u16_t ping_seq_num;
+struct _ip_addr
+{
+    rt_uint8_t addr0, addr1, addr2, addr3;
+};
+
+/** Prepare a echo ICMP request */
+static void ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len)
+{
+    size_t i;
+    size_t data_len = len - sizeof(struct icmp_echo_hdr);
+
+    ICMPH_TYPE_SET(iecho, ICMP_ECHO);
+    ICMPH_CODE_SET(iecho, 0);
+    iecho->chksum = 0;
+    iecho->id     = PING_ID;
+    iecho->seqno  = htons(++ping_seq_num);
+
+    /* fill the additional data buffer with some data */
+    for(i = 0; i < data_len; i++)
+    {
+        ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
+    }
+
+    iecho->chksum = inet_chksum(iecho, len);
+}
+
+/* Ping using the socket ip */
+static err_t ping_send(int s, ip_addr_t *addr, int size)
+{
+    int err;
+    struct icmp_echo_hdr *iecho;
+    struct sockaddr_in to;
+    size_t ping_size = sizeof(struct icmp_echo_hdr) + size;
+    LWIP_ASSERT("ping_size is too big", ping_size <= 0xffff);
+
+    iecho = rt_malloc(ping_size);
+    if (iecho == RT_NULL)
+    {
+        return ERR_MEM;
+    }
+
+    ping_prepare_echo(iecho, (u16_t)ping_size);
+
+    to.sin_len = sizeof(to);
+    to.sin_family = AF_INET;
+    to.sin_addr.s_addr = addr->addr;
+
+    err = lwip_sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to));
+    rt_free(iecho);
+
+    return (err == ping_size ? ERR_OK : ERR_VAL);
+}
+
+static int ping_recv(int s, int *ttl)
+{
+    char buf[64];
+    int fromlen = sizeof(struct sockaddr_in), len;
+    struct sockaddr_in from;
+    struct ip_hdr *iphdr;
+    struct icmp_echo_hdr *iecho;
+
+    while((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0)
+    {
+        if (len >= (sizeof(struct ip_hdr)+sizeof(struct icmp_echo_hdr)))
+        {
+            iphdr = (struct ip_hdr *)buf;
+            iecho = (struct icmp_echo_hdr *)(buf+(IPH_HL(iphdr) * 4));
+            if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num)))
+            {
+                *ttl = iphdr->_ttl;
+                return len;
+            }
+        }
+    }
+
+    return len;
+}
+
+rt_err_t ping(char* target, rt_uint32_t times, rt_size_t size)
+{
+    int s, ttl, recv_len;
+    struct timeval timeout = { PING_RCV_TIMEO / RT_TICK_PER_SECOND, PING_RCV_TIMEO % RT_TICK_PER_SECOND };
+    ip_addr_t ping_target;
+    rt_uint32_t send_times;
+    rt_tick_t recv_start_tick;
+    struct _ip_addr
+    {
+        rt_uint8_t addr0, addr1, addr2, addr3;
+    } *addr;
+
+    send_times = 0;
+    ping_seq_num = 0;
+
+    if(size == 0)
+        size = PING_DATA_SIZE;
+
+    if (inet_aton(target, &ping_target) == 0)
+    {
+        rt_kprintf("ping: unknown host %s\n", target);
+        return -RT_ERROR;
+    }
+    addr = (struct _ip_addr*)&ping_target;
+
+    if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0)
+    {
+        rt_kprintf("ping: create socket failled\n");
+        return -RT_ERROR;
+    }
+
+    lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval));
+
+    while (1)
+    {
+        if (ping_send(s, &ping_target, size) == ERR_OK)
+        {
+            recv_start_tick = rt_tick_get();
+            if ((recv_len = ping_recv(s, &ttl)) >= 0)
+            {
+                rt_kprintf("%d bytes from %d.%d.%d.%d icmp_seq=%d ttl=%d time=%d ticks\n", recv_len, addr->addr0,
+                        addr->addr1, addr->addr2, addr->addr3, send_times, ttl, rt_tick_get() - recv_start_tick);
+            }
+            else
+            {
+                rt_kprintf("From %d.%d.%d.%d icmp_seq=%d timeout\n", addr->addr0, addr->addr1, addr->addr2,
+                        addr->addr3, send_times);
+            }
+        }
+        else
+        {
+            rt_kprintf("Send %d.%d.%d.%d - error\n", addr->addr0, addr->addr1, addr->addr2, addr->addr3);
+        }
+
+        send_times ++;
+        if (send_times >= times) break; /* send ping times reached, stop */
+
+        rt_thread_delay(PING_DELAY); /* take a delay */
+    }
+
+    lwip_close(s);
+
+    return RT_EOK;
+}
+#ifdef RT_USING_FINSH
+#include <finsh.h>
+
+FINSH_FUNCTION_EXPORT(ping, ping network host);
+
+int cmd_ping(int argc, char **argv)
+{
+    if (argc == 1)
+    {
+        rt_kprintf("Please input: ping <host address>\n");
+    }
+    else
+    {
+        ping(argv[1], 4, 0);
+    }
+
+    return 0;
+}
+FINSH_FUNCTION_EXPORT_ALIAS(cmd_ping, __cmd_ping, ping network host);
+#endif

+ 1 - 0
telnet/README.md

@@ -0,0 +1 @@
+Coming soon...

+ 10 - 0
telnet/SConscript

@@ -0,0 +1,10 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+group = DefineGroup('NetUtils', src, depend = ['PKG_NETUTILS_TELNET'], CPPPATH = CPPPATH)
+
+Return('group')

+ 395 - 0
telnet/telnet.c

@@ -0,0 +1,395 @@
+#include <rtthread.h>
+#include <lwip/api.h>
+#include <lwip/sockets.h>
+#include <rtdevice.h>
+
+#include <finsh.h>
+#include <shell.h>
+
+#if RTTHREAD_VERSION >= 30000
+#error "not supported, I hope you can fix it"
+#endif
+
+#define TELNET_PORT         23
+#define TELNET_BACKLOG      5
+#define RX_BUFFER_SIZE      256
+#define TX_BUFFER_SIZE      4096
+
+#define ISO_nl              0x0a
+#define ISO_cr              0x0d
+
+#define STATE_NORMAL        0
+#define STATE_IAC           1
+#define STATE_WILL          2
+#define STATE_WONT          3
+#define STATE_DO            4
+#define STATE_DONT          5
+#define STATE_CLOSE         6
+
+#define TELNET_IAC          255
+#define TELNET_WILL         251
+#define TELNET_WONT         252
+#define TELNET_DO           253
+#define TELNET_DONT         254
+
+struct telnet_session
+{
+    struct rt_ringbuffer rx_ringbuffer;
+    struct rt_ringbuffer tx_ringbuffer;
+
+    rt_mutex_t rx_ringbuffer_lock;
+    rt_mutex_t tx_ringbuffer_lock;
+
+    struct rt_device device;
+    rt_int32_t server_fd;
+    rt_int32_t client_fd;
+
+    /* telnet protocol */
+    rt_uint8_t state;
+    rt_uint8_t echo_mode;
+
+};
+
+static struct telnet_session* telnet;
+
+/* process tx data */
+static void send_to_client(struct telnet_session* telnet)
+{
+    rt_size_t length;
+    rt_uint8_t tx_buffer[32];
+
+    while (1)
+    {
+        rt_memset(tx_buffer, 0, sizeof(tx_buffer));
+        rt_mutex_take(telnet->tx_ringbuffer_lock, RT_WAITING_FOREVER);
+        /* get buffer from ringbuffer */
+        length = rt_ringbuffer_get(&(telnet->tx_ringbuffer), tx_buffer, sizeof(tx_buffer));
+        rt_mutex_release(telnet->tx_ringbuffer_lock);
+
+        /* do a tx procedure */
+        if (length > 0)
+        {
+            send(telnet->client_fd, tx_buffer, length, 0);
+        }
+        else break;
+    }
+}
+
+/* send telnet option to remote */
+static void send_option_to_client(struct telnet_session* telnet, rt_uint8_t option, rt_uint8_t value)
+{
+    rt_uint8_t optbuf[4];
+
+    optbuf[0] = TELNET_IAC;
+    optbuf[1] = option;
+    optbuf[2] = value;
+    optbuf[3] = 0;
+
+    rt_mutex_take(telnet->tx_ringbuffer_lock, RT_WAITING_FOREVER);
+    rt_ringbuffer_put(&telnet->tx_ringbuffer, optbuf, 3);
+    rt_mutex_release(telnet->tx_ringbuffer_lock);
+
+    send_to_client(telnet);
+}
+
+/* process rx data */
+static void process_rx(struct telnet_session* telnet, rt_uint8_t *data, rt_size_t length)
+{
+    rt_size_t rx_length, index;
+
+    for (index = 0; index < length; index ++)
+    {
+        switch(telnet->state)
+        {
+        case STATE_IAC:
+            if (*data == TELNET_IAC)
+            {
+                rt_mutex_take(telnet->rx_ringbuffer_lock, RT_WAITING_FOREVER);
+                /* put buffer to ringbuffer */
+                rt_ringbuffer_putchar(&(telnet->rx_ringbuffer), *data);
+                rt_mutex_release(telnet->rx_ringbuffer_lock);
+
+                telnet->state = STATE_NORMAL;
+            }
+            else
+            {
+                /* set telnet state according to received package */
+                switch (*data)
+                {
+                case TELNET_WILL: telnet->state = STATE_WILL; break;
+                case TELNET_WONT: telnet->state = STATE_WONT; break;
+                case TELNET_DO:   telnet->state = STATE_DO; break;
+                case TELNET_DONT: telnet->state = STATE_DONT; break;
+                default: telnet->state = STATE_NORMAL; break;
+                }
+            }
+            break;
+
+        /* don't option */
+        case STATE_WILL:
+        case STATE_WONT:
+            send_option_to_client(telnet, TELNET_DONT, *data);
+            telnet->state = STATE_NORMAL;
+            break;
+
+        /* won't option */
+        case STATE_DO:
+        case STATE_DONT:
+            send_option_to_client(telnet, TELNET_WONT, *data);
+            telnet->state = STATE_NORMAL;
+            break;
+
+        case STATE_NORMAL:
+            if (*data == TELNET_IAC) telnet->state = STATE_IAC;
+            else if (*data != '\r') /* ignore '\r' */
+            {
+                rt_mutex_take(telnet->rx_ringbuffer_lock, RT_WAITING_FOREVER);
+                /* put buffer to ringbuffer */
+                rt_ringbuffer_putchar(&(telnet->rx_ringbuffer), *data);
+                rt_mutex_release(telnet->rx_ringbuffer_lock);
+            }
+            break;
+        }
+
+        data ++;
+    }
+
+    rt_mutex_take(telnet->rx_ringbuffer_lock, RT_WAITING_FOREVER);
+    /* get total size */
+    rx_length = rt_ringbuffer_data_len(&telnet->rx_ringbuffer);
+    rt_mutex_release(telnet->rx_ringbuffer_lock);
+
+    /* indicate there are reception data */
+    if ((rx_length > 0) && (telnet->device.rx_indicate != RT_NULL))
+        telnet->device.rx_indicate(&telnet->device, rx_length);
+
+    return;
+}
+
+/* client close */
+static void client_close(struct telnet_session* telnet)
+{
+    /* set console */
+    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
+    /* set finsh device */
+    finsh_set_device(RT_CONSOLE_DEVICE_NAME);
+
+    /* close connection */
+    closesocket(telnet->client_fd);
+
+    /* restore shell option */
+    finsh_set_echo(telnet->echo_mode);
+
+    rt_kprintf("resume console to %s\n", RT_CONSOLE_DEVICE_NAME);
+}
+
+/* RT-Thread Device Driver Interface */
+static rt_err_t telnet_init(rt_device_t dev)
+{
+    return RT_EOK;
+}
+
+static rt_err_t telnet_open(rt_device_t dev, rt_uint16_t oflag)
+{
+    return RT_EOK;
+}
+
+static rt_err_t telnet_close(rt_device_t dev)
+{
+    return RT_EOK;
+}
+
+static rt_size_t telnet_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
+{
+    rt_size_t result;
+
+    /* read from rx ring buffer */
+    rt_mutex_take(telnet->rx_ringbuffer_lock, RT_WAITING_FOREVER);
+    result = rt_ringbuffer_get(&(telnet->rx_ringbuffer), buffer, size);
+    rt_mutex_release(telnet->rx_ringbuffer_lock);
+
+    return result;
+}
+
+static rt_size_t telnet_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
+{
+    const rt_uint8_t *ptr;
+
+    ptr = (rt_uint8_t*) buffer;
+
+    rt_mutex_take(telnet->tx_ringbuffer_lock, RT_WAITING_FOREVER);
+    while (size)
+    {
+        if (*ptr == '\n')
+            rt_ringbuffer_putchar(&telnet->tx_ringbuffer, '\r');
+
+        if (rt_ringbuffer_putchar(&telnet->tx_ringbuffer, *ptr) == 0) /* overflow */
+            break;
+        ptr++;
+        size--;
+    }
+    rt_mutex_release(telnet->tx_ringbuffer_lock);
+
+    /* send data to telnet client */
+    send_to_client(telnet);
+
+    return (rt_uint32_t) ptr - (rt_uint32_t) buffer;
+}
+
+static rt_err_t telnet_control(rt_device_t dev, int cmd, void *args)
+{
+    return RT_EOK;
+}
+
+/* telnet server thread entry */
+static void telnet_thread(void* parameter)
+{
+#define RECV_BUF_LEN 64
+
+    struct sockaddr_in addr;
+    socklen_t addr_size;
+    rt_uint8_t recv_buf[RECV_BUF_LEN];
+    rt_int32_t recv_len = 0;
+
+    if ((telnet->server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+    {
+        rt_kprintf("telnet: create socket failed\n");
+        return;
+    }
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(TELNET_PORT);
+    addr.sin_addr.s_addr = INADDR_ANY;
+    rt_memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
+    if (bind(telnet->server_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1)
+    {
+        rt_kprintf("telnet: bind socket failed\n");
+        return;
+    }
+
+    if (listen(telnet->server_fd, TELNET_BACKLOG) == -1)
+    {
+        rt_kprintf("telnet: listen socket failed\n");
+        return;
+    }
+
+    /* register telnet device */
+    telnet->device.type     = RT_Device_Class_Char;
+    telnet->device.init     = telnet_init;
+    telnet->device.open     = telnet_open;
+    telnet->device.close    = telnet_close;
+    telnet->device.read     = telnet_read;
+    telnet->device.write    = telnet_write;
+    telnet->device.control  = telnet_control;
+
+    /* no private */
+    telnet->device.user_data = RT_NULL;
+
+    /* register telnet device */
+    rt_device_register(&telnet->device, "telnet",
+                       RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STREAM);
+
+    while (1)
+    {
+        rt_kprintf("telnet server waiting for connection\n");
+
+        /* grab new connection */
+        if ((telnet->client_fd = accept(telnet->server_fd, (struct sockaddr * )&addr, &addr_size)) == -1)
+        {
+            continue;
+        }
+
+        rt_kprintf("new telnet client(%s:%d) connection, switch console to telnet...\n", inet_ntoa(addr.sin_addr), addr.sin_port);
+
+        /* process the new connection */
+        /* set console */
+        rt_console_set_device("telnet");
+        /* set finsh device */
+        finsh_set_device("telnet");
+
+        /* set init state */
+        telnet->state = STATE_NORMAL;
+
+        telnet->echo_mode = finsh_get_echo();
+        /* disable echo mode */
+        finsh_set_echo(0);
+
+        while (1)
+        {
+            /* try to send all data in tx ringbuffer */
+            send_to_client(telnet);
+
+            /* do a rx procedure */
+            if ((recv_len = recv(telnet->client_fd, recv_buf, RECV_BUF_LEN, 0)) > 0)
+            {
+                process_rx(telnet, recv_buf, recv_len);
+            }
+            else
+            {
+                /* close connection */
+                client_close(telnet);
+                break;
+            }
+        }
+    }
+}
+
+/* telnet server */
+void telnet_srv(void)
+{
+    rt_thread_t tid;
+
+    if (telnet == RT_NULL)
+    {
+        rt_uint8_t *ptr;
+
+        telnet = rt_malloc(sizeof(struct telnet_session));
+        if (telnet == RT_NULL)
+        {
+            rt_kprintf("telnet: no memory\n");
+            return;
+        }
+        /* init ringbuffer */
+        ptr = rt_malloc(RX_BUFFER_SIZE);
+        if (ptr)
+        {
+            rt_ringbuffer_init(&telnet->rx_ringbuffer, ptr, RX_BUFFER_SIZE);
+        }
+        else
+        {
+            rt_kprintf("telnet: no memory\n");
+            return;
+        }
+        ptr = rt_malloc(TX_BUFFER_SIZE);
+        if (ptr)
+        {
+            rt_ringbuffer_init(&telnet->tx_ringbuffer, ptr, TX_BUFFER_SIZE);
+        }
+        else
+        {
+            rt_kprintf("telnet: no memory\n");
+            return;
+        }
+        /* create tx ringbuffer lock */
+        telnet->tx_ringbuffer_lock = rt_mutex_create("telnet_tx", RT_IPC_FLAG_FIFO);
+        /* create rx ringbuffer lock */
+        telnet->rx_ringbuffer_lock = rt_mutex_create("telnet_rx", RT_IPC_FLAG_FIFO);
+
+        tid = rt_thread_create("telnet", telnet_thread, RT_NULL, 2048, 25, 5);
+        if (tid != RT_NULL)
+            rt_thread_startup(tid);
+    }
+    else
+    {
+        rt_kprintf("telnet: already running\n");
+    }
+
+}
+
+#ifdef RT_USING_FINSH
+#include <finsh.h>
+FINSH_FUNCTION_EXPORT(telnet_srv, startup telnet server);
+#ifdef FINSH_USING_MSH
+MSH_CMD_EXPORT(telnet_srv, startup telnet server)
+#endif /* FINSH_USING_MSH */
+#endif /* RT_USING_FINSH */

+ 1 - 0
tftp/README.md

@@ -0,0 +1 @@
+Coming soon...

+ 10 - 0
tftp/SConscript

@@ -0,0 +1,10 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+group = DefineGroup('NetUtils', src, depend = ['PKG_NETUTILS_TFTP'], CPPPATH = CPPPATH)
+
+Return('group')

+ 87 - 0
tftp/tftp_port.c

@@ -0,0 +1,87 @@
+/*
+ * File      : tftp_port.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2017, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2017-08-17     armink       first version.
+ */
+
+
+#include <rtthread.h>
+#include <dfs_posix.h>
+#include <lwip/apps/tftp_server.h>
+
+static struct tftp_context ctx;
+
+static void* tftp_open(const char* fname, const char* mode, u8_t write)
+{
+    int fd = -1;
+
+    if (!rt_strcmp(mode, "octet"))
+    {
+        if (write)
+        {
+            fd = open(fname, O_WRONLY | O_CREAT, 0);
+        }
+        else
+        {
+            fd = open(fname, O_RDONLY, 0);
+        }
+    }
+    else
+    {
+        rt_kprintf("tftp: No support this mode(%s).", mode);
+    }
+
+    return (void *) fd;
+}
+
+static int tftp_write(void* handle, struct pbuf* p)
+{
+    int fd = (int) handle;
+
+    return write(fd, p->payload, p->len);
+}
+
+#if defined(RT_USING_FINSH)
+#include <finsh.h>
+
+static void tftp_server(uint8_t argc, char **argv)
+{
+    ctx.open = tftp_open;
+    ctx.close = (void (*)(void *)) close;
+    ctx.read = (int (*)(void *, void *, int)) read;
+    ctx.write = tftp_write;
+
+    if (tftp_init(&ctx) == ERR_OK)
+    {
+        rt_kprintf("TFTP server start successfully.\n");
+    }
+    else
+    {
+        rt_kprintf("TFTP server start failed.\n");
+    }
+}
+FINSH_FUNCTION_EXPORT(tftp_server, start tftp server.);
+
+#if defined(FINSH_USING_MSH)
+MSH_CMD_EXPORT(tftp_server, start tftp server.);
+#endif /* defined(FINSH_USING_MSH) */
+
+#endif /* defined(RT_USING_FINSH) */

+ 417 - 0
tftp/tftp_server.c

@@ -0,0 +1,417 @@
+/****************************************************************//**
+ *
+ * @file tftp_server.c
+ *
+ * @author   Logan Gunthorpe <logang@deltatee.com>
+ *           Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ * @brief    Trivial File Transfer Protocol (RFC 1350)
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * 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. 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.
+ *
+ * Author: Logan Gunthorpe <logang@deltatee.com>
+ *         Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+/**
+ * @defgroup tftp TFTP server
+ * @ingroup apps
+ *
+ * This is simple TFTP server for the lwIP raw API.
+ */
+
+#include "lwip/apps/tftp_server.h"
+
+#if LWIP_UDP
+
+#include "lwip/udp.h"
+#include "lwip/timeouts.h"
+#include "lwip/debug.h"
+
+#define TFTP_MAX_PAYLOAD_SIZE 512
+#define TFTP_HEADER_LENGTH    4
+
+#define TFTP_RRQ   1
+#define TFTP_WRQ   2
+#define TFTP_DATA  3
+#define TFTP_ACK   4
+#define TFTP_ERROR 5
+
+enum tftp_error {
+  TFTP_ERROR_FILE_NOT_FOUND    = 1,
+  TFTP_ERROR_ACCESS_VIOLATION  = 2,
+  TFTP_ERROR_DISK_FULL         = 3,
+  TFTP_ERROR_ILLEGAL_OPERATION = 4,
+  TFTP_ERROR_UNKNOWN_TRFR_ID   = 5,
+  TFTP_ERROR_FILE_EXISTS       = 6,
+  TFTP_ERROR_NO_SUCH_USER      = 7
+};
+
+#include <string.h>
+
+struct tftp_state {
+  const struct tftp_context *ctx;
+  void *handle;
+  struct pbuf *last_data;
+  struct udp_pcb *upcb;
+  ip_addr_t addr;
+  u16_t port;
+  int timer;
+  int last_pkt;
+  u16_t blknum;
+  u8_t retries;
+  u8_t mode_write;
+};
+
+static struct tftp_state tftp_state;
+
+static void tftp_tmr(void* arg);
+
+static void
+close_handle(void)
+{
+  tftp_state.port = 0;
+  ip_addr_set_any(0, &tftp_state.addr);
+
+  if(tftp_state.last_data != NULL) {
+    pbuf_free(tftp_state.last_data);
+    tftp_state.last_data = NULL;
+  }
+
+  sys_untimeout(tftp_tmr, NULL);
+  
+  if (tftp_state.handle) {
+    tftp_state.ctx->close(tftp_state.handle);
+    tftp_state.handle = NULL;
+    LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
+  }
+}
+
+static void
+send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
+{
+  int str_length = strlen(str);
+  struct pbuf* p;
+  u16_t* payload;
+  
+  p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
+  if(p == NULL) {
+    return;
+  }
+
+  payload = (u16_t*) p->payload;
+  payload[0] = PP_HTONS(TFTP_ERROR);
+  payload[1] = lwip_htons(code);
+  MEMCPY(&payload[2], str, str_length + 1);
+
+  udp_sendto(tftp_state.upcb, p, addr, port);
+  pbuf_free(p);
+}
+
+static void
+send_ack(u16_t blknum)
+{
+  struct pbuf* p;
+  u16_t* payload;
+  
+  p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
+  if(p == NULL) {
+    return;
+  }
+  payload = (u16_t*) p->payload;
+  
+  payload[0] = PP_HTONS(TFTP_ACK);
+  payload[1] = lwip_htons(blknum);
+  udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
+  pbuf_free(p);
+}
+
+static void
+resend_data(void)
+{
+  struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
+  if(p == NULL) {
+    return;
+  }
+
+  if(pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
+    pbuf_free(p);
+    return;
+  }
+    
+  udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
+  pbuf_free(p);
+}
+
+static void
+send_data(void)
+{
+  u16_t *payload;
+  int ret;
+
+  if(tftp_state.last_data != NULL) {
+    pbuf_free(tftp_state.last_data);
+  }
+  
+  tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
+  if(tftp_state.last_data == NULL) {
+    return;
+  }
+
+  payload = (u16_t *) tftp_state.last_data->payload;
+  payload[0] = PP_HTONS(TFTP_DATA);
+  payload[1] = lwip_htons(tftp_state.blknum);
+
+  ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
+  if (ret < 0) {
+    send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
+    close_handle();
+    return;
+  }
+
+  pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
+  resend_data();
+}
+
+static void
+recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  u16_t *sbuf = (u16_t *) p->payload;
+  int opcode;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(upcb);
+  
+  if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
+      (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
+    send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+    pbuf_free(p);
+    return;
+  }
+
+  opcode = sbuf[0];
+
+  tftp_state.last_pkt = tftp_state.timer;
+  tftp_state.retries = 0;
+
+  switch (opcode) {
+    case PP_HTONS(TFTP_RRQ): /* fall through */
+    case PP_HTONS(TFTP_WRQ):
+    {
+      const char tftp_null = 0;
+      char filename[TFTP_MAX_FILENAME_LEN + 1] = { 0 };
+      char mode[TFTP_MAX_MODE_LEN] = { 0 };
+      u16_t filename_end_offset;
+      u16_t mode_end_offset;
+
+      if(tftp_state.handle != NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+        break;
+      }
+      
+      sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+      /* find \0 in pbuf -> end of filename string */
+      filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
+      if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
+        break;
+      }
+      pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
+
+      /* find \0 in pbuf -> end of mode string */
+      mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
+      if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
+        break;
+      }
+      pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
+ 
+      tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
+      tftp_state.blknum = 1;
+
+      if (!tftp_state.handle) {
+        send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
+        break;
+      }
+
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
+      ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
+
+      ip_addr_copy(tftp_state.addr, *addr);
+      tftp_state.port = port;
+
+      if (opcode == PP_HTONS(TFTP_WRQ)) {
+        tftp_state.mode_write = 1;
+        send_ack(0);
+      } else {
+        tftp_state.mode_write = 0;
+        send_data();
+      }
+
+      break;
+    }
+    
+    case PP_HTONS(TFTP_DATA):
+    {
+      int ret;
+      u16_t blknum;
+      
+      if (tftp_state.handle == NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+        break;
+      }
+
+      if (tftp_state.mode_write != 1) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
+        break;
+      }
+
+      blknum = lwip_ntohs(sbuf[1]);
+      pbuf_header(p, -TFTP_HEADER_LENGTH);
+
+      ret = tftp_state.ctx->write(tftp_state.handle, p);
+      if (ret < 0) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
+        close_handle();
+      } else {
+        send_ack(blknum);
+      }
+
+      if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
+        close_handle();
+      }
+      break;
+    }
+
+    case PP_HTONS(TFTP_ACK):
+    {
+      u16_t blknum;
+      int lastpkt;
+
+      if (tftp_state.handle == NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+        break;
+      }
+
+      if (tftp_state.mode_write != 0) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
+        break;
+      }
+
+      blknum = lwip_ntohs(sbuf[1]);
+      if (blknum != tftp_state.blknum) {
+        send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
+        break;
+      }
+
+      lastpkt = 0;
+
+      if (tftp_state.last_data != NULL) {
+        lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
+      }
+
+      if (!lastpkt) {
+        tftp_state.blknum++;
+        send_data();
+      } else {
+        close_handle();
+      }
+
+      break;
+    }
+    
+    default:
+      send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
+      break;
+  }
+
+  pbuf_free(p);
+}
+
+static void
+tftp_tmr(void* arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  
+  tftp_state.timer++;
+
+  if (tftp_state.handle == NULL) {
+    return;
+  }
+
+  sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+  if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
+    if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
+      resend_data();
+      tftp_state.retries++;
+    } else {
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
+      close_handle();
+    }
+  }
+}
+
+/** @ingroup tftp
+ * Initialize TFTP server.
+ * @param ctx TFTP callback struct
+ */
+err_t 
+tftp_init(const struct tftp_context *ctx)
+{
+  err_t ret;
+
+  struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  if (pcb == NULL) {
+    return ERR_MEM;
+  }
+
+  ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
+  if (ret != ERR_OK) {
+    udp_remove(pcb);
+    return ret;
+  }
+
+  tftp_state.handle    = NULL;
+  tftp_state.port      = 0;
+  tftp_state.ctx       = ctx;
+  tftp_state.timer     = 0;
+  tftp_state.last_data = NULL;
+  tftp_state.upcb      = pcb;
+
+  udp_recv(pcb, recv, NULL);
+
+  return ERR_OK;
+}
+
+#endif /* LWIP_UDP */

BIN
tools/Tftpd64-4.60-setup.exe


BIN
tools/iperf-3.1.3-win32.zip


BIN
tools/iperf-3.1.3-win64.zip


BIN
tools/netio-gui_v1.0.4_portable.exe