diff --git a/Makefile.dep b/Makefile.dep index 548d75b474..a9dd641507 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -143,11 +143,6 @@ ifneq (,$(filter netdev_tap,$(USEMODULE))) USEMODULE += iolist endif -ifneq (,$(filter gnrc_tftp,$(USEMODULE))) - USEMODULE += gnrc_udp - USEMODULE += xtimer -endif - ifneq (,$(filter gnrc_rpl_p2p,$(USEMODULE))) USEMODULE += gnrc_rpl endif diff --git a/examples/gnrc_tftp/Makefile b/examples/gnrc_tftp/Makefile deleted file mode 100644 index 1f54dd427d..0000000000 --- a/examples/gnrc_tftp/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -# name of your application -APPLICATION = gnrc_tftp_example - -# 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)/../.. - -# 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_netdev_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: -DEVELHELP ?= 1 - -# Change this to 0 show compiler invocation lines by default: -QUIET ?= 1 - -include $(RIOTBASE)/Makefile.include - -# Set a custom channel if needed -include $(RIOTMAKE)/default-radio-settings.inc.mk diff --git a/examples/gnrc_tftp/Makefile.ci b/examples/gnrc_tftp/Makefile.ci deleted file mode 100644 index 5837815daa..0000000000 --- a/examples/gnrc_tftp/Makefile.ci +++ /dev/null @@ -1,50 +0,0 @@ -BOARD_INSUFFICIENT_MEMORY := \ - airfy-beacon \ - arduino-duemilanove \ - arduino-leonardo \ - arduino-mega2560 \ - arduino-nano \ - arduino-uno \ - atmega1284p \ - atmega328p \ - b-l072z-lrwan1 \ - blackpill \ - bluepill \ - calliope-mini \ - chronos \ - derfmega128 \ - hifive1 \ - hifive1b \ - i-nucleo-lrwan1 \ - mega-xplained \ - microbit \ - microduino-corerf \ - msb-430 \ - msb-430h \ - nrf51dk \ - nrf51dongle \ - nrf6310 \ - nucleo-f030r8 \ - nucleo-f031k6 \ - nucleo-f042k6 \ - nucleo-f070rb \ - nucleo-f072rb \ - nucleo-f103rb \ - nucleo-f302r8 \ - nucleo-f303k8 \ - nucleo-f334r8 \ - nucleo-l031k6 \ - nucleo-l053r8 \ - saml10-xpro \ - saml11-xpro \ - spark-core \ - stm32f030f4-demo \ - stm32f0discovery \ - stm32l0538-disco \ - telosb \ - waspmote-pro \ - wsn430-v1_3b \ - wsn430-v1_4 \ - yunjia-nrf51822 \ - z1 \ - # diff --git a/examples/gnrc_tftp/README.md b/examples/gnrc_tftp/README.md deleted file mode 100644 index 19b5a499a9..0000000000 --- a/examples/gnrc_tftp/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# 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: - - sudo ./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) diff --git a/examples/gnrc_tftp/main.c b/examples/gnrc_tftp/main.c deleted file mode 100644 index e287aa2126..0000000000 --- a/examples/gnrc_tftp/main.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 - * - * @} - */ - -#include -#include -#include -#include - -#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; -} diff --git a/examples/gnrc_tftp/tftp_client.c b/examples/gnrc_tftp/tftp_client.c deleted file mode 100644 index 013a5a7930..0000000000 --- a/examples/gnrc_tftp/tftp_client.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * 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 - * - * @} - */ - -#include -#include -#include - -#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:%lu\n", str_mode, str_action, file_name, (unsigned long)*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", (int)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 = "UNKNOWN"; - - 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 */ - if (msg != NULL) { - printf("tftp_client: %s: %s\n", cause, msg); - } - else { - printf("tftp_client: %s\n", cause); - } -} - -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 \n" - "\t \n", argv[0]); - } - - return 0; -} diff --git a/examples/gnrc_tftp/tftp_server.c b/examples/gnrc_tftp/tftp_server.c deleted file mode 100644 index 96b025c71d..0000000000 --- a/examples/gnrc_tftp/tftp_server.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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 - * - * @} - */ - -#include -#include -#include -#include - -#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:%lu\n", str_mode, str_action, file_name, (unsigned long)*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", (int)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 = "UNKNOWN"; - - 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, THREAD_CREATE_WOUT_YIELD | THREAD_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; - } - /* falls through */ - - default: - printf("usage: %s [start|stop]\n", argv[0]); - break; - } - - return 1; -} diff --git a/sys/include/net/gnrc/tftp.h b/sys/include/net/gnrc/tftp.h deleted file mode 100644 index 0c4110d8a5..0000000000 --- a/sys/include/net/gnrc/tftp.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2015 Nick van IJzendoorn - * - * 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 - * - * @deprecated This module has serious quality defects and is not in a - * maintainable state. It will be removed after the 2020.04 release - * at the latest. - * @{ - * - * @file - * @brief TFTP support library - * - * @warning Currently only runs with link-local addresses when there is only - * one interface - * - * 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 - */ - -#ifndef NET_GNRC_TFTP_H -#define NET_GNRC_TFTP_H - -#include - -#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 * US_PER_SEC) -#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 transferred - * @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 - * - * @pre `(GNRC_NETIF_NUMOF == 1) || !ipv6_addr_is_link_local(addr)` - * - * @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 - * - * @pre `(GNRC_NETIF_NUMOF == 1) || !ipv6_addr_is_link_local(addr)` - * - * @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 /* NET_GNRC_TFTP_H */ - -/** - * @} - */ diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index 991b41069e..6b6c1f3eda 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -130,8 +130,5 @@ endif ifneq (,$(filter gnrc_tcp,$(USEMODULE))) DIRS += transport_layer/tcp endif -ifneq (,$(filter gnrc_tftp,$(USEMODULE))) - DIRS += application_layer/tftp -endif include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/application_layer/tftp/Makefile b/sys/net/gnrc/application_layer/tftp/Makefile deleted file mode 100644 index 4232bae24f..0000000000 --- a/sys/net/gnrc/application_layer/tftp/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -MODULE = gnrc_tftp - -include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/application_layer/tftp/gnrc_tftp.c b/sys/net/gnrc/application_layer/tftp/gnrc_tftp.c deleted file mode 100644 index c04c495191..0000000000 --- a/sys/net/gnrc/application_layer/tftp/gnrc_tftp.c +++ /dev/null @@ -1,1189 +0,0 @@ -/* - * Copyright (C) 2015 Nick van IJzendoorn - * - * 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 net_gnrc_tftp - * @{ - * - * @file - * - * @author Nick van IJzendoorn - */ - -#include -#include -#include -#include - -#include "net/gnrc/tftp.h" -#include "net/gnrc/netapi.h" -#include "net/gnrc/netif.h" -#include "net/gnrc/netreg.h" -#include "net/gnrc/udp.h" -#include "net/gnrc/ipv6.h" -#include "random.h" - -#define ENABLE_DEBUG (0) -#include "debug.h" - -#if ENABLE_DEBUG -/* For PRIu16 etc. */ -#include -#endif - -#if (GNRC_NETIF_NUMOF > 1) -/* TODO: change API to make link-local address communitcation with - * multiple network interfaces */ -#warning "gnrc_tftp does not work reliably with link-local addresses and >1 network interfaces." -#endif - -static kernel_pid_t _tftp_kernel_pid; - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define CT_HTONS(x) ((uint16_t)(( \ - (((uint16_t)(x)) >> 8) & 0x00FF) | \ - ((((uint16_t)(x)) << 8) & 0xFF00))) -#else -#define CT_HTONS(x) ((uint16_t)x) -#endif - -#define MIN(a, b) ((a) > (b) ? (b) : (a)) - -#define TFTP_TIMEOUT_MSG 0x4000 -#define TFTP_STOP_SERVER_MSG 0x4001 -#define TFTP_MIN_PACKET_LEN 4 -#define TFTP_DEFAULT_DATA_SIZE (GNRC_TFTP_MAX_TRANSFER_UNIT \ - + sizeof(tftp_packet_data_t)) - -/** - * @brief TFTP mode help support - */ -#define MODE(mode) { # mode, sizeof(# mode) } - -typedef struct { - char *name; - uint8_t len; -} tftp_opt_t; - -/** - * @brief TFTP opcodes - * @{ - */ -typedef uint16_t tftp_opcodes_t; - -#define TO_RRQ CT_HTONS(1) /**< Read Request */ -#define TO_WRQ CT_HTONS(2) /**< Write Request */ -#define TO_DATA CT_HTONS(3) /**< Data */ -#define TO_ACK CT_HTONS(4) /**< Acknowledgment */ -#define TO_ERROR CT_HTONS(5) /**< Error */ -#define TO_OACK CT_HTONS(6) /**< Option ACK */ -/** - * @} - */ - -/** - * @brief TFTP Error Codes - * @{ - */ -typedef uint16_t tftp_err_codes_t; - -#define TE_UN_DEF CT_HTONS(0) /**< Not defined, see error message */ -#define TE_NO_FILE CT_HTONS(1) /**< File not found */ -#define TE_ACCESS CT_HTONS(2) /**< Access violation */ -#define TE_DFULL CT_HTONS(3) /**< Disk full or allocation exceeded */ -#define TE_ILLOPT CT_HTONS(4) /**< Illegal TFTP operation */ -#define TE_UNKOWN_ID CT_HTONS(5) /**< Unknown transfer ID */ -#define TE_EXISTS CT_HTONS(6) /**< File already exists */ -#define TE_UNKOWN_USR CT_HTONS(7) /**< No such user */ -/** - * @} - */ - -/* ordered as @see tftp_mode_t */ -tftp_opt_t _tftp_modes[] = { - [TTM_ASCII] = MODE(netascii), - [TTM_OCTET] = MODE(octet), - [TTM_MAIL] = MODE(mail), -}; - -/** - * @brief TFTP Options - */ -typedef enum { - TOPT_BLKSIZE, - TOPT_TIMEOUT, - TOPT_TSIZE, -} tftp_options_t; - -/* ordered as @see tftp_options_t */ -tftp_opt_t _tftp_options[] = { - [TOPT_BLKSIZE] = MODE(blksize), - [TOPT_TIMEOUT] = MODE(timeout), - [TOPT_TSIZE] = MODE(tsize), -}; - -/** - * @brief The TFTP state - */ -typedef enum { - TS_APP_FAILED = -3, - TS_DUP = -2, - TS_FAILED = -1, - TS_BUSY = 0, - TS_FINISHED = 1 -} tftp_state; - -/** - * @brief The type of the context used - */ -typedef enum { - CT_SERVER, - CT_CLIENT -} tftp_context_type; - -/** - * @brief The TFTP context for the current transfer - */ -typedef struct { - char file_name[GNRC_TFTP_MAX_FILENAME_LEN]; - tftp_mode_t mode; - tftp_opcodes_t op; - ipv6_addr_t peer; - xtimer_t timer; - msg_t timer_msg; - uint32_t timeout; - uint16_t dst_port; - uint16_t src_port; - tftp_context_type ct; - tftp_start_cb_t start_cb; - tftp_data_cb_t data_cb; - tftp_stop_cb_t stop_cb; - gnrc_netreg_entry_t entry; - - /* transfer parameters */ - uint16_t block_nr; - uint16_t block_size; - size_t transfer_size; - uint32_t block_timeout; - uint32_t retries; - bool use_options; - bool enable_options; - bool write_finished; -} tftp_context_t; - -/** - * @brief The default TFTP header - */ -typedef struct __attribute__((packed)) { - tftp_opcodes_t opc; - uint8_t data[]; -} tftp_header_t; - -/** - * @brief The TFTP data packet - */ -typedef struct __attribute__((packed)) { - tftp_opcodes_t opc; - network_uint16_t block_nr; - uint8_t data[]; -} tftp_packet_data_t; - -/** - * @brief The TFTP error packet - */ -typedef struct __attribute__((packed)) { - tftp_opcodes_t opc; - tftp_err_codes_t err_code; - char err_msg[]; -} tftp_packet_error_t; - -/* get the TFTP opcode */ -static inline tftp_opcodes_t _tftp_parse_type(uint8_t *buf) -{ - return ((tftp_header_t *)buf)->opc; -} - -/* initialize the context to it's default state */ -static int _tftp_init_ctxt(ipv6_addr_t *addr, const char *file_name, - tftp_opcodes_t op, tftp_mode_t mode, tftp_context_type type, - tftp_start_cb_t start, tftp_stop_cb_t stop, - tftp_data_cb_t data, bool enable_options, tftp_context_t *ctxt); - -/* set the default TFTP options */ -static void _tftp_set_default_options(tftp_context_t *ctxt); - -/* set the TFTP options to use */ -static int _tftp_set_opts(tftp_context_t *ctxt, size_t blksize, uint32_t timeout, size_t total_size); - -/* this function registers the UDP port and won't return till the TFTP transfer is finished */ -static int _tftp_do_client_transfer(tftp_context_t *ctxt); - -/* the state process of the TFTP transfer */ -static tftp_state _tftp_state_processes(tftp_context_t *ctxt, msg_t *m); - -/* send an start request if we run in client mode */ -static tftp_state _tftp_send_start(tftp_context_t *ctxt, gnrc_pktsnip_t *buf); - -/* send data or and ack depending if we are reading or writing */ -static tftp_state _tftp_send_dack(tftp_context_t *ctxt, gnrc_pktsnip_t *buf, tftp_opcodes_t op); - -/* send and TFTP error to the client */ -static tftp_state _tftp_send_error(tftp_context_t *ctxt, gnrc_pktsnip_t *buf, tftp_err_codes_t err, const char *err_msg); - -/* this function sends the actual packet */ -static tftp_state _tftp_send(gnrc_pktsnip_t *buf, tftp_context_t *ctxt, size_t len); - -/* decode the default TFTP start packet */ -static int _tftp_decode_start(tftp_context_t *ctxt, gnrc_pktsnip_t *inpkt, gnrc_pktsnip_t *outbuf); - -/* decode the TFTP option extensions */ -static int _tftp_decode_options(tftp_context_t *ctxt, gnrc_pktsnip_t *buf, uint32_t start); - -/* decode the received ACK packet */ -static bool _tftp_validate_ack(tftp_context_t *ctxt, uint8_t *buf); - -/* processes the received data packet and calls the callback defined by the user */ -static int _tftp_process_data(tftp_context_t *ctxt, gnrc_pktsnip_t *buf); - -/* decode the received error packet and calls the callback defined by the user */ -static int _tftp_decode_error(uint8_t *buf, tftp_err_codes_t *err, const char * *err_msg); - -/* TFTP super loop server */ -static int _tftp_server(tftp_context_t *ctxt); - -/* get the maximum allowed transfer unit to avoid 6Lo fragmentation */ -static uint16_t _tftp_get_maximum_block_size(void) -{ - uint16_t tmp; - gnrc_netif_t *netif = gnrc_netif_iter(NULL); - - if ((netif != NULL) && gnrc_netapi_get(netif->pid, NETOPT_MAX_PDU_SIZE, - 0, &tmp, sizeof(uint16_t)) >= 0) { - /* TODO calculate proper block size */ - return tmp - sizeof(udp_hdr_t) - sizeof(ipv6_hdr_t) - 10; - } - return GNRC_TFTP_MAX_TRANSFER_UNIT; -} - -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_extensions) -{ - tftp_context_t ctxt; - - assert(data_cb); - assert(start_cb); - assert(stop_cb); - - /* prepare the context */ - if (_tftp_init_ctxt(addr, file_name, TO_RRQ, mode, CT_CLIENT, start_cb, stop_cb, data_cb, use_option_extensions, &ctxt) != TS_FINISHED) { - return -EINVAL; - } - - /* set the transfer options */ - uint16_t mtu = _tftp_get_maximum_block_size(); - if (!use_option_extensions || - _tftp_set_opts(&ctxt, mtu, GNRC_TFTP_DEFAULT_TIMEOUT, 0) != TS_FINISHED) { - _tftp_set_default_options(&ctxt); - - if (use_option_extensions) { - return -EINVAL; - } - } - - /* start the process */ - int ret = _tftp_do_client_transfer(&ctxt); - - /* remove possibly stale timer */ - xtimer_remove(&(ctxt.timer)); - - return ret; -} - -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_extensions) -{ - tftp_context_t ctxt; - - assert(data_cb); - assert(stop_cb); - - /* prepare the context */ - if (_tftp_init_ctxt(addr, file_name, TO_WRQ, mode, CT_CLIENT, NULL, stop_cb, data_cb, use_option_extensions, &ctxt) < 0) { - return -EINVAL; - } - - /* set the transfer options */ - uint16_t mtu = _tftp_get_maximum_block_size(); - if (!use_option_extensions || - _tftp_set_opts(&ctxt, mtu, GNRC_TFTP_DEFAULT_TIMEOUT, total_size) != TS_FINISHED) { - - _tftp_set_default_options(&ctxt); - - if (use_option_extensions) { - return -EINVAL; - } - } - - /* start the process */ - int ret = _tftp_do_client_transfer(&ctxt); - - /* remove possibly stale timer */ - xtimer_remove(&(ctxt.timer)); - - return ret; -} - -int _tftp_init_ctxt(ipv6_addr_t *addr, const char *file_name, - tftp_opcodes_t op, tftp_mode_t mode, tftp_context_type type, - tftp_start_cb_t start, tftp_stop_cb_t stop, - tftp_data_cb_t data, bool enable_options, tftp_context_t *ctxt) -{ - - if (!addr) { - return TS_FAILED; - } - - memset(ctxt, 0, sizeof(*ctxt)); - - /* set the default context parameters */ - ctxt->op = op; - ctxt->ct = type; - ctxt->data_cb = data; - ctxt->start_cb = start; - ctxt->stop_cb = stop; - memcpy(&(ctxt->peer), addr, sizeof(ctxt->peer)); - ctxt->mode = mode; - if (file_name) { - strncpy(ctxt->file_name, file_name, GNRC_TFTP_MAX_FILENAME_LEN); - } - ctxt->file_name[GNRC_TFTP_MAX_FILENAME_LEN - 1] = 0; - ctxt->dst_port = GNRC_TFTP_DEFAULT_DST_PORT; - ctxt->enable_options = enable_options; - - /* transport layer parameters */ - ctxt->block_size = GNRC_TFTP_MAX_TRANSFER_UNIT; - ctxt->block_timeout = GNRC_TFTP_DEFAULT_TIMEOUT; - ctxt->write_finished = false; - - /* generate a random source UDP source port */ - do { - ctxt->src_port = (random_uint32() & 0xff) + GNRC_TFTP_DEFAULT_SRC_PORT; - } while (gnrc_netreg_lookup(GNRC_NETTYPE_UDP, ctxt->src_port)); - - return TS_FINISHED; -} - -void _tftp_set_default_options(tftp_context_t *ctxt) -{ - ctxt->block_size = GNRC_TFTP_MAX_TRANSFER_UNIT; - ctxt->timeout = GNRC_TFTP_DEFAULT_TIMEOUT; - ctxt->block_timeout = GNRC_TFTP_DEFAULT_TIMEOUT; - ctxt->transfer_size = 0; - ctxt->use_options = false; -} - -int _tftp_set_opts(tftp_context_t *ctxt, size_t blksize, uint32_t timeout, size_t total_size) -{ - if (blksize > GNRC_TFTP_MAX_TRANSFER_UNIT || !timeout) { - return TS_FAILED; - } - - ctxt->block_size = blksize; - ctxt->timeout = timeout; - ctxt->block_timeout = timeout; - ctxt->transfer_size = total_size; - ctxt->use_options = true; - - return TS_FINISHED; -} - -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) -{ - /* check if there is only one TFTP server running */ - if (_tftp_kernel_pid != KERNEL_PID_UNDEF) { - DEBUG("tftp: only one TFTP server allowed\n"); - return -1; - } - - /* context will be initialized when a connection is established */ - tftp_context_t ctxt = { - .dst_port = GNRC_TFTP_DEFAULT_DST_PORT, - .src_port = GNRC_TFTP_DEFAULT_DST_PORT, - .data_cb = data_cb, - .start_cb = start_cb, - .stop_cb = stop_cb, - .enable_options = use_options, - }; - - /* validate our arguments */ - assert(data_cb); - assert(start_cb); - assert(stop_cb); - - /* save our kernel PID */ - _tftp_kernel_pid = thread_getpid(); - - /* start the server */ - int ret = _tftp_server(&ctxt); - - /* remove possibly stale timer */ - xtimer_remove(&(ctxt.timer)); - - /* reset the kernel PID */ - _tftp_kernel_pid = KERNEL_PID_UNDEF; - - return ret; -} - -int gnrc_tftp_server_stop(void) -{ - /* check if there is a server running */ - if (_tftp_kernel_pid == KERNEL_PID_UNDEF) { - DEBUG("tftp: no TFTP server running\n"); - return -1; - } - - /* prepare the stop message */ - msg_t m = { - thread_getpid(), - TFTP_STOP_SERVER_MSG, - { NULL } - }; - - /* send the stop message */ - msg_send(&m, _tftp_kernel_pid); - - return 0; -} - -int _tftp_server(tftp_context_t *ctxt) -{ - msg_t msg; - bool active = true; - gnrc_netreg_entry_t entry = GNRC_NETREG_ENTRY_INIT_PID(GNRC_TFTP_DEFAULT_DST_PORT, - sched_active_pid); - - while (active) { - int ret = TS_BUSY; - bool got_client = false; - - /* register the servers main listening port */ - if (gnrc_netreg_register(GNRC_NETTYPE_UDP, &entry)) { - DEBUG("tftp: error starting server.\n"); - return TS_FAILED; - } - - /* main processing loop */ - while (ret == TS_BUSY) { - /* wait for a message */ - msg_receive(&msg); - - /* check if the server stop message has been received */ - if (msg.type == TFTP_STOP_SERVER_MSG) { - active = false; - ret = TS_FAILED; - break; - } - else { - /* continue normal server operation */ - DEBUG("tftp: message incoming\n"); - ret = _tftp_state_processes(ctxt, &msg); - - /* release packet if we received one */ - if (msg.type == GNRC_NETAPI_MSG_TYPE_RCV) { - gnrc_pktbuf_release(msg.content.ptr); - } - } - - /* if we just accepted a client, disable the server main listening port */ - if (!got_client) { - gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &entry); - - if (ret == TS_BUSY) { - got_client = true; - DEBUG("tftp: connection established\n"); - } - } - } - - /* remove any stall timers */ - xtimer_remove(&(ctxt->timer)); - - /* if the server transfer has finished, unregister the client dst port */ - gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &(ctxt->entry)); - DEBUG("tftp: connection terminated\n"); - } - - /* unregister our UDP listener on this thread */ - gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &entry); - - return 0; -} - -int _tftp_do_client_transfer(tftp_context_t *ctxt) -{ - msg_t msg; - tftp_state ret = TS_BUSY; - - /* register our DNS response listener */ - gnrc_netreg_entry_t entry = GNRC_NETREG_ENTRY_INIT_PID(ctxt->src_port, - sched_active_pid); - - if (gnrc_netreg_register(GNRC_NETTYPE_UDP, &entry)) { - DEBUG("tftp: error starting server.\n"); - return TS_FAILED; - } - - /* try to start the TFTP transfer */ - ret = _tftp_state_processes(ctxt, NULL); - if (ret != TS_BUSY) { - DEBUG("tftp: transfer failed\n"); - /* if the start failed return */ - return ret; - } - - /* main processing loop */ - while (ret == TS_BUSY) { - /* wait for a message */ - msg_receive(&msg); - DEBUG("tftp: message received\n"); - ret = _tftp_state_processes(ctxt, &msg); - - /* release packet if we received one */ - if (msg.type == GNRC_NETAPI_MSG_TYPE_RCV) { - gnrc_pktbuf_release(msg.content.ptr); - } - } - - /* unregister our UDP listener on this thread */ - gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &entry); - - return ret; -} - -tftp_state _tftp_state_processes(tftp_context_t *ctxt, msg_t *m) -{ - gnrc_pktsnip_t *outbuf = gnrc_pktbuf_add(NULL, NULL, TFTP_DEFAULT_DATA_SIZE, - GNRC_NETTYPE_UNDEF); - /* check if this is an client start */ - if (!m) { - DEBUG("tftp: starting transaction as client\n"); - return _tftp_send_start(ctxt, outbuf); - } - else if (m->type == TFTP_TIMEOUT_MSG) { - DEBUG("tftp: timeout occurred\n"); - if (++(ctxt->retries) > GNRC_TFTP_MAX_RETRIES) { - /* transfer failed due to lost peer */ - DEBUG("tftp: peer lost\n"); - gnrc_pktbuf_release(outbuf); - return TS_FAILED; - } - - /* increase the timeout for congestion control */ - ctxt->block_timeout <<= 1; - - /* the send message timed out, re-sending */ - if (ctxt->dst_port == GNRC_TFTP_DEFAULT_DST_PORT) { - DEBUG("tftp: sending timed out, re-sending\n"); - /* we are still negotiating resent, start */ - return _tftp_send_start(ctxt, outbuf); - } - else { - DEBUG("tftp: last data or ack packet lost, resending\n"); - /* we are sending / receiving data */ - /* if we are reading resent the ACK, if writing the DATA */ - return _tftp_send_dack(ctxt, outbuf, (ctxt->op == TO_RRQ) ? TO_ACK : TO_DATA); - } - } - else if (m->type != GNRC_NETAPI_MSG_TYPE_RCV) { - DEBUG("tftp: unknown message\n"); - gnrc_pktbuf_release(outbuf); - return TS_BUSY; - } - - gnrc_pktsnip_t *pkt = m->content.ptr; - if (pkt->size < TFTP_MIN_PACKET_LEN) { - DEBUG("tftp: packet is too short\n"); - gnrc_pktbuf_release(outbuf); - return TS_FAILED; - } - - gnrc_pktsnip_t *tmp; - tmp = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_UDP); - udp_hdr_t *udp = (udp_hdr_t *)tmp->data; - - tmp = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); - ipv6_hdr_t *ip = (ipv6_hdr_t *)tmp->data; - uint8_t *data = (uint8_t *)pkt->data; - - xtimer_remove(&(ctxt->timer)); - - switch (_tftp_parse_type(data)) { - case TO_RRQ: - case TO_WRQ: { - if (byteorder_ntohs(udp->dst_port) != GNRC_TFTP_DEFAULT_DST_PORT) { - /* not a valid start packet */ - DEBUG("tftp: incoming packet on port %d dropped\n", byteorder_ntohs(udp->dst_port)); - gnrc_pktbuf_release(outbuf); - return TS_FAILED; - } - - /* reinitialize the context with the current client */ - tftp_opcodes_t op = _tftp_parse_type(data); - _tftp_init_ctxt(&(ip->src), NULL, op, TTM_ASCII, CT_SERVER, - ctxt->start_cb, ctxt->stop_cb, ctxt->data_cb, - ctxt->enable_options, ctxt); - - /* get the context of the client */ - ctxt->dst_port = byteorder_ntohs(udp->src_port); - DEBUG("tftp: client's port is %" PRIu16 "\n", ctxt->dst_port); - - int offset = _tftp_decode_start(ctxt, pkt, outbuf); - DEBUG("tftp: offset after decode start = %i\n", offset); - if (offset < 0) { - DEBUG("tftp: there is no data?\n"); - gnrc_pktbuf_release(outbuf); - return TS_FAILED; - } - - /* register a listener for the UDP port */ - gnrc_netreg_entry_init_pid(&(ctxt->entry), ctxt->src_port, - sched_active_pid); - gnrc_netreg_register(GNRC_NETTYPE_UDP, &(ctxt->entry)); - - /* try to decode the options */ - tftp_state state; - tftp_opcodes_t opcode; - if (ctxt->enable_options && - _tftp_decode_options(ctxt, pkt, offset) > offset) { - DEBUG("tftp: send option ACK\n"); - - /* the client send the TFTP options */ - opcode = TO_OACK; - } - else { - DEBUG("tftp: send normal ACK\n"); - - /* the client didn't send options, use ACK and set defaults */ - _tftp_set_default_options(ctxt); - - /* send the first data block */ - if (ctxt->op == TO_RRQ) { - ++(ctxt->block_nr); - opcode = TO_DATA; - } - else { - opcode = TO_ACK; - } - } - - /* validate if the application accepts the action, mode, filename and transfer_size */ - tftp_action_t action = (ctxt->op == TO_RRQ) ? TFTP_READ : TFTP_WRITE; - if (!ctxt->start_cb(action, ctxt->mode, ctxt->file_name, &(ctxt->transfer_size))) { - _tftp_send_error(ctxt, outbuf, TE_ACCESS, "Blocked by user application"); - DEBUG("tftp: callback not able to handle mode\n"); - return TS_FAILED; - } - - /* the client send the TFTP options */ - state = _tftp_send_dack(ctxt, outbuf, opcode); - - /* check if the client negotiation was successful */ - if (state != TS_BUSY) { - DEBUG("tftp: not able to send ACK\n"); - } - return state; - } break; - - case TO_DATA: { - /* try to process the data */ - int proc = _tftp_process_data(ctxt, pkt); - - if (proc == TS_APP_FAILED || proc == TS_FAILED) { - DEBUG("tftp: data not accepted\n"); - /* the data is not accepted return */ - /* we are maybe releasing twice XXX*/ - gnrc_pktbuf_release(outbuf); - return proc; - } - - if (proc == TS_DUP) { - DEBUG("tftp: duplicated data received, acking...\n"); - ctxt->dst_port = byteorder_ntohs(udp->src_port); - DEBUG("tftp: client's port is %" PRIu16 "\n", ctxt->dst_port); - _tftp_send_dack(ctxt, outbuf, TO_ACK); - return TS_BUSY; - } - - /* check if this is the first block */ - if (!ctxt->block_nr - && ctxt->dst_port == GNRC_TFTP_DEFAULT_DST_PORT - && !ctxt->use_options) { - /* no OACK received, restore default TFTP parameters */ - _tftp_set_default_options(ctxt); - DEBUG("tftp: restore default TFTP parameters\n"); - - /* switch the destination port to the src port of the server */ - ctxt->dst_port = byteorder_ntohs(udp->src_port); - } - - /* wait for the next data block */ - DEBUG("tftp: wait for the next data block\n"); - ++(ctxt->block_nr); - _tftp_send_dack(ctxt, outbuf, TO_ACK); - - /* check if the data transfer has finished */ - if (proc < (int)ctxt->block_size) { - DEBUG("tftp: transfer finished\n"); - - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_SUCCESS, NULL); - } - - return TS_FINISHED; - } - - return TS_BUSY; - } - break; - - case TO_ACK: { - /* validate if this is the ACK we are waiting for */ - if (!_tftp_validate_ack(ctxt, data)) { - /* invalid packet ACK, drop */ - gnrc_pktbuf_release(outbuf); - return TS_BUSY; - } - - /* check if the write action is finished */ - if (ctxt->write_finished) { - gnrc_pktbuf_release(outbuf); - - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_SUCCESS, NULL); - } - - return TS_FINISHED; - } - - /* check if this is the first ACK */ - if (!ctxt->block_nr && ctxt->dst_port != byteorder_ntohs(udp->src_port)) { - /* no OACK received restore default TFTP parameters */ - _tftp_set_default_options(ctxt); - - /* switch the destination port to the src port of the server */ - ctxt->dst_port = byteorder_ntohs(udp->src_port); - } - - /* send the next data block */ - ++(ctxt->block_nr); - - return _tftp_send_dack(ctxt, outbuf, TO_DATA); - } break; - - case TO_ERROR: { - tftp_err_codes_t err; - const char *err_msg; - - /* decode the received error */ - _tftp_decode_error(data, &err, &err_msg); - - /* inform the user application about the error */ - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_PEER_ERROR, err_msg); - } - - DEBUG("tftp: ERROR: %s\n", err_msg); - gnrc_pktbuf_release(outbuf); - return TS_FAILED; - } break; - - case TO_OACK: { - /* only allow one OACK to be received */ - if (ctxt->dst_port != byteorder_ntohs(udp->src_port)) { - DEBUG("tftp: TO_OACK received\n"); - - /* decode the options */ - _tftp_decode_options(ctxt, pkt, 0); - - /* take the new source port */ - ctxt->dst_port = byteorder_ntohs(udp->src_port); - - /* we must send block one to finish the negotiation in send mode */ - if (ctxt->op == TO_WRQ) { - ++(ctxt->block_nr); - } - } - else { - DEBUG("tftp: dropping double TO_OACK\n"); - } - - return _tftp_send_dack(ctxt, outbuf, (ctxt->op == TO_WRQ) ? TO_DATA : TO_ACK); - } break; - } - - gnrc_pktbuf_release(outbuf); - return TS_FAILED; -} - -size_t _tftp_add_option(uint8_t *dst, tftp_opt_t *opt, uint32_t value) -{ - size_t offset; - - /* set the option name */ - memcpy(dst, opt->name, opt->len); - offset = opt->len; - - /* set the option value */ - offset += sprintf((char *)(dst + opt->len), "%" PRIu32, value); - - /* finish option value */ - *(dst + offset) = 0; - return ++offset; -} - -uint32_t _tftp_append_options(tftp_context_t *ctxt, tftp_header_t *hdr, uint32_t offset) -{ - offset += _tftp_add_option(hdr->data + offset, _tftp_options + TOPT_BLKSIZE, ctxt->block_size); - offset += _tftp_add_option(hdr->data + offset, _tftp_options + TOPT_TIMEOUT, (ctxt->timeout / US_PER_SEC)); - - /** - * Only set the transfer option if we are sending. - * Or when we are reading in bin mode. - */ - if ((ctxt->ct == CT_SERVER && ctxt->op == TO_RRQ) || - (ctxt->ct == CT_CLIENT && ctxt->op == TO_WRQ) || - ctxt->mode == TTM_OCTET) { - offset += _tftp_add_option(hdr->data + offset, _tftp_options + TOPT_TSIZE, ctxt->transfer_size); - } - - return offset; -} - -tftp_state _tftp_send_start(tftp_context_t *ctxt, gnrc_pktsnip_t *buf) -{ - /* get required values */ - int len = strlen(ctxt->file_name) + 1; /* we also want the \0 char */ - tftp_opt_t *m = _tftp_modes + ctxt->mode; - - /* start filling the header */ - tftp_header_t *hdr = (tftp_header_t *)(buf->data); - - hdr->opc = ctxt->op; - memcpy(hdr->data, ctxt->file_name, len); - memcpy(hdr->data + len, m->name, m->len); - - /* fill the options */ - uint32_t offset = (len + m->len); - if (ctxt->use_options) { - offset = _tftp_append_options(ctxt, hdr, offset); - } - - /* send the data */ - return _tftp_send(buf, ctxt, offset + sizeof(tftp_header_t)); -} - -tftp_state _tftp_send_dack(tftp_context_t *ctxt, gnrc_pktsnip_t *buf, tftp_opcodes_t op) -{ - size_t len = 0; - - assert(op == TO_DATA || op == TO_ACK || op == TO_OACK); - - /* fill the packet */ - tftp_packet_data_t *pkt = (tftp_packet_data_t *)(buf->data); - pkt->block_nr = byteorder_htons(ctxt->block_nr); - pkt->opc = op; - - if (op == TO_DATA) { - DEBUG("tftp: getting data from callback\n"); - /* get the required data from the user */ - len = ctxt->data_cb(ctxt->block_size * (ctxt->block_nr - 1), pkt->data, ctxt->block_size); - - /* check if we are finished on ACK receive */ - ctxt->write_finished = (len < ctxt->block_size); - - /* enable timeout */ - ctxt->block_timeout = ctxt->timeout; - } - else if (op == TO_OACK) { - /* append the options */ - len = _tftp_append_options(ctxt, (tftp_header_t *)pkt, 0); - - /* disable timeout*/ - ctxt->block_timeout = 0; - } - else if (op == TO_ACK) { - /* disable timeout*/ - ctxt->block_timeout = 0; - } - - /* send the data */ - return _tftp_send(buf, ctxt, sizeof(tftp_packet_data_t) + len); -} - -tftp_state _tftp_send_error(tftp_context_t *ctxt, gnrc_pktsnip_t *buf, tftp_err_codes_t err, const char *err_msg) -{ - int strl = err_msg ? strlen(err_msg) + 1 : 0; - - (void) ctxt; - - /* fill the packet */ - tftp_packet_error_t *pkt = (tftp_packet_error_t *)(buf->data); - pkt->opc = TO_ERROR; - pkt->err_code = err; - memcpy(pkt->err_msg, err_msg, strl); - - /* don't set a timeout on the error */ - ctxt->block_timeout = 0; - - /* return the size of the packet */ - _tftp_send(buf, ctxt, sizeof(tftp_packet_error_t) + strl); - - return TS_FAILED; -} - -tftp_state _tftp_send(gnrc_pktsnip_t *buf, tftp_context_t *ctxt, size_t len) -{ - network_uint16_t src_port, dst_port; - gnrc_pktsnip_t *udp, *ip; - - assert(len <= TFTP_DEFAULT_DATA_SIZE); - - /* down-size the packet to it's used size */ - if (len > TFTP_DEFAULT_DATA_SIZE) { - DEBUG("tftp: can't reallocate to bigger packet, buffer overflowed\n"); - gnrc_pktbuf_release(buf); - - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_INTERN_ERROR, "buffer overflowed"); - } - - return TS_FAILED; - } - else if (gnrc_pktbuf_realloc_data(buf, len) != 0) { - assert(false); - - DEBUG("tftp: failed to reallocate data snippet\n"); - gnrc_pktbuf_release(buf); - - /* inform the user that we can't reallocate */ - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_INTERN_ERROR, "no reallocate"); - } - - return TS_FAILED; - } - - /* allocate UDP header, set source port := destination port */ - src_port.u16 = ctxt->src_port; - dst_port.u16 = ctxt->dst_port; - udp = gnrc_udp_hdr_build(buf, src_port.u16, dst_port.u16); - if (udp == NULL) { - DEBUG("tftp: error unable to allocate UDP header\n"); - gnrc_pktbuf_release(buf); - - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_INTERN_ERROR, "no udp allocate"); - } - - return TS_FAILED; - } - - /* allocate IPv6 header */ - ip = gnrc_ipv6_hdr_build(udp, NULL, &(ctxt->peer)); - if (ip == NULL) { - DEBUG("tftp: error unable to allocate IPv6 header\n"); - gnrc_pktbuf_release(udp); - - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_INTERN_ERROR, "no ip allocate"); - } - - return TS_FAILED; - } - - if (ipv6_addr_is_link_local(&(ctxt->peer))) { - gnrc_pktsnip_t *netif_hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0); - if (netif_hdr == NULL) { - DEBUG("tftp: error unable to allocate IPv6 header\n"); - gnrc_pktbuf_release(ip); - - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_INTERN_ERROR, "no netif_hdr allocate"); - } - - return TS_FAILED; - } - gnrc_netif_hdr_set_netif(netif_hdr->data, gnrc_netif_iter(NULL)); - LL_PREPEND(ip, netif_hdr); - } - - /* send packet */ - if (gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP, GNRC_NETREG_DEMUX_CTX_ALL, - ip) == 0) { - /* if send failed inform the user */ - DEBUG("tftp: error unable to locate UDP thread\n"); - gnrc_pktbuf_release(ip); - - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_INTERN_ERROR, "no dispatch send"); - } - - return TS_FAILED; - } - - /* only set timeout if enabled for this block */ - if (ctxt->block_timeout) { - ctxt->timer_msg.type = TFTP_TIMEOUT_MSG; - xtimer_set_msg(&(ctxt->timer), ctxt->block_timeout, &(ctxt->timer_msg), thread_getpid()); - DEBUG("tftp: set timeout %" PRIu32 " ms\n", ctxt->block_timeout / US_PER_MS); - } - - return TS_BUSY; -} - -bool _tftp_validate_ack(tftp_context_t *ctxt, uint8_t *buf) -{ - tftp_packet_data_t *pkt = (tftp_packet_data_t *) buf; - - return ctxt->block_nr == byteorder_ntohs(pkt->block_nr); -} - -int _tftp_decode_start(tftp_context_t *ctxt, gnrc_pktsnip_t *inpkt, gnrc_pktsnip_t *outbuf) -{ - /* decode the packet */ - tftp_header_t *hdr = (tftp_header_t *)inpkt->data; - - /* get the file name and copy terminating byte */ - size_t fnlen = strlen((char *)hdr->data) + 1; - - if (fnlen >= GNRC_TFTP_MAX_FILENAME_LEN) { - _tftp_send_error(ctxt, outbuf, TE_ILLOPT, "Filename to long"); - - if (ctxt->stop_cb) { - ctxt->stop_cb(TFTP_INTERN_ERROR, "Filename to long"); - } - - return TS_FAILED; - } - memcpy(ctxt->file_name, hdr->data, fnlen); - - /* Get mode string by advancing pointer */ - char *str_mode = (char *)hdr->data + fnlen; - DEBUG("tftp: incoming request '%s', mode: %s\n", ctxt->file_name, str_mode); - - /* decode the TFTP transfer mode */ - for (uint32_t idx = 0; idx < ARRAY_SIZE(_tftp_modes); ++idx) { - if (_tftp_modes[idx].len > (inpkt->size - sizeof(*hdr) - fnlen)) { - continue; - } - - if (memcmp(_tftp_modes[idx].name, str_mode, _tftp_modes[idx].len) == 0) { - ctxt->mode = (tftp_mode_t)idx; - return (str_mode + _tftp_modes[idx].len) - (char *)hdr->data; - } - } - - return -EINVAL; -} - -int _tftp_decode_options(tftp_context_t *ctxt, gnrc_pktsnip_t *buf, uint32_t start) -{ - tftp_header_t *pkt = (tftp_header_t *)buf->data; - size_t offset = start; - - DEBUG("tftp: decode options\n"); - DEBUG("tftp: buffer size = %lu\n", (unsigned long)buf->size); - while ((offset + sizeof(uint16_t)) < (buf->size)) { - DEBUG("tftp: offset = %lu\n", (unsigned long)offset); - /* get the option name */ - const char *name = (const char *)(pkt->data + offset); - offset += strlen(name) + 1; - /* get the value name */ - const char *value = (const char *)(pkt->data + offset); - offset += strlen(value) + 1; - - /* check what option we are parsing */ - for (uint32_t idx = 0; idx < ARRAY_SIZE(_tftp_options); ++idx) { - if (memcmp(name, _tftp_options[idx].name, _tftp_options[idx].len) == 0) { - /* set the option value of the known options */ - switch (idx) { - case TOPT_BLKSIZE: - ctxt->block_size = atoi(value); - DEBUG("tftp: got option TOPT_BLKSIZE = %" PRIu16 "\n", ctxt->block_size); - break; - - case TOPT_TSIZE: - ctxt->transfer_size = atoi(value); - DEBUG("tftp: got option TOPT_TSIZE = %" PRIu32 "\n", (uint32_t)ctxt->transfer_size); - - if (ctxt->start_cb && ctxt->ct == CT_CLIENT) { - ctxt->start_cb(TFTP_READ, ctxt->mode, ctxt->file_name, &(ctxt->transfer_size)); - } - break; - - case TOPT_TIMEOUT: - ctxt->timeout = atoi(value) * US_PER_SEC; - DEBUG("tftp: option TOPT_TIMEOUT = %" PRIu32 " ms\n", ctxt->timeout / US_PER_MS); - break; - } - - break; - } - } - } - - DEBUG("tftp: return %lu\n", (unsigned long)offset); - return offset; -} - -int _tftp_process_data(tftp_context_t *ctxt, gnrc_pktsnip_t *buf) -{ - tftp_packet_data_t *pkt = (tftp_packet_data_t *) buf->data; - - DEBUG("tftp: processing data\n"); - - uint16_t block_nr = byteorder_ntohs(pkt->block_nr); - - /* check if this is the packet we are waiting for */ - if (block_nr > (ctxt->block_nr + 1)) { - DEBUG("tftp: incorrect block_nr %d received from server, expected %d\n", - block_nr, (ctxt->block_nr + 1)); - return TS_FAILED; - } - if (block_nr < (ctxt->block_nr + 1)) { - DEBUG("tftp: not the packet we were waiting for, expected %d, received %d\n", - (uint16_t)(ctxt->block_nr + 1), block_nr); - return TS_DUP; - } - - /* send the user data trough to the user application */ - if (ctxt->data_cb(ctxt->block_nr * ctxt->block_size, pkt->data, - buf->size - sizeof(tftp_packet_data_t)) < 0) { - DEBUG("tftp: error in data callback\n"); - return TS_APP_FAILED; - } - - /* return the number of data bytes received */ - return buf->size - sizeof(tftp_packet_data_t); -} - -int _tftp_decode_error(uint8_t *buf, tftp_err_codes_t *err, const char * *err_msg) -{ - tftp_packet_error_t *pkt = (tftp_packet_error_t *) buf; - - /* return the error code and message */ - *err = pkt->err_code; - *err_msg = pkt->err_msg; - - return sizeof(tftp_packet_error_t) + strlen(pkt->err_msg) + 1; -} - -/** - * @} - */