Browse Source

tftp: implement TFTP server and client

Fix: packet size and typo in transfer mode main -> mail
cc430
DipSwitch 7 years ago
parent
commit
ede923bc5c
  1. 5
      Makefile.dep
  2. 42
      examples/gnrc_tftp/Makefile
  3. 86
      examples/gnrc_tftp/README.md
  4. 56
      examples/gnrc_tftp/main.c
  5. 216
      examples/gnrc_tftp/tftp_client.c
  6. 191
      examples/gnrc_tftp/tftp_server.c
  7. 206
      sys/include/net/gnrc/tftp.h
  8. 3
      sys/net/gnrc/Makefile
  9. 3
      sys/net/gnrc/application_layer/tftp/Makefile
  10. 1139
      sys/net/gnrc/application_layer/tftp/gnrc_tftp.c

5
Makefile.dep

@ -43,6 +43,11 @@ ifneq (,$(filter gnrc_zep,$(USEMODULE)))
USEMODULE += vtimer
endif
ifneq (,$(filter gnrc_tftp,$(USEMODULE)))
USEMODULE += gnrc_udp
USEMODULE += xtimer
endif
ifneq (,$(filter gnrc_rpl,$(USEMODULE)))
USEMODULE += fib
USEMODULE += gnrc_ipv6_router_default

42
examples/gnrc_tftp/Makefile

@ -0,0 +1,42 @@
# name of your application
APPLICATION = gnrc_tftp
# If no BOARD is found in the environment, use this default:
BOARD ?= native
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..
BOARD_INSUFFICIENT_MEMORY := airfy-beacon chronos msb-430 msb-430h nrf51dongle \
nrf6310 nucleo-f334 pca10000 pca10005 spark-core \
stm32f0discovery telosb weio wsn430-v1_3b wsn430-v1_4 \
yunjia-nrf51822 z1
# Include packages that pull up and auto-init the link layer.
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
USEMODULE += gnrc_netif_default
USEMODULE += auto_init_gnrc_netif
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_router_default
USEMODULE += gnrc_udp
# Add a routing protocol
USEMODULE += gnrc_rpl
# This application dumps received packets to STDIO using the pktdump module
USEMODULE += gnrc_pktdump
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
USEMODULE += gnrc_tftp
# Add also the shell, some shell commands
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
CFLAGS += -DDEVELHELP
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include

86
examples/gnrc_tftp/README.md

@ -0,0 +1,86 @@
# gnrc_tftp example
## Connecting RIOT native and the Linux host
> **Note:** RIOT does not support IPv4, so you need to stick to IPv6 anytime. To establish a connection between RIOT and the Linux host,
you will need `tftp` (with IPv6 support). On Ubuntu and Debian you would need the package `tftp-hpa` for TFTP client and `tftpd-hpa` for TFTP server.
Be aware that many programs require you to add an option such as -6 to tell them to use IPv6, otherwise they
will fail. If you're using a _Raspberry Pi_, run `sudo modprobe ipv6` before trying this example, because raspbian does not load the
IPv6 module automatically.
On some systems (openSUSE for example), the _firewall_ may interfere, and prevent some packets to arrive at the application (they will
however show up in Wireshark, which can be confusing). So be sure to adjust your firewall rules, or turn it off (who needs security anyway).
First, create a tap interface (to which RIOT will connect) and a bridge (to which Linux will connect) from the RIOT main directory run:
./dist/tools/tapsetup/tapsetup -c 1
Now you can start the `gnrc_tftp` example by invoking `make term`. This should automatically connect to the `tap0` interface. If
this doesn't work for some reason, run `make` without any arguments, and then run the binary manually like so (assuming you are in the `examples/gnrc_tftp` directory):
To verify that there is connectivity between RIOT and Linux, go to the RIOT console and run `ifconfig`:
> ifconfig
Iface 6 HWaddr: 7e:ed:d2:ee:e1:07
MTU:1280
Source address length: 6
Link type: wired
inet6 addr: ff02::1/128 scope: local [multicast]
inet6 addr: fe80::7ced:d2ff:feee:e107/64 scope: local
inet6 addr: ff02::1:ffee:e107/128 scope: local [multicast]
Copy the [link-local address](https://en.wikipedia.org/wiki/Link-local_address) of the RIOT node (prefixed with `fe80`) and try to ping it **from the Linux node**:
ping6 fe80::7ced:d2ff:feee:e107%tapbr0
Note that the interface on which to send the ping needs to be appended to the IPv6 address, `%tapbr0` in the above example. When talking to the RIOT node, you always want to send to/receive from the `tapbr0` interface.
If the pings succeed you can go on to send UDP packets. To do that, first start a UDP server on the RIOT node:
> tftps start
tftp_server: Starting TFTP service at port 69
Now, on the Linux host, you can run tftp to connect with RIOT's TFTP server:
$ tftp -v -6 fe80::7ced:d2ff:feee:e107%tapbr0 -c get welcome.txt
The output will be something like:
Connected to fe80::7ced:d2ff:feee:e107%tapbr0 (fe80::7ced:d2ff:feee:e107), port 69
getting from fe80::7ced:d2ff:feee:e107%tapbr0:welcome.txt to welcome.txt [netascii]
Received 94 bytes in 0.0 seconds [113425 bit/s]
The `-6` option is necessary to tell tftp to use IPv6 only and the `-v` is to tell the tftp client to be verbose.
You should now see that the TFTP messages are received on the RIOT side. Opening a TFTP server on the Linux side is also possible. To do that, write down the IP address of the host (run on Linux):
ifconfig tapbr0
tapbr0 Link encap:Ethernet HWaddr 0e:bc:0f:49:7f:e4
inet6 addr: fe80::cbc:fff:fe49:7fe4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:22 errors:0 dropped:0 overruns:0 frame:0
TX packets:53 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1928 (1.9 KB) TX bytes:6217 (6.2 KB)
And start the tftp server on Linux:
$ sudo /etc/init.d/tftpd-hpa stop && mkdir -p tftpserv && echo "hello world" > tftpserv/welcome.txt && sudo in.tftpd -vvv -L -6 -c -s -u ${USER} ./tftpserv
Now, on the RIOT side, send a UDP packet using:
tftpc get welcome.txt octet 1 fe80::cbc:fff:fe49:7fe4
You will get output that looks like this:
tftp_client: bin read welcome.txt:12
-- CLIENT DATA --
hello worl
-- CLIENT DATA --
-- CLIENT DATA --
d
-- CLIENT DATA --
tftp_client: SUCCESS: (null)

56
examples/gnrc_tftp/main.c

@ -0,0 +1,56 @@
/*
* Copyright (C) 2015 Engineering-Spirit
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
* @brief Example application for demonstrating the RIOT TFTP stack
*
* @author Nick van IJzendoorn <nijzendoorn@engineering-spirit.nl>
*
* @}
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "shell.h"
#include "msg.h"
extern int tftp_client_cmd(int argc, char * *argv);
extern int tftp_server_cmd(int argc, char * *argv);
#define MAIN_QUEUE_SIZE (4)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
static const shell_command_t shell_commands[] = {
{ "tftpc", "get/put data to a TFTP server", tftp_client_cmd },
{ "tftps", "start and stop the TFTP server", tftp_server_cmd },
{ NULL, NULL, NULL }
};
int main(void)
{
/* we need a message queue for the thread running the shell in order to
* receive potentially fast incoming networking packets */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
puts("RIOT TFTP stack example application");
/* start shell */
puts("All up, running the shell now");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
/* should be never reached */
return 0;
}

216
examples/gnrc_tftp/tftp_client.c

@ -0,0 +1,216 @@
/*
* Copyright (C) 2015 Engineering-Spirit
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
* @brief Demonstrating the sending and receiving of data via the TFTP client
*
* @author Nick van IJzendoorn <nijzendoorn@engineering-spirit.nl>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "kernel.h"
#include "net/gnrc/tftp.h"
static const char *_tftp_default_host = "::1";
/* default server text which can be received */
static const char _tftp_client_hello[] = "Hello,\n"
"\n"
"Client text would also need to exist to be able to put data.\n"
"\n"
"Enjoy the RIOT-OS\n";
static tftp_action_t _tftp_action;
/**
* @brief called at every transaction start
*/
static bool _tftp_client_start_cb(tftp_action_t action, tftp_mode_t mode,
const char *file_name, size_t *len)
{
/* translate the mode */
const char *str_mode = "ascii";
if (mode == TTM_OCTET) {
str_mode = "bin";
}
else if (mode == TTM_MAIL) {
str_mode = "mail";
}
/* translate the action */
const char *str_action = "read";
if (action == TFTP_WRITE) {
str_action = "write";
}
/* display the action being performed */
printf("tftp_client: %s %s %s:%u\n", str_mode, str_action, file_name, *len);
/* return the length of the text, if this is an read action */
if (action == TFTP_READ) {
*len = sizeof(_tftp_client_hello);
}
/* remember the action of the current transfer */
_tftp_action = action;
/* we accept the transfer to take place so we return true */
return true;
}
/**
* @brief called to get or put data, depending on the mode received by `_tftp_start_cb(action, ...)`
*/
static int _tftp_client_data_cb(uint32_t offset, void *data, size_t data_len)
{
char *c = (char *) data;
/* if we are reading return the part of the data that is being requested */
if (_tftp_action == TFTP_WRITE) {
/* calculate the length of the data block to transfer */
if (offset + data_len > sizeof(_tftp_client_hello)) {
data_len -= (offset + data_len) - sizeof(_tftp_client_hello);
}
/* copy the block to the output buffer */
memcpy(data, _tftp_client_hello + offset, data_len);
}
else {
/* we received a data block which we output to the console */
printf("\n -- CLIENT DATA --\n%.*s\n -- CLIENT DATA --\n", data_len, c);
}
/* return the length of the data block */
return data_len;
}
/**
* @brief the transfer has stopped, see the event argument to determined if it was successful
* or not.
*/
static void _tftp_client_stop_cb(tftp_event_t event, const char *msg)
{
/* decode the stop event received */
const char *cause = "UNKOWN";
if (event == TFTP_SUCCESS) {
cause = "SUCCESS";
}
else if (event == TFTP_PEER_ERROR) {
cause = "ERROR From Client";
}
else if (event == TFTP_INTERN_ERROR) {
cause = "ERROR Internal Server Error";
}
/* print the transfer result to the console */
printf("tftp_client: %s: %s\n", cause, msg);
}
static int _tftp_client_cmd(int argc, char * *argv)
{
ipv6_addr_t ip;
const char *file_name = argv[2];
tftp_mode_t mode = TTM_OCTET;
bool use_options = true;
ipv6_addr_from_str(&ip, _tftp_default_host);
if (argc >= 3 && argc <= 6) {
/* decode the action */
if (strcmp(argv[1], "get") == 0) {
_tftp_action = TFTP_READ;
}
else if (strcmp(argv[1], "put") == 0) {
_tftp_action = TFTP_WRITE;
}
else {
return -1;
}
/* get the transfer mode */
if (argc >= 4) {
if (strcmp(argv[3], "octet") == 0) {
mode = TTM_OCTET;
}
else if (strcmp(argv[3], "ascii") == 0) {
mode = TTM_ASCII;
}
else if (strcmp(argv[3], "mail") == 0) {
mode = TTM_MAIL;
}
else {
puts("tftp: couldn't parse the TFTP transfer mode");
return -1;
}
}
/* decode if we must use the TFTP option extension or not */
if (argc >= 5) {
if (strcmp(argv[4], "0") == 0) {
use_options = false;
}
else if (strcmp(argv[4], "1") == 0) {
use_options = true;
}
else {
puts("tftp: invalid options choose 0 or 1");
return -1;
}
}
/* decode the address */
if (argc >= 6) {
if (!ipv6_addr_from_str(&ip, argv[5])) {
puts("tftp: invalid IP address");
return -1;
}
}
}
else {
return -1;
}
if (_tftp_action == TFTP_READ) {
puts("tftp: starting read request");
gnrc_tftp_client_read(&ip, file_name, mode, _tftp_client_data_cb,
_tftp_client_start_cb, _tftp_client_stop_cb, use_options);
}
else if (_tftp_action == TFTP_WRITE) {
puts("tftp: starting write request");
gnrc_tftp_client_write(&ip, file_name, mode, _tftp_client_data_cb,
sizeof(_tftp_client_hello), _tftp_client_stop_cb, use_options);
}
return 0;
}
/**
* @brief start the TFTP server by creating a thread
*/
int tftp_client_cmd(int argc, char * *argv)
{
if (_tftp_client_cmd(argc, argv) < 0) {
printf("usage: %s <action[get|put]> <file_name> <mode[ascii | octet(default) | mail]>\n"
"\t<use_options[1 (default) | 0]> <addr(default: ::1)>\n", argv[0]);
}
return 0;
}

191
examples/gnrc_tftp/tftp_server.c

@ -0,0 +1,191 @@
/*
* Copyright (C) 2015 Engineering-Spirit
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
* @brief Demonstrating the sending and receiving of data via the TFTP server
*
* @author Nick van IJzendoorn <nijzendoorn@engineering-spirit.nl>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include "thread.h"
#include "net/gnrc/tftp.h"
/* the message queues */
#define TFTP_QUEUE_SIZE (4)
static msg_t _tftp_msg_queue[TFTP_QUEUE_SIZE];
/* allocate the stack */
char _tftp_stack[THREAD_STACKSIZE_MAIN + THREAD_EXTRA_STACKSIZE_PRINTF];
/* default server text which can be received */
static const char _tftp_server_hello[] = "Hello world,\n"
"\n"
"Welcome to the wonderful world of IoT and embedded systems.\n"
"\n"
"Enjoy the RIOT-OS\n";
static tftp_action_t _tftp_action;
/**
* @brief called at every transcation start
*/
static bool _tftp_server_start_cb(tftp_action_t action, tftp_mode_t mode,
const char *file_name, size_t *len)
{
/* translate the mode */
const char *str_mode = "ascii";
if (mode == TTM_OCTET) {
str_mode = "bin";
}
else if (mode == TTM_MAIL) {
str_mode = "mail";
}
/* translate the action */
const char *str_action = "read";
if (action == TFTP_WRITE) {
str_action = "write";
}
/* display the action being performed */
printf("tftp_server: %s %s %s:%u\n", str_mode, str_action, file_name, *len);
/* return the length of the text, if this is an read action */
if (action == TFTP_READ) {
*len = sizeof(_tftp_server_hello);
}
/* remember the action of the current transfer */
_tftp_action = action;
/* we accept the transfer to take place so we return true */
return true;
}
/**
* @brief called to get or put data, depending on the mode received by `_tftp_start_cb(action, ...)`
*/
static int _tftp_server_data_cb(uint32_t offset, void *data, size_t data_len)
{
char *c = (char *) data;
/* if we are reading return the part of the data that is being requested */
if (_tftp_action == TFTP_READ) {
/* calculate the length of the data block to transfer */
if (offset + data_len > sizeof(_tftp_server_hello)) {
data_len -= (offset + data_len) - sizeof(_tftp_server_hello);
}
/* copy the block to the output buffer */
memcpy(data, _tftp_server_hello + offset, data_len);
}
else {
/* we received a data block which we output to the console */
printf("\n -- SERVER DATA --\n%.*s\n -- SERVER DATA --\n", data_len, c);
}
/* return the length of the data block */
return data_len;
}
/**
* @brief the transfer has stopped, see the event argument to determined if it was successful
* or not.
*/
static void _tftp_server_stop_cb(tftp_event_t event, const char *msg)
{
/* decode the stop event received */
const char *cause = "UNKOWN";
if (event == TFTP_SUCCESS) {
cause = "SUCCESS";
}
else if (event == TFTP_PEER_ERROR) {
cause = "ERROR From Client";
}
else if (event == TFTP_INTERN_ERROR) {
cause = "ERROR Internal Server Error";
}
/* print the transfer result to the console */
printf("tftp_server: %s: %s\n", cause, (msg == NULL) ? "NULL" : msg);
}
/**
* @brief the TFTP server thread
*/
void *tftp_server_wrapper(void *arg)
{
(void)arg;
/* A message queue is needed to register for incoming packets */
msg_init_queue(_tftp_msg_queue, TFTP_QUEUE_SIZE);
/* inform the user */
puts("tftp_server: Starting TFTP service at port 69");
/* run the TFTP server */
gnrc_tftp_server(_tftp_server_data_cb, _tftp_server_start_cb, _tftp_server_stop_cb, true);
/* the TFTP server has been stopped */
puts("tftp_server: Stopped TFTP service");
return NULL;
}
/**
* @brief start the TFTP server by creating a thread
*/
void tftp_server_start(void)
{
thread_create(_tftp_stack, sizeof(_tftp_stack),
1, CREATE_WOUT_YIELD | CREATE_STACKTEST,
tftp_server_wrapper, NULL, "TFTP Server");
}
/**
* @brief stop the TFTP server by sending a message to the thread
*/
void tftp_server_stop(void)
{
gnrc_tftp_server_stop();
}
int tftp_server_cmd(int argc, char * *argv)
{
switch (argc) {
case 2:
if (strcmp(argv[1], "start") == 0) {
tftp_server_start();
return 0;
}
else if (strcmp(argv[1], "stop") == 0) {
tftp_server_stop();
return 0;
}
/* no break */
default:
printf("usage: %s [start|stop]\n", argv[0]);
return 0;
}
return 0;
}

206
sys/include/net/gnrc/tftp.h

@ -0,0 +1,206 @@
/*
* Copyright (C) 2015 Nick van IJzendoorn <nijzendoorn@engineering-spirit.nl>
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for
* more details.
*/
/**
* @defgroup net_gnrc_tftp TFTP Support Library
* @ingroup net_gnrc
* @brief Add's support for TFTP protocol parsing
* @{
*
* @file
* @brief TFTP support library
*
* The TFTP module add's support for the TFTP protocol.
* It implements the following RFC's:
* - https://tools.ietf.org/html/rfc1350
* (RFC1350 The TFTP Protocol (Revision 2)
*
* - https://tools.ietf.org/html/rfc2347
* (RFC2347 TFTP Option Extension)
*
* - https://tools.ietf.org/html/rfc2348
* (RFC2348 TFTP Blocksize Option)
*
* - https://tools.ietf.org/html/rfc2349
* (RFC2349 TFTP Timeout Interval and Transfer Size Options)
*
* @author Nick van IJzendoorn <nijzendoorn@engineering-spirit.nl>
*/
#ifndef GNRC_TFTP_H_
#define GNRC_TFTP_H_
#include <inttypes.h>
#include "byteorder.h"
#include "kernel_types.h"
#include "net/ipv6/addr.h"
#include "net/gnrc/nettype.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The maximum allowed length of the transfer filename
*/
#ifndef GNRC_TFTP_MAX_FILENAME_LEN
#define GNRC_TFTP_MAX_FILENAME_LEN (64)
#endif
/**
* @brief The base source port to be used by TFTP
*/
#ifndef GNRC_TFTP_DEFAULT_SRC_PORT
#define GNRC_TFTP_DEFAULT_SRC_PORT (10690)
#endif
/**
* @brief The default destination port of the TFTP server
*/
#ifndef GNRC_TFTP_DEFAULT_DST_PORT
#define GNRC_TFTP_DEFAULT_DST_PORT (69)
#endif
/**
* @brief The maximum allowed data bytes in the data packet
*/
#ifndef GNRC_TFTP_MAX_TRANSFER_UNIT
#define GNRC_TFTP_MAX_TRANSFER_UNIT (512)
#endif
/**
* @brief The number of retries that must be made before stopping a transfer
*/
#ifndef GNRC_TFTP_MAX_RETRIES
#define GNRC_TFTP_MAX_RETRIES (5)
#endif
/**
* @brief The default timeout of a data packet
*/
#ifndef GNRC_TFTP_DEFAULT_TIMEOUT
#define GNRC_TFTP_DEFAULT_TIMEOUT (1 * SEC_IN_USEC)
#endif
/**
* @brief TFTP action to perform
*/
typedef enum {
TFTP_READ,
TFTP_WRITE
} tftp_action_t;
/**
* @brief TFTP Transfer modes
*/
typedef enum {
TTM_ASCII,
TTM_OCTET,
TTM_MAIL
} tftp_mode_t;
/**
* @brief TFTP stop / finish events
*/
typedef enum {
TFTP_SUCCESS, /**< The transfer was successful */
TFTP_PEER_ERROR, /**< The peer send the given error */
TFTP_INTERN_ERROR /**< There was an internal error */
} tftp_event_t;
/**
* @brief callback define which is called when a new server request is placed
* or when an client read request is made and the data length option is received
*
* @param [in] action The action the transfer want to perform
* @param [in] mode The data mode of the transfer
* @param [in] file_name The filename of the file being transfered
* @param [in/out] data_len When a read action is performed, the application must give
* the total transfer size of the data. When a write action
* is performed the total transfer size will be given.
*/
typedef bool (*tftp_start_cb_t)(tftp_action_t action, tftp_mode_t mode,
const char *file_name, size_t *data_len);
/**
* @brief callback define which is called to get or set data from/to the user application
*/
typedef int (*tftp_data_cb_t)(uint32_t offset, void *data, size_t data_len);
/**
* @brief callback define which is called when an transfer is stopped
*/
typedef void (*tftp_stop_cb_t)(tftp_event_t event, const char *msg);
/**
* @brief Start the TFTP server
*
* @param [in] data_cb called for each read data block
* @param [in] start_cb called if a new client connection is requested
* @param [in] stop_cb called if the transfer has finished
* @param [in] use_options when set the client uses the option extensions
*
* @return 1 on success
* @return -1 on failure
*/
int gnrc_tftp_server(tftp_data_cb_t data_cb, tftp_start_cb_t start_cb, tftp_stop_cb_t stop_cb, bool use_options);
/**
* @brief Stop the TFTP server
*
* @return 1 on success
* @return -1 on failure
*/
int gnrc_tftp_server_stop(void);
/**
* @brief Start an TFTP client read action from the given destination
*
* @param [in] addr the address of the server
* @param [in] file_name the filename of the file to get
* @param [in] mode the transfer mode
* @param [in] data_cb called for each read data block
* @param [in] start_cb called if the server returns the transfer_size option
* @param [in] stop_cb called if the transfer has finished
* @param [in] use_option when set the client uses the option extensions
*
* @return 1 on success
* @return -1 on failure
*/
int gnrc_tftp_client_read(ipv6_addr_t *addr, const char *file_name, tftp_mode_t mode,
tftp_data_cb_t data_cb, tftp_start_cb_t start_cb, tftp_stop_cb_t stop_cb,
bool use_option);
/**
* @brief Start an TFTP client write action to the given destination
*
* @param [in] addr the address of the server
* @param [in] file_name the filename of the file to write
* @param [in] mode the transfer mode
* @param [in] data_cb called to store the received block
* @param [in] total_size the total size of the transfer
* @param [in] stop_cb called if the server returns the transfer_size option
* @param [in] use_option when set the client uses the option extensions
*
* @return 1 on success
* @return -1 on failure
*/
int gnrc_tftp_client_write(ipv6_addr_t *addr, const char *file_name, tftp_mode_t mode,
tftp_data_cb_t data_cb, size_t total_size, tftp_stop_cb_t stop_cb,
bool use_option);
#ifdef __cplusplus
}
#endif
#endif /* GNRC_TFTP_H_ */
/**
* @}
*/

3
sys/net/gnrc/Makefile

@ -109,5 +109,8 @@ endif
ifneq (,$(filter gnrc_zep,$(USEMODULE)))
DIRS += application_layer/zep
endif
ifneq (,$(filter gnrc_tftp,$(USEMODULE)))
DIRS += application_layer/tftp
endif
include $(RIOTBASE)/Makefile.base

3
sys/net/gnrc/application_layer/tftp/Makefile

@ -0,0 +1,3 @@
MODULE = gnrc_tftp
include $(RIOTBASE)/Makefile.base

1139
sys/net/gnrc/application_layer/tftp/gnrc_tftp.c

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save