libtftp: initial commit of a simple tftp client library
authorReto Achermann <reto.achermann@inf.ethz.ch>
Thu, 25 Jun 2015 14:07:59 +0000 (16:07 +0200)
committerReto Achermann <reto.achermann@inf.ethz.ch>
Thu, 25 Jun 2015 14:58:18 +0000 (16:58 +0200)
generic usage:
  tftp_client_connect();
  tftp_client_read_file();
  tftp_client_disconnect();

Currently, timeouts are not handled.

Signed-off-by: Reto Achermann <reto.achermann@inf.ethz.ch>

include/tftp/tftp.h [new file with mode: 0644]
lib/tftp/Hakefile [new file with mode: 0644]
lib/tftp/client.c [new file with mode: 0644]
lib/tftp/common.c [new file with mode: 0644]
lib/tftp/server.c [new file with mode: 0644]
lib/tftp/tftp_internal.h [new file with mode: 0644]

diff --git a/include/tftp/tftp.h b/include/tftp/tftp.h
new file mode 100644 (file)
index 0000000..8155602
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * \file
+ * \brief TFTP library
+ */
+
+/*
+ * Copyright (c) 2015 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+
+#ifndef TFTP_H_
+#define TFTP_H_ 1
+
+/*
+ * -------------------------------------------------------------------------------
+ * TFTP Server
+ * -------------------------------------------------------------------------------=
+ */
+
+///< server request callback function
+typedef errval_t(*tfpt_server_cb_f_t)(void);
+
+/**
+ * \brief starts the tftp server on this machine on a given port
+ *
+ * \param ip    ip address to be used
+ * \param port  port to be used
+ * \param cb    callback function called when clients connect
+ *
+ * \return  SYS_ERR_OK on success
+ *          TFTP_ERR_* on failure
+ */
+errval_t tftp_server_accept(char *ip, uint16_t port, tfpt_server_cb_f_t cb);
+
+/**
+ * \brief terminates the tftp server connection
+ *
+ * \return  SYS_ERR_OK on success
+ *          TFTP_ERR_* on failure
+ */
+errval_t tftp_server_terminate(void);
+
+/*
+ * -------------------------------------------------------------------------------
+ * TFTP Client
+ * -------------------------------------------------------------------------------=
+ */
+
+/**
+ * \brief   writes a file over tftp on the established connection
+ *
+ * \param name      name of the remote file to write
+ * \param buf       buffer containing the data
+ * \param buflen    length of the file to write
+ *
+ * \return  SYS_ERR_OK on success
+ *          TFTP_ERR_* on failure
+ */
+errval_t tftp_client_write_file(char *name, void *buf, size_t buflen);
+
+
+/**
+ * \brief   reads a file over tftp on the established connection
+ *
+ * \param path      path of the file to readf rom
+ * \param buf       buffer where to store the contents
+ * \param buflen    maximum length of the buffer
+ * \param ret_size  returns the number of bytes received
+ *
+ * \return  SYS_ERR_OK on success
+ *          TFTP_ERR_* on failure
+ */
+errval_t tftp_client_read_file(char *path, void *buf, size_t buflen, size_t *ret_size);
+
+
+/**
+ * \brief attempts to initialize a new TFTP connection to a server
+ *
+ * \returns SYS_ERR_OK on success
+ *          TFTP_ERR_* on failure
+ */
+errval_t tftp_client_connect(char *ip, uint16_t port);
+
+/**
+ * \brief terminates the connection to the tftp server
+ *
+ * \return SYS_ERR_OK on success
+ *         TFTP_ERR_* on failure
+ */
+errval_t tftp_client_disconnect(void);
+
+
+#endif
diff --git a/lib/tftp/Hakefile b/lib/tftp/Hakefile
new file mode 100644 (file)
index 0000000..3fdf498
--- /dev/null
@@ -0,0 +1,29 @@
+--------------------------------------------------------------------------
+-- Copyright (c) 2007-2009, ETH Zurich.
+-- All rights reserved.
+--
+-- This file is distributed under the terms in the attached LICENSE file.
+-- If you do not find this file, copies can be found by writing to:
+-- ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+--
+-- Hakefile for lib/timer
+-- 
+--------------------------------------------------------------------------
+
+[ build library { 
+    target = "tftp_client",
+    cFiles = [ 
+        "client.c", 
+        "common.c" 
+    ],
+    addLibraries = [ "lwip", "contmng", "net_if_raw"]
+  },
+  build library { 
+    target = "tftp_server",
+    cFiles = [ 
+        "server.c", 
+        "common.c" 
+    ],
+    addLibraries = [ "lwip", "contmng", "net_if_raw"]
+  }
+]
diff --git a/lib/tftp/client.c b/lib/tftp/client.c
new file mode 100644 (file)
index 0000000..fb1a8bb
--- /dev/null
@@ -0,0 +1,410 @@
+/**
+ * \file
+ * \brief TFTP library
+ */
+
+/*
+ * Copyright (c) 2015 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/waitset.h>
+#include <barrelfish/nameservice_client.h>
+
+#include <lwip/udp.h>
+#include <lwip/init.h>
+
+#include <tftp/tftp.h>
+
+#include "tftp_internal.h"
+
+
+// error definitions
+#define TFTP_ERR_BUSY 1
+#define TFTP_ERR_DISCONNECTED 1
+#define TFTP_ERR_NOT_FOUND 1
+#define TFTP_ERR_ACCESS_DENIED 1
+#define TFTP_ERR_FILE_EXISTS 1
+
+
+
+///< the TFTP client
+struct tftp_client
+{
+    /* client state */
+    tftp_st_t state;
+
+    /* connection information */
+    struct ip_addr server_ip;
+    uint16_t server_port;
+    tftp_mode_t mode;
+
+    /* request information */
+    uint32_t block;
+    size_t bytes;
+    void *buf;
+    size_t buflen;
+
+    /* connection information */
+    struct udp_pcb *pcb;
+    struct pbuf *p;
+    void *ppayload;
+    struct udp_pcb *rcv_pcb;
+};
+
+
+struct tftp_client tftp_client;
+
+
+static errval_t tftp_client_send_data(struct udp_pcb *pcb, uint32_t blockno, void *buf,
+                                      uint32_t length, struct ip_addr *addr, u16_t port,
+                                      struct pbuf *p)
+{
+    p->len = TFTP_MAX_MSGSIZE;
+    p->tot_len = TFTP_MAX_MSGSIZE;
+    p->payload = tftp_client.ppayload;
+
+    size_t offset = set_opcode(p->payload, TFTP_OP_DATA);
+    offset += set_block_no(p->payload + offset, blockno);
+    if (length > TFTP_BLOCKSIZE) {
+        length = TFTP_BLOCKSIZE;
+    }
+
+    memcpy(p->payload + offset, buf, length);
+    p->len = (uint16_t)length + offset;
+    p->tot_len = (uint16_t)length + offset;
+
+    int r = udp_sendto(pcb, p, addr, port);
+    if (r != ERR_OK) {
+        USER_PANIC("send failed");
+    }
+
+    return SYS_ERR_OK;
+}
+
+
+/*
+ * ------------------------------------------------------------------------------
+ * Recv Handlers
+ * ------------------------------------------------------------------------------
+ */
+
+static void tftp_client_handle_write(struct udp_pcb *pcb, struct pbuf *pbuf,
+                                     struct ip_addr *addr, u16_t port)
+{
+    USER_PANIC("NYI");
+    tpft_op_t op = get_opcode(pbuf->payload);
+    uint32_t blockno;
+    switch(op) {
+        case TFTP_OP_ACK :
+            blockno = get_block_no(pbuf->payload, pbuf->len);
+            assert(pbuf->len == pbuf->tot_len);
+            if (blockno == TFTP_ERR_INVALID_BUFFER) {
+                TFTP_DEBUG("failed to decode block number in data packet\n");
+                break;
+            }
+
+            if (blockno == tftp_client.block) {
+                if (tftp_client.state == TFTP_ST_LAST_DATA_SENT) {
+                    tftp_client.state = TFTP_ST_CLOSED;
+                    break;
+                }
+
+                uint32_t offset = TFTP_BLOCKSIZE * blockno;
+                uint32_t length = TFTP_BLOCKSIZE;
+                if (tftp_client.buflen - offset < TFTP_BLOCKSIZE) {
+                    length = tftp_client.buflen - offset;
+                    tftp_client.state = TFTP_ST_LAST_DATA_SENT;
+                }
+
+                tftp_client.block++;
+
+                tftp_client_send_data(pcb, tftp_client.block, tftp_client.buf + offset, length,
+                                      addr, port, tftp_client.p);
+                tftp_client.state = TFTP_ST_DATA_SENT;
+            } else  {
+                TFTP_DEBUG("got double packet: %u\n", blockno);
+            }
+
+            break;
+        case TFTP_OP_ERROR :
+            TFTP_DEBUG("got a error packet\n");
+            break;
+        default:
+            tftp_client.state = TFTP_ST_ERROR;
+            break;
+    }
+
+    pbuf_free(pbuf);
+}
+
+static void tftp_client_handle_read(struct udp_pcb *pcb, struct pbuf *pbuf,
+                                    struct ip_addr *addr, u16_t port)
+{
+    tpft_op_t op = get_opcode(pbuf->payload);
+    uint32_t blockno;
+    switch(op) {
+        case TFTP_OP_DATA :
+            blockno = get_block_no(pbuf->payload, pbuf->len);
+            assert(pbuf->len == pbuf->tot_len);
+            if (blockno == TFTP_ERR_INVALID_BUFFER) {
+                TFTP_DEBUG("failed to decode block number in data packet\n");
+                break;
+            }
+
+            if (blockno == tftp_client.block) {
+                if (pbuf->len < 5) {
+                    TFTP_DEBUG("too small pbuf lenth\n");
+                }
+
+                void *buf = pbuf->payload + 4;
+                size_t length = pbuf->len - 4;
+                TFTP_DEBUG_PACKETS("received block %u of size %lu bytes\n", blockno, length);
+
+                if (tftp_client.buflen < tftp_client.bytes + length) {
+                    TFTP_DEBUG("too less bufferspace available\n");
+                    length = tftp_client.buflen - tftp_client.bytes;
+                }
+                memcpy(tftp_client.buf + tftp_client.bytes, buf, length);
+
+                int r = tftp_send_ack(pcb, blockno, addr, port, tftp_client.p,
+                                      tftp_client.ppayload);
+                if (r != ERR_OK) {
+                    tftp_client.state = TFTP_ST_ERROR;
+                    break;
+                }
+                tftp_client.state = TFTP_ST_ACK_SENT;
+                tftp_client.block++;
+                tftp_client.bytes += length;
+                if (length < TFTP_BLOCKSIZE) {
+                    TFTP_DEBUG("setting the last ack state\n");
+                    tftp_client.state = TFTP_ST_LAST_ACK_SENT;
+                }
+            } else  {
+                TFTP_DEBUG("got double packet: %u\n", blockno);
+                int r = tftp_send_ack(pcb, blockno, addr, port, tftp_client.p,
+                                      tftp_client.ppayload);
+                if (r != ERR_OK) {
+                    tftp_client.state = TFTP_ST_ERROR;
+                    break;
+                }
+                tftp_client.state = TFTP_ST_ACK_SENT;
+            }
+
+            break;
+        case TFTP_OP_ERROR :
+            TFTP_DEBUG("got a error packet\n");
+            get_error(pbuf->payload, pbuf->len);
+            tftp_client.state = TFTP_ST_ERROR;
+            break;
+        default:
+            tftp_client.state = TFTP_ST_ERROR;
+            TFTP_DEBUG("unexpected packet\n");
+            break;
+    }
+
+    pbuf_free(pbuf);
+}
+
+
+static void tftp_client_recv_handler(void *arg, struct udp_pcb *pcb, struct pbuf *pbuf,
+                             struct ip_addr *addr, u16_t port)
+{
+    switch(tftp_client.state) {
+        case TFTP_ST_WRITE_REQ_SENT:
+        case TFTP_ST_DATA_SENT :
+        case TFTP_ST_LAST_DATA_SENT :
+            tftp_client_handle_write(pcb, pbuf, addr, port);
+            break;
+        case TFTP_ST_READ_REQ_SENT :
+        case TFTP_ST_ACK_SENT :
+            tftp_client_handle_read(pcb, pbuf, addr, port);
+            break;
+        default:
+            TFTP_DEBUG("unexpected state: %u\n", tftp_client.state);
+            break;
+    }
+}
+
+static void new_request(char *path, tpft_op_t opcode)
+{
+    size_t path_length = strlen(path);
+    assert(strlen(path) + 14 < TFTP_MAX_MSGSIZE);
+
+    struct pbuf *p = tftp_client.p;
+    assert(p);
+
+    p->len = TFTP_MAX_MSGSIZE;
+    p->tot_len = TFTP_MAX_MSGSIZE;
+    p->payload = tftp_client.ppayload;
+
+    memset(p->payload, 0, path_length + 16);
+
+    size_t length = set_opcode(p->payload, opcode);
+
+    length += snprintf(p->payload + length, path_length + 1, "%s", path) + 1;
+    length += set_mode(p->payload + length, tftp_client.mode);
+
+    p->len = (uint16_t)length;
+    p->tot_len = (uint16_t)length;
+
+    TFTP_DEBUG("sending udp payload of %lu bytes\n", length);
+
+
+
+    int r = udp_send(tftp_client.pcb, p);
+    if (r != ERR_OK) {
+        TFTP_DEBUG("send failed\n");
+    }
+}
+
+
+errval_t tftp_client_write_file(char *name, void *buf, size_t buflen)
+{
+    if (tftp_client.state < TFTP_ST_IDLE) {
+        TFTP_DEBUG("attempt to read file with no connection");
+        return TFTP_ERR_DISCONNECTED;
+    }
+
+    if (tftp_client.state > TFTP_ST_IDLE) {
+        return TFTP_ERR_BUSY;
+    }
+
+    tftp_client.buf = buf;
+    tftp_client.buflen = buflen;
+    tftp_client.block = 1;
+    tftp_client.state = TFTP_ST_WRITE_REQ_SENT;
+    tftp_client.bytes = 0;
+
+    return SYS_ERR_OK;
+}
+
+errval_t tftp_client_read_file(char *path, void *buf, size_t buflen, size_t *ret_size)
+{
+    if (tftp_client.state < TFTP_ST_IDLE) {
+        TFTP_DEBUG("attempt to read file with no connection");
+        return TFTP_ERR_DISCONNECTED;
+    }
+
+    if (tftp_client.state > TFTP_ST_IDLE) {
+        return TFTP_ERR_BUSY;
+    }
+
+    tftp_client.buf = buf;
+    tftp_client.buflen = buflen;
+    tftp_client.block = 1;
+    tftp_client.state = TFTP_ST_READ_REQ_SENT;
+    tftp_client.bytes = 0;
+
+    assert(tftp_client.pcb);
+
+    TFTP_DEBUG("read request of file %s\n", path);
+
+    new_request(path, TFTP_OP_READ_REQ);
+
+    while(tftp_client.state > TFTP_ST_ERROR) {
+        event_dispatch(get_default_waitset());
+    }
+
+    TFTP_DEBUG("tftp read file done.\n");
+
+    if (ret_size) {
+        *ret_size = tftp_client.bytes;
+    }
+
+    if (tftp_client.state == TFTP_ST_ERROR) {
+        tftp_client.state = TFTP_ST_IDLE;
+        return -1;
+    }
+
+    tftp_client.state = TFTP_ST_IDLE;
+
+    return SYS_ERR_OK;
+}
+
+
+
+/**
+ * \brief attempts to initialize a new TFTP connection to a server
+ *
+ * \returns SYS_ERR_OK on success
+ *          TFTP_ERR_* on failure
+ */
+errval_t tftp_client_connect(char *ip, uint16_t port)
+{
+    switch(tftp_client.state) {
+        case TFTP_ST_INVALID :
+            lwip_init_auto();
+            tftp_client.pcb = udp_new();
+            TFTP_DEBUG("new connection from uninitialized state\n");
+            break;
+        case TFTP_ST_CLOSED :
+            TFTP_DEBUG("new connection from closed state\n");
+            tftp_client.pcb = udp_new();
+            break;
+        default:
+            TFTP_DEBUG("connection already established, cannot connect\n");
+            return TFTP_ERR_BUSY;
+    }
+
+    if (tftp_client.pcb == NULL) {
+        return LIB_ERR_MALLOC_FAIL;
+    }
+
+    tftp_client.server_port = port;
+
+    struct in_addr peer_ip_gen;
+    int ret = inet_aton(ip, &peer_ip_gen);
+    if (ret == 0) {
+        TFTP_DEBUG("Invalid IP addr: %s\n", ip);
+        return 1;
+    }
+    tftp_client.server_ip.addr = peer_ip_gen.s_addr;
+
+    TFTP_DEBUG("connecting to %s:%" PRIu16 "\n", ip, port);
+    tftp_client.rcv_pcb = udp_new();
+
+    int r = udp_bind(tftp_client.rcv_pcb, IP_ADDR_ANY, 0);
+    if (r != ERR_OK) {
+        USER_PANIC("UDP bind failed");
+    }
+
+    r = udp_connect(tftp_client.pcb, &tftp_client.server_ip, tftp_client.server_port);
+    if (r != ERR_OK) {
+        USER_PANIC("UDP connect failed");
+    }
+    tftp_client.pcb->local_port = tftp_client.rcv_pcb->local_port;
+
+    TFTP_DEBUG("registering recv handler\n");
+    udp_recv(tftp_client.pcb, tftp_client_recv_handler, NULL);
+    udp_recv(tftp_client.rcv_pcb, tftp_client_recv_handler, NULL);
+
+    tftp_client.state = TFTP_ST_IDLE;
+    tftp_client.mode = TFTP_MODE_OCTET;
+    tftp_client.p = pbuf_alloc(PBUF_TRANSPORT, TFTP_MAX_MSGSIZE, PBUF_POOL);
+    if (!tftp_client.p) {
+        USER_PANIC("no buffer");
+    }
+    tftp_client.ppayload = tftp_client.p->payload;
+    TFTP_DEBUG("all set up. connection idle\n");
+    return SYS_ERR_OK;
+}
+
+errval_t tftp_client_disconnect(void)
+{
+    pbuf_free(tftp_client.p);
+    udp_remove(tftp_client.pcb);
+    udp_remove(tftp_client.rcv_pcb);
+    tftp_client.state = TFTP_ST_CLOSED;
+    return SYS_ERR_OK;
+}
+
+
diff --git a/lib/tftp/common.c b/lib/tftp/common.c
new file mode 100644 (file)
index 0000000..7a3ed73
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * \file
+ * \brief TFTP library
+ */
+
+/*
+ * Copyright (c) 2015 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <barrelfish/barrelfish.h>
+#include <lwip/udp.h>
+
+#include <tftp/tftp.h>
+#include "tftp_internal.h"
+
+
+/*
+ * ------------------------------------------------------------------------------
+ * Ack
+ * ------------------------------------------------------------------------------
+ */
+
+errval_t tftp_send_ack(struct udp_pcb *pcb, uint32_t blockno,
+                       struct ip_addr *addr, u16_t port,
+                       struct pbuf *p, void *payload)
+{
+    TFTP_DEBUG_PACKETS("sending ack(%u)\n", blockno);
+
+    p->len = TFTP_MAX_MSGSIZE;
+    p->tot_len = TFTP_MAX_MSGSIZE;
+    p->payload = payload;
+
+    memset(p->payload, 0, sizeof(uint32_t) + sizeof(uint16_t));
+
+    size_t length = set_opcode(p->payload, TFTP_OP_ACK);
+    length += set_block_no(p->payload + length, blockno);
+
+    p->len = (uint16_t)length +1;
+    p->tot_len = (uint16_t)length+1;
+
+    int r = udp_sendto(pcb, p, addr, port);
+    if (r != ERR_OK) {
+        TFTP_DEBUG("send failed\n");
+    }
+
+    return SYS_ERR_OK;
+}
+
diff --git a/lib/tftp/server.c b/lib/tftp/server.c
new file mode 100644 (file)
index 0000000..afba039
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * \file
+ * \brief TFTP library
+ */
+
+/*
+ * Copyright (c) 2015 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <barrelfish/barrelfish.h>
+
+#include <tftp/tftp.h>
+#include "tftp_internal.h"
+
+
+/**
+ * \brief starts the tftp server on this machine on a given port
+ *
+ * \param ip    ip address to be used
+ * \param port  port to be used
+ * \param cb    callback function called when clients connect
+ *
+ * \return  SYS_ERR_OK on success
+ *          TFTP_ERR_* on failure
+ */
+errval_t tftp_server_accept(char *ip, uint16_t port, tfpt_server_cb_f_t cb)
+{
+    USER_PANIC("NYI");
+    return SYS_ERR_OK;
+}
+
+
+/**
+ * \brief terminates the tftp server connection
+ *
+ * \return  SYS_ERR_OK on success
+ *          TFTP_ERR_* on failure
+ */
+errval_t tftp_server_terminate(void)
+{
+    USER_PANIC("NYI");
+    return SYS_ERR_OK;
+}
diff --git a/lib/tftp/tftp_internal.h b/lib/tftp/tftp_internal.h
new file mode 100644 (file)
index 0000000..57ceb5e
--- /dev/null
@@ -0,0 +1,271 @@
+/**
+ * \file
+ * \brief TFTP library
+ */
+
+/*
+ * Copyright (c) 2015 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef TFTP_INTERNAL_H_
+#define TFTP_INTERNAL_H_ 1
+
+/*
+ * ------------------------------------------------------------------------------
+ * General defines
+ * ------------------------------------------------------------------------------
+ */
+#define TFTP_BLOCKSIZE      512
+#define TFTP_MAX_MSGSIZE    (4 + TFTP_BLOCKSIZE)
+#define TFTP_TIMEOUT        500
+
+
+/*
+ * ------------------------------------------------------------------------------
+ * Debugging options
+ * ------------------------------------------------------------------------------
+ */
+//#define TFTP_DEBUG(x...) debug_printf("[tftp] " x)
+#define TFTP_DEBUG(x...)
+//#define TFTP_DEBUG_PACKETS(x...) debug_printf("[tftp] " x)
+#define TFTP_DEBUG_PACKETS(x...)
+
+/*
+ * ------------------------------------------------------------------------------
+ * operation codes
+ * ------------------------------------------------------------------------------
+ */
+
+///< TFTP Operation Codes
+typedef enum tftp_op {
+    TFTP_OP_INVALID   = 0,  ///< op code is invalid
+    TFTP_OP_READ_REQ  = 1,  ///< read request
+    TFTP_OP_WRITE_REQ = 2,  ///< write request
+    TFTP_OP_DATA      = 3,  ///< data response
+    TFTP_OP_ACK       = 4,  ///< ack response
+    TFTP_OP_ERROR     = 5   ///< error response
+} tpft_op_t;
+
+
+/**
+ * \brief writes the opcode into the buffer
+ *
+ * \param buf       buffer where to write the opcode to
+ * \param opcode    opcode to be written
+ *
+ * \return number of bytes written
+ */
+static inline size_t set_opcode(void *buf, uint16_t opcode)
+{
+    uint8_t *p = buf;
+    *p = ((opcode >> 8) & 0xff); p++;
+    *p = (opcode & 0xff);
+
+    return sizeof(uint16_t);
+}
+
+/**
+ * \brief obtains the opcode from the buffer
+ *
+ * \param buf   buffer where to extract the op code
+ *
+ * \return tftp op code
+ */
+static inline tpft_op_t get_opcode(void *buf)
+{
+    uint8_t *p = buf;
+    uint16_t opcode = (*p) << 8; p++;
+    opcode |= *p;
+
+    return (tpft_op_t)opcode;
+}
+
+/*
+ * ------------------------------------------------------------------------------
+ * Operation codes
+ * ------------------------------------------------------------------------------
+ */
+
+///< TFTP state
+typedef enum tpft_st {
+    TFTP_ST_INVALID,        ///< state is invalid
+    TFTP_ST_CLOSED,         ///< connection is closed
+    TFTP_ST_IDLE,           ///< the client is connected and in the  idle state
+    TFTP_ST_LAST_ACK_SENT,  ///< last ack has been sent
+    TFTP_ST_LAST_DATA_SENT, ///< last data response sent
+    TFTP_ST_ERROR,          ///< tftp request resulted in an error
+    TFTP_ST_READ_REQ_SENT,  ///< a read request has been sent
+    TFTP_ST_WRITE_REQ_SENT, ///< a write request has been sent
+    TFTP_ST_DATA_SENT,      ///< data respnse is being sent
+    TFTP_ST_ACK_SENT,       ///< ack has been sent
+} tftp_st_t;
+
+
+/*
+ * ------------------------------------------------------------------------------
+ * Transfer modes
+ * ------------------------------------------------------------------------------
+ */
+
+///< operation mode of the TFTP connection
+typedef enum tftp_mode {
+    TFTP_MODE_INVALID,      ///< invalid operation mode
+    TFTP_MODE_OCTET,        ///< use octet moded
+    TFTP_MODE_NETASCII,     ///< use netsascii mode
+    TFTP_MODE_MAIL,         ///< use mail mode
+} tftp_mode_t;
+
+/**
+ * \brief sets the transfer mode in a buffer
+ *
+ * \param buf   buffer where to write the transfer mode to
+ * \param mode  transfer mode to write
+ *
+ * \returns number of bytes written
+ */
+static inline size_t set_mode(void *buf, tftp_mode_t mode)
+{
+    switch(mode) {
+        case TFTP_MODE_OCTET:
+            return snprintf(buf, 6, "octet")+1;
+            break;
+        case TFTP_MODE_NETASCII:
+            return snprintf(buf, 6, "netascii")+1;
+            break;
+        case TFTP_MODE_MAIL:
+            return snprintf(buf, 5, "mail")+1;
+            break;
+        default:
+            assert(!"this should not happen");
+            return 0;
+    }
+}
+
+/**
+ * \brief parses the transfer mode from the buffer
+ *
+ * \param buf   buffer to extract the transfer mode from
+ *
+ * \return tftp transfer mode
+ */
+static inline tftp_mode_t get_mode(void *buf)
+{
+    if (strncmp(buf, "octet", 5) == 0) {
+        return TFTP_MODE_OCTET;
+    } else if (strncmp(buf, "netascii", 8) == 0) {
+        return TFTP_MODE_NETASCII;
+    } else if (strncmp(buf, "mail", 4) == 0) {
+        return TFTP_MODE_MAIL;
+    }
+
+    return TFTP_MODE_INVALID;
+}
+
+/*
+ * ------------------------------------------------------------------------------
+ * Error
+ * ------------------------------------------------------------------------------
+ */
+
+///< possible tftp errors in packages
+typedef enum tftp_err {
+    TFTP_ERR_NOT_DEFINED = 0,             ///< not defined
+    TFTP_ERR_NOT_FOUND =  1,              ///< file not found
+    TFTP_ERR_ACCESS_DENIED =  2,          ///< access denied
+    TFTP_ERR_DISK_FULL = 3,               ///< disk is full
+    TFTP_ERR_UNKNOWN_TID =   4,           ///< unkown transfer id
+    TFTP_ERR_ILLEGAL_OP = 5,              ///< illegal operation
+    TFTP_ERR_FILE_EXISTS =  6,            ///< destination file exist
+    TFTP_ERR_NO_SUCH_USER =  7,           ///< no such user
+    TFTP_ERR_INVALID_BUFFER = 0xFFFFFFFF, ///< invalid buffer
+} tftp_err_t;
+
+/**
+ * \brief extracts the error code from an error package
+ *
+ * \param buf       buffer to extract the error code from
+ * \param buflen    length of the buffer in bytes
+ *
+ * \returns tftp error code
+ */
+static inline tftp_err_t get_error(void *buf, size_t buflen) {
+    if (buflen < 5) {
+        return TFTP_ERR_INVALID_BUFFER;
+    }
+    uint8_t *p = buf;
+
+    return (tftp_err_t)((p[2] << 8) + p[3]);
+}
+
+/**
+ * \brief sets the error code in a buffer
+ *
+ * \param buf   buffer where to write the error code
+ * \param error error code to write
+ *
+ * \returns number of bytes written
+ */
+static inline size_t set_error(void *buf, tftp_err_t error)
+{
+    uint8_t *p = buf;
+    p[0] = 0;
+    p[1] = 1;
+    p[2] = ((error >> 8) & 0xff);
+    p[3] = (error & 0xff);
+
+    return 4;
+}
+
+/*
+ * ------------------------------------------------------------------------------
+ * Data packets
+ * ------------------------------------------------------------------------------
+ */
+
+/**
+ * \brief extracts the block number from a buffer
+ *
+ * \param buf       buffer where to extract the block number
+ * \param buflen    length of the buffer
+ *
+ * \return block number or TFTP_BLOCKNO_INVALID
+ */
+static inline uint32_t get_block_no(void *buf, size_t buflen)
+{
+    if (buflen < 4) {
+        return TFTP_ERR_INVALID_BUFFER;
+    }
+
+    uint8_t *data = buf;
+    return (data[2] << 8) + data[3];
+}
+
+/**
+ * \brief sets the block number in a buffer
+ *
+ * \param buf       buffer where to write the block number to
+ * \param blockno   block number to write
+ *
+ * \return  number of bytes written
+ */
+static inline size_t set_block_no(void *buf, uint16_t blockno) {
+    uint8_t *p = buf;
+    *p = (uint8_t)((blockno >> 8) & 0xff); p++;
+    *p = (uint8_t)(blockno & 0xff);
+    return sizeof(uint16_t);
+}
+
+/*
+ * ------------------------------------------------------------------------------
+ * Sending generic messages
+ * ------------------------------------------------------------------------------
+ */
+errval_t tftp_send_ack(struct udp_pcb *pcb, uint32_t blockno,
+                       struct ip_addr *addr, u16_t port,
+                       struct pbuf *p, void *payload);
+#endif /* TFTP_INTERNAL_H_ */