Browse Source

add ping, tftp client, sntp client and chargen network applications

git-svn-id: https://rt-thread.googlecode.com/svn/trunk@137 bbd45198-f89e-11dd-88c7-29a3b14d5316
bernard.xiong 15 years ago
parent
commit
36dfee4075
4 changed files with 783 additions and 0 deletions
  1. 204 0
      net/apps/chargen.c
  2. 195 0
      net/apps/ping.c
  3. 213 0
      net/apps/sntp.c
  4. 171 0
      net/apps/tftp.c

+ 204 - 0
net/apps/chargen.c

@@ -0,0 +1,204 @@
+#include <rtthread.h>
+
+#include "lwip/sockets.h"
+#define MAX_SERV                 5         /* Maximum number of chargen services. Don't need too many */
+#define CHARGEN_THREAD_NAME      "chargen"
+#define CHARGEN_PRIORITY         200       /* Really low priority */
+#define CHARGEN_THREAD_STACKSIZE 1024
+struct charcb
+{
+    struct charcb *next;
+    int socket;
+    struct sockaddr_in cliaddr;
+    socklen_t clilen;
+    char nextchar;
+};
+
+static struct charcb *charcb_list = 0;
+static int do_read(struct charcb *p_charcb);
+static void close_chargen(struct charcb *p_charcb);
+
+/**************************************************************
+ * void chargen_thread(void *arg)
+ *
+ * chargen task. This server will wait for connections on well
+ * known TCP port number: 19. For every connection, the server will
+ * write as much data as possible to the tcp port.
+ **************************************************************/
+static void chargen_thread(void *arg)
+{
+    int listenfd;
+    struct sockaddr_in chargen_saddr;
+    fd_set readset;
+    fd_set writeset;
+    int i, maxfdp1;
+    struct charcb *p_charcb;
+
+    /* First acquire our socket for listening for connections */
+    listenfd = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+    LWIP_ASSERT("chargen_thread(): Socket create failed.", listenfd >= 0);
+    memset(&chargen_saddr, 0, sizeof(chargen_saddr));
+    chargen_saddr.sin_family = AF_INET;
+    chargen_saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    chargen_saddr.sin_port = htons(19);     // Chargen server port
+
+    if (lwip_bind(listenfd, (struct sockaddr *) &chargen_saddr, sizeof(chargen_saddr)) == -1)
+        LWIP_ASSERT("chargen_thread(): Socket bind failed.", 0);
+
+    /* Put socket into listening mode */
+    if (lwip_listen(listenfd, MAX_SERV) == -1)
+        LWIP_ASSERT("chargen_thread(): Listen failed.", 0);
+
+    /* Wait forever for network input: This could be connections or data */
+    for (;;)
+    {
+        maxfdp1 = listenfd+1;
+
+        /* Determine what sockets need to be in readset */
+        FD_ZERO(&readset);
+        FD_ZERO(&writeset);
+        FD_SET(listenfd, &readset);
+        for (p_charcb = charcb_list; p_charcb; p_charcb = p_charcb->next)
+        {
+            if (maxfdp1 < p_charcb->socket + 1)
+                maxfdp1 = p_charcb->socket + 1;
+            FD_SET(p_charcb->socket, &readset);
+            FD_SET(p_charcb->socket, &writeset);
+        }
+
+        /* Wait for data or a new connection */
+        i = lwip_select(maxfdp1, &readset, &writeset, 0, 0);
+
+        if (i == 0) continue;
+
+        /* At least one descriptor is ready */
+        if (FD_ISSET(listenfd, &readset))
+        {
+            /* We have a new connection request!!! */
+            /* Lets create a new control block */
+            p_charcb = (struct charcb *)rt_calloc(1, sizeof(struct charcb));
+            if (p_charcb)
+            {
+                p_charcb->socket = lwip_accept(listenfd,
+                                        (struct sockaddr *) &p_charcb->cliaddr,
+                                        &p_charcb->clilen);
+                if (p_charcb->socket < 0)
+                    rt_free(p_charcb);
+                else
+                {
+                    /* Keep this tecb in our list */
+                    p_charcb->next = charcb_list;
+                    charcb_list = p_charcb;
+                    p_charcb->nextchar = 0x21;
+                }
+            }
+			else
+			{
+                /* No memory to accept connection. Just accept and then close */
+                int sock;
+                struct sockaddr cliaddr;
+                socklen_t clilen;
+
+                sock = lwip_accept(listenfd, &cliaddr, &clilen);
+                if (sock >= 0)
+                    lwip_close(sock);
+            }
+        }
+        /* Go through list of connected clients and process data */
+        for (p_charcb = charcb_list; p_charcb; p_charcb = p_charcb->next)
+        {
+            if (FD_ISSET(p_charcb->socket, &readset))
+            {
+                /* This socket is ready for reading. This could be because someone typed
+                 * some characters or it could be because the socket is now closed. Try reading
+                 * some data to see. */
+                if (do_read(p_charcb) < 0)
+                    break;
+            }
+            if (FD_ISSET(p_charcb->socket, &writeset))
+            {
+                char line[80];
+                char setchar = p_charcb->nextchar;
+
+                for( i = 0; i < 59; i++)
+                {
+                    line[i] = setchar;
+                    if (++setchar == 0x7f)
+                        setchar = 0x21;
+                }
+                line[i] = 0;
+                strcat(line, "\n\r");
+                if (lwip_write(p_charcb->socket, line, strlen(line)) < 0)
+                {
+                    close_chargen(p_charcb);
+                    break;
+                }
+                if (++p_charcb->nextchar == 0x7f)
+                    p_charcb->nextchar = 0x21;
+            }
+        }
+    }
+}
+
+/**************************************************************
+ * void close_chargen(struct charcb *p_charcb)
+ *
+ * Close the socket and remove this charcb from the list.
+ **************************************************************/
+static void close_chargen(struct charcb *p_charcb)
+{
+    struct charcb *p_search_charcb;
+
+    /* Either an error or tcp connection closed on other
+     * end. Close here */
+    lwip_close(p_charcb->socket);
+
+    /* Free charcb */
+    if (charcb_list == p_charcb)
+        charcb_list = p_charcb->next;
+    else
+        for (p_search_charcb = charcb_list; p_search_charcb; p_search_charcb = p_search_charcb->next)
+        {
+            if (p_search_charcb->next == p_charcb)
+            {
+                p_search_charcb->next = p_charcb->next;
+                break;
+            }
+        }
+
+    rt_free(p_charcb);
+}
+
+/**************************************************************
+ * void do_read(struct charcb *p_charcb)
+ *
+ * Socket definitely is ready for reading. Read a buffer from the socket and
+ * discard the data.  If no data is read, then the socket is closed and the
+ * charcb is removed from the list and freed.
+ **************************************************************/
+static int do_read(struct charcb *p_charcb)
+{
+    char buffer[80];
+    int readcount;
+
+    /* Read some data */
+    readcount = lwip_read(p_charcb->socket, &buffer, 80);
+    if (readcount <= 0)
+    {
+        close_chargen(p_charcb);
+        return -1;
+    }
+    return 0;
+}
+
+void chargen_init(void)
+{
+	rt_thread_t chargen;
+
+	chargen = rt_thread_create(CHARGEN_THREAD_NAME,
+		chargen_thread, RT_NULL,
+		CHARGEN_THREAD_STACKSIZE,
+		CHARGEN_PRIORITY, 5);
+	if (chargen != RT_NULL) rt_thread_startup(chargen);
+}

+ 195 - 0
net/apps/ping.c

@@ -0,0 +1,195 @@
+/*
+ * 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 target - should be a "struct ip_addr" */
+#ifndef PING_TARGET
+#define PING_TARGET   (netif_default?netif_default->gw:ip_addr_any)
+#endif
+
+/** ping receive timeout - in milliseconds */
+#ifndef PING_RCV_TIMEO
+#define PING_RCV_TIMEO 1000
+#endif
+
+/** ping delay - in milliseconds */
+#ifndef PING_DELAY
+#define PING_DELAY     100
+#endif
+
+/** 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 result action - no default action */
+#ifndef PING_RESULT
+#define PING_RESULT(ping_ok)
+#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, struct ip_addr *addr)
+{
+	int err;
+	struct icmp_echo_hdr *iecho;
+	struct sockaddr_in to;
+	size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_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 ? ERR_OK : ERR_VAL);
+}
+
+static void ping_recv(int s)
+{
+	char buf[64];
+	int fromlen, len;
+	struct sockaddr_in from;
+	struct ip_hdr *iphdr;
+	struct icmp_echo_hdr *iecho;
+	struct _ip_addr *addr;
+
+	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)))
+		{
+			addr = (struct _ip_addr *)&(from.sin_addr);
+			rt_kprintf("ping: recv %d.%d.%d.%d\n", addr->addr0, addr->addr1, addr->addr2, addr->addr3);
+
+			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)))
+			{
+				/* do some ping result processing */
+				PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER));
+				return;
+			}
+			else
+			{
+				rt_kprintf("ping: drop\n");
+			}
+		}
+	}
+
+	if (len == 0)
+	{
+		LWIP_DEBUGF( PING_DEBUG, ("ping: recv - %lu ms - timeout\n", (sys_now()-ping_time)));
+	}
+
+	/* do some ping result processing */
+	PING_RESULT(0);
+}
+
+rt_err_t ping(char* target, rt_uint32_t time, rt_size_t size)
+{
+	int s;
+	int timeout = PING_RCV_TIMEO;
+	struct ip_addr ping_target;
+	rt_uint32_t send_time;
+	struct _ip_addr
+	{
+		rt_uint8_t addr0, addr1, addr2, addr3;
+	} *addr;
+
+    send_time = 0;
+
+	if (inet_aton(target, (struct in_addr*)&ping_target) == 0) return -RT_ERROR;
+	addr = (struct _ip_addr*)&ping_target;
+
+	if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0)
+	{
+	    rt_kprintf("create socket failled\n");
+		return -RT_ERROR;
+	}
+
+	lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+
+	while (1)
+	{
+		if (ping_send(s, &ping_target) == ERR_OK)
+		{
+			rt_kprintf("ping: send %d.%d.%d.%d\n", addr->addr0, addr->addr1, addr->addr2, addr->addr3);
+			ping_recv(s);
+		}
+		else
+		{
+			rt_kprintf("ping: send %d.%d.%d.%d - error\n", addr->addr0, addr->addr1, addr->addr2, addr->addr3);
+		}
+
+		send_time ++;
+		if (send_time > time) break; /* send ping times reached, stop */
+
+		rt_thread_delay(PING_DELAY); /* take a delay */
+	}
+
+	return RT_EOK;
+}
+#ifdef RT_USING_FINSH
+#include <finsh.h>
+FINSH_FUNCTION_EXPORT(ping, ping network host);
+#endif

+ 213 - 0
net/apps/sntp.c

@@ -0,0 +1,213 @@
+/**
+ * @file
+ * SNTP client module
+ *
+ */
+
+/*
+ * 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/sys.h"
+#include "lwip/sockets.h"
+
+#include <string.h>
+#include <time.h>
+
+/** This is an example of a "SNTP" client (with socket API).
+ *
+ * For a list of some public NTP servers, see this link :
+ * http://support.ntp.org/bin/view/Servers/NTPPoolServers
+ *
+ */
+
+/**
+ * SNTP_DEBUG: Enable debugging for SNTP.
+ */
+#ifndef SNTP_DEBUG
+#define SNTP_DEBUG                  LWIP_DBG_ON
+#endif
+
+/** SNTP server port */
+#ifndef SNTP_PORT
+#define SNTP_PORT                   123
+#endif
+
+/** SNTP server address as IPv4 address in "u32_t" format */
+#ifndef SNTP_SERVER_ADDRESS
+#define SNTP_SERVER_ADDRESS         inet_addr("213.161.194.93") /* pool.ntp.org */
+#endif
+
+/** SNTP receive timeout - in milliseconds */
+#ifndef SNTP_RECV_TIMEOUT
+#define SNTP_RECV_TIMEOUT           3000
+#endif
+
+/** SNTP update delay - in milliseconds */
+#ifndef SNTP_UPDATE_DELAY
+#define SNTP_UPDATE_DELAY           60000
+#endif
+
+/** SNTP macro to change system time and/or the update the RTC clock */
+#ifndef SNTP_SYSTEM_TIME
+#define SNTP_SYSTEM_TIME(t)
+#endif
+
+/* SNTP protocol defines */
+#define SNTP_MAX_DATA_LEN           48
+#define SNTP_RCV_TIME_OFS           32
+#define SNTP_LI_NO_WARNING          0x00
+#define SNTP_VERSION               (4/* NTP Version 4*/<<3)
+#define SNTP_MODE_CLIENT            0x03
+#define SNTP_MODE_SERVER            0x04
+#define SNTP_MODE_BROADCAST         0x05
+#define SNTP_MODE_MASK              0x07
+
+/* number of seconds between 1900 and 1970 */
+#define DIFF_SEC_1900_1970         (2208988800)
+
+/**
+ * SNTP processing
+ */
+static void sntp_process( time_t t)
+{
+	/* change system time and/or the update the RTC clock */
+	SNTP_SYSTEM_TIME(t);
+
+	/* display local time from GMT time */
+	LWIP_DEBUGF( SNTP_DEBUG, ("sntp_process: %s", ctime(&t)));
+}
+
+/**
+ * SNTP request
+ */
+static void sntp_request()
+{
+	int                sock;
+	struct sockaddr_in local;
+	struct sockaddr_in to;
+	int                tolen;
+	int                size;
+	int                timeout;
+	u8_t               sntp_request [SNTP_MAX_DATA_LEN];
+	u8_t               sntp_response[SNTP_MAX_DATA_LEN];
+	u32_t              sntp_server_address;
+	u32_t              timestamp;
+	time_t             t;
+
+	/* initialize SNTP server address */
+	sntp_server_address = SNTP_SERVER_ADDRESS;
+
+	/* if we got a valid SNTP server address... */
+	if (sntp_server_address!=0)
+	{
+		/* create new socket */
+		sock = socket( AF_INET, SOCK_DGRAM, 0);
+		if (sock>=0)
+		{
+			/* prepare local address */
+			memset(&local, 0, sizeof(local));
+			local.sin_family      = AF_INET;
+			local.sin_port        = htons(INADDR_ANY);
+			local.sin_addr.s_addr = htonl(INADDR_ANY);
+
+			/* bind to local address */
+			if (bind( sock, (struct sockaddr *)&local, sizeof(local))==0)
+			{
+				/* set recv timeout */
+				timeout = SNTP_RECV_TIMEOUT;
+				setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
+
+				/* prepare SNTP request */
+				memset( sntp_request, 0, sizeof(sntp_request));
+				sntp_request[0] = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
+
+				/* prepare SNTP server address */
+				memset(&to, 0, sizeof(to));
+				to.sin_family      = AF_INET;
+				to.sin_port        = htons(SNTP_PORT);
+				to.sin_addr.s_addr = sntp_server_address;
+
+				/* send SNTP request to server */
+				if (sendto( sock, sntp_request, sizeof(sntp_request), 0, (struct sockaddr *)&to, sizeof(to))>=0)
+				{
+					/* receive SNTP server response */
+					tolen = sizeof(to);
+					size  = recvfrom( sock, sntp_response, sizeof(sntp_response), 0, (struct sockaddr *)&to, (socklen_t *)&tolen);
+
+					/* if the response size is good */
+					if (size == SNTP_MAX_DATA_LEN)
+					{
+						/* if this is a SNTP response... */
+						if (((sntp_response[0] & SNTP_MODE_MASK) == SNTP_MODE_SERVER) || ((sntp_response[0] & SNTP_MODE_MASK) == SNTP_MODE_BROADCAST))
+						{
+							/* extract GMT time from response */
+							SMEMCPY( &timestamp, (sntp_response+SNTP_RCV_TIME_OFS), sizeof(timestamp));
+							t = (ntohl(timestamp) - DIFF_SEC_1900_1970);
+
+							/* do time processing */
+							sntp_process(t);
+
+						}
+						else
+						{
+							LWIP_DEBUGF( SNTP_DEBUG, ("sntp_request: not response frame code\n"));
+						}
+					}
+					else
+					{
+						LWIP_DEBUGF( SNTP_DEBUG, ("sntp_request: not recvfrom==%i\n", errno));
+					}
+				}
+				else
+				{
+					LWIP_DEBUGF( SNTP_DEBUG, ("sntp_request: not sendto==%i\n", errno));
+				}
+			}
+			/* close the socket */
+			closesocket(sock);
+		}
+	}
+}
+
+/**
+ * SNTP thread
+ */
+static void
+sntp_thread(void *arg)
+{
+	LWIP_UNUSED_ARG(arg);
+	while(1)
+	{
+		sntp_request();
+		sys_msleep(SNTP_UPDATE_DELAY);
+	}
+}
+
+void sntp_init(void)
+{
+	sys_thread_new("sntp_thread", sntp_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
+}

+ 171 - 0
net/apps/tftp.c

@@ -0,0 +1,171 @@
+#include <dfs_posix.h>
+#include <lwip/sockets.h>
+
+#include <finsh.h>
+
+#define TFTP_PORT	69
+/* opcode */
+#define TFTP_RRQ			1 	/* read request */
+#define TFTP_WRQ			2	/* write request */
+#define TFTP_DATA			3	/* data */
+#define TFTP_ACK			4	/* ACK */
+#define TFTP_ERROR			5	/* error */
+
+rt_uint8_t tftp_buffer[512 + 4];
+/* tftp client */
+void tftp_get(const char* host, const char* filename)
+{
+	int fd, sock_fd;
+	struct sockaddr_in tftp_addr, from_addr;
+	rt_uint32_t length;
+	socklen_t fromlen;
+
+	/* make local file name */
+	getcwd((char*)tftp_buffer, sizeof(tftp_buffer));
+	strcat((char*)tftp_buffer, "/");
+	strcat((char*)tftp_buffer, filename);
+
+	/* open local file for write */
+	fd = open((char*)tftp_buffer, O_RDWR, 0);
+	if (fd < 0)
+	{
+		rt_kprintf("can't open local filename\n");
+		return;
+	}
+
+	/* connect to tftp server */
+    inet_aton(host, (struct in_addr*)&(tftp_addr.sin_addr));
+    tftp_addr.sin_family = AF_INET;
+    tftp_addr.sin_port = htons(TFTP_PORT);
+    
+    sock_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
+    if (sock_fd < 0)
+	{
+	    close(fd);
+	    rt_kprintf("can't create a socket\n");
+	    return ;
+	}
+	
+	/* make tftp request */
+	tftp_buffer[0] = 0;			/* opcode */
+	tftp_buffer[1] = TFTP_RRQ; 	/* RRQ */
+	length = rt_sprintf((char *)&tftp_buffer[2], "%s", filename) + 2;
+	tftp_buffer[length] = 0; length ++;
+	length += rt_sprintf((char*)&tftp_buffer[length], "%s", "octet");
+	tftp_buffer[length] = 0; length ++;
+
+	fromlen = sizeof(struct sockaddr_in);
+	
+	/* send request */	
+	lwip_sendto(sock_fd, tftp_buffer, length, 0, 
+		(struct sockaddr *)&tftp_addr, fromlen);
+	
+	do
+	{
+		length = lwip_recvfrom(sock_fd, tftp_buffer, sizeof(tftp_buffer), 0, 
+			(struct sockaddr *)&from_addr, &fromlen);
+		
+		if (length > 0)
+		{
+			write(fd, &tftp_buffer[4], length - 4);
+			rt_kprintf("#");
+
+			/* make ACK */			
+			tftp_buffer[0] = 0; tftp_buffer[1] = TFTP_ACK; /* opcode */
+			/* send ACK */
+			lwip_sendto(sock_fd, tftp_buffer, 4, 0, 
+				(struct sockaddr *)&from_addr, fromlen);
+		}
+	} while (length != 516);
+	
+	close(fd);
+	lwip_close(sock_fd);
+}
+FINSH_FUNCTION_EXPORT(tftp_get, get file from tftp server);
+
+void tftp_put(const char* host, const char* filename)
+{
+	int fd, sock_fd;
+	struct sockaddr_in tftp_addr, from_addr;
+	rt_uint32_t length, block_number = 0;
+	socklen_t fromlen;
+
+	/* make local file name */
+	getcwd((char*)tftp_buffer, sizeof(tftp_buffer));
+	strcat((char*)tftp_buffer, "/");
+	strcat((char*)tftp_buffer, filename);
+
+	/* open local file for write */
+	fd = open((char*)tftp_buffer, O_RDONLY, 0);
+	if (fd < 0)
+	{
+		rt_kprintf("can't open local filename\n");
+		return;
+	}
+
+	/* connect to tftp server */
+    inet_aton(host, (struct in_addr*)&(tftp_addr.sin_addr));
+    tftp_addr.sin_family = AF_INET;
+    tftp_addr.sin_port = htons(TFTP_PORT);
+    tftp_addr.sin_addr.s_addr = htonl(tftp_addr.sin_addr.s_addr);
+    
+    sock_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
+    if (sock_fd < 0)
+	{
+	    close(fd);
+	    rt_kprintf("can't create a socket\n");
+	    return ;
+	}
+	
+	/* make tftp request */
+	tftp_buffer[0] = 0;			/* opcode */
+	tftp_buffer[1] = TFTP_WRQ; 	/* WRQ */
+	length = rt_sprintf((char *)&tftp_buffer[2], "%s", filename) + 2;
+	tftp_buffer[length] = 0; length ++;
+	length += rt_sprintf((char*)&tftp_buffer[length], "%s", "octet");
+	tftp_buffer[length] = 0; length ++;
+
+	fromlen = sizeof(struct sockaddr_in);
+	
+	/* send request */	
+	lwip_sendto(sock_fd, tftp_buffer, length, 0, 
+		(struct sockaddr *)&tftp_addr, fromlen);
+
+	/* wait ACK 0 */	
+	length = lwip_recvfrom(sock_fd, tftp_buffer, sizeof(tftp_buffer), 0, 
+		(struct sockaddr *)&from_addr, &fromlen);
+	if (!(tftp_buffer[0] == 0 &&
+		tftp_buffer[1] == TFTP_ACK &&
+		tftp_buffer[2] == 0 &&
+		tftp_buffer[3] == 0))
+	{
+		rt_kprintf("tftp server error\n");
+		close(fd);
+		return;
+	}
+
+	do
+	{
+		length = read(fd, &tftp_buffer[4], 512);
+		if (length > 0)
+		{
+			/* make opcode and block number */
+			tftp_buffer[0] = 0; tftp_buffer[1] = TFTP_DATA;
+			tftp_buffer[2] = block_number ; tftp_buffer[3] = block_number;
+
+			lwip_sendto(sock_fd, tftp_buffer, length + 4, 0, 
+				(struct sockaddr *)&from_addr, fromlen);
+		}
+
+		/* receive ack */
+		length = lwip_recvfrom(sock_fd, tftp_buffer, sizeof(tftp_buffer), 0, 
+			(struct sockaddr *)&from_addr, &fromlen);
+		if (length > 0)
+		{
+		}
+	} while (length != 516);
+	
+	close(fd);
+	lwip_close(sock_fd);
+}
+FINSH_FUNCTION_EXPORT(tftp_put, put file to tftp server);