diff --git a/.gitignore b/.gitignore index 9d075da..2ac5bbb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.pcapng loop +loop2 test_arp test_arp_cov report_arp_cov \ No newline at end of file diff --git a/3p/include/tuntap-export.h b/3p/include/tuntap-export.h new file mode 100644 index 0000000..c178e95 --- /dev/null +++ b/3p/include/tuntap-export.h @@ -0,0 +1,42 @@ + +#ifndef TUNTAP_EXPORT_H +#define TUNTAP_EXPORT_H + +#ifdef TUNTAP_STATIC_DEFINE +# define TUNTAP_EXPORT +# define TUNTAP_NO_EXPORT +#else +# ifndef TUNTAP_EXPORT +# ifdef tuntap_EXPORTS + /* We are building this library */ +# define TUNTAP_EXPORT +# else + /* We are using this library */ +# define TUNTAP_EXPORT +# endif +# endif + +# ifndef TUNTAP_NO_EXPORT +# define TUNTAP_NO_EXPORT +# endif +#endif + +#ifndef TUNTAP_DEPRECATED +# define TUNTAP_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef TUNTAP_DEPRECATED_EXPORT +# define TUNTAP_DEPRECATED_EXPORT TUNTAP_EXPORT TUNTAP_DEPRECATED +#endif + +#ifndef TUNTAP_DEPRECATED_NO_EXPORT +# define TUNTAP_DEPRECATED_NO_EXPORT TUNTAP_NO_EXPORT TUNTAP_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef TUNTAP_NO_DEPRECATED +# define TUNTAP_NO_DEPRECATED +# endif +#endif + +#endif /* TUNTAP_EXPORT_H */ diff --git a/3p/include/tuntap.h b/3p/include/tuntap.h new file mode 100644 index 0000000..9fedbde --- /dev/null +++ b/3p/include/tuntap.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2012 Tristan Le Guern + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#ifndef LIBTUNTAP_H_ +# define LIBTUNTAP_H_ + +# define TUNTAP_ID_MAX 256 +# define TUNTAP_ID_ANY 257 + +# define TUNTAP_MODE_ETHERNET 0x0001 +# define TUNTAP_MODE_TUNNEL 0x0002 +# define TUNTAP_MODE_PERSIST 0x0004 + +# define TUNTAP_LOG_NONE 0x0000 +# define TUNTAP_LOG_DEBUG 0x0001 +# define TUNTAP_LOG_INFO 0x0002 +# define TUNTAP_LOG_NOTICE 0x0004 +# define TUNTAP_LOG_WARN 0x0008 +# define TUNTAP_LOG_ERR 0x0016 + +/* Versioning: 0xMMmm, with 'M' for major and 'm' for minor */ +# define TUNTAP_VERSION_MAJOR 0 +# define TUNTAP_VERSION_MINOR 4 +# define TUNTAP_VERSION ((TUNTAP_VERSION_MAJOR<<8)|TUNTAP_VERSION_MINOR) + +# ifdef __cplusplus +extern "C" { +# endif + +/* Forward declaration of struct device */ +struct device; + +# if defined Windows +typedef void* t_tun; +# else /* Unix */ +typedef int t_tun; +# endif + +/* User definable log callback */ +typedef void (*t_tuntap_log)(int, const char *); + +/* Portable "public" functions */ +TUNTAP_EXPORT struct device *tuntap_init(void); +TUNTAP_EXPORT int tuntap_version(void); +TUNTAP_EXPORT void tuntap_destroy(struct device *); +TUNTAP_EXPORT void tuntap_release(struct device *); +TUNTAP_EXPORT int tuntap_start(struct device *, int, int); +TUNTAP_EXPORT char *tuntap_get_ifname(struct device *); +TUNTAP_EXPORT int tuntap_set_ifname(struct device *, const char *); +TUNTAP_EXPORT char *tuntap_get_hwaddr(struct device *); +TUNTAP_EXPORT int tuntap_set_hwaddr(struct device *, const char *); +TUNTAP_EXPORT int tuntap_set_descr(struct device *, const char *); +TUNTAP_EXPORT char *tuntap_get_descr(struct device *); +TUNTAP_EXPORT int tuntap_up(struct device *); +TUNTAP_EXPORT int tuntap_down(struct device *); +TUNTAP_EXPORT int tuntap_get_mtu(struct device *); +TUNTAP_EXPORT int tuntap_set_mtu(struct device *, int); +TUNTAP_EXPORT int tuntap_set_ip(struct device *, const char *, int); +TUNTAP_EXPORT int tuntap_read(struct device *, void *, size_t); +TUNTAP_EXPORT int tuntap_write(struct device *, void *, size_t); +TUNTAP_EXPORT int tuntap_get_readable(struct device *); +TUNTAP_EXPORT int tuntap_set_nonblocking(struct device *dev, int); +TUNTAP_EXPORT int tuntap_set_debug(struct device *dev, int); +TUNTAP_EXPORT t_tun tuntap_get_fd(struct device *); + +/* Logging functions */ +TUNTAP_EXPORT void tuntap_log_set_cb(t_tuntap_log cb); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/3p/lib/libtuntap.a b/3p/lib/libtuntap.a new file mode 100644 index 0000000..11c7c8b Binary files /dev/null and b/3p/lib/libtuntap.a differ diff --git a/3p/libtuntap b/3p/libtuntap new file mode 160000 index 0000000..ef7ed7b --- /dev/null +++ b/3p/libtuntap @@ -0,0 +1 @@ +Subproject commit ef7ed7b4441624f825c1798ae79e0ddfc8edd4cb diff --git a/include/microtcp.h b/include/microtcp.h index 796dac9..9a9522d 100644 --- a/include/microtcp.h +++ b/include/microtcp.h @@ -10,12 +10,6 @@ typedef struct microtcp_socket_t microtcp_socket_t; #define MICROTCP_MAX_BUFFERS 8 #define MICROTCP_MAX_SOCKETS 32 -typedef struct { - uint8_t data[6]; -} microtcp_mac_t; - -typedef uint32_t microtcp_ip_t; - typedef enum { MICROTCP_ERRCODE_NONE = 0, @@ -30,7 +24,6 @@ typedef enum { // Returned by microtcp_accept MICROTCP_ERRCODE_NOTLISTENER, MICROTCP_ERRCODE_CANTBLOCK, - MICROTCP_ERRCODE_NOTHINGTOACCEPT, // Returned by microtcp_recv and microtcp_send MICROTCP_ERRCODE_NOTCONNECTION, @@ -44,14 +37,18 @@ typedef struct { int (*recv)(void *data, void *dst, size_t len); } microtcp_callbacks_t; -microtcp_t *microtcp_create(); -microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac, microtcp_callbacks_t callbacks); +#ifdef MICROTCP_USING_TAP +bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac, microtcp_callbacks_t *callbacks); +microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip, const char *tap_mac, const char *stack_mac); +#endif + +microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_callbacks_t callbacks); void microtcp_destroy(microtcp_t *mtcp); const char *microtcp_strerror(microtcp_errcode_t errcode); microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port, microtcp_errcode_t *errcode); microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, bool no_block, microtcp_errcode_t *errcode); void microtcp_close(microtcp_socket_t *socket); -size_t microtcp_send(microtcp_socket_t *socket, const void *src, size_t len, microtcp_errcode_t *errcode); -size_t microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len, microtcp_errcode_t *errcode); +size_t microtcp_send(microtcp_socket_t *socket, const void *src, size_t len, bool no_block, microtcp_errcode_t *errcode); +size_t microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len, bool no_block, microtcp_errcode_t *errcode); void microtcp_step(microtcp_t *mtcp); void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len); \ No newline at end of file diff --git a/makefile b/makefile index 5a7be94..224689e 100644 --- a/makefile +++ b/makefile @@ -1,7 +1,10 @@ -all: loop test_net test_arp report_arp_cov +all: loop2 + +loop2: + gcc src/arp.c src/ip.c src/icmp.c src/tcp.c src/microtcp.c test/loop2.c -o loop2 -Wall -Wextra -g -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_USING_TAP -pthread -ltuntap -Iinclude/ -I3p/include/ -L3p/lib/ loop: - gcc src/arp.c src/ip.c src/icmp.c src/tcp.c src/microtcp.c src/microtcp_linux.c test/loop.c -o loop -Wall -Wextra -g -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_LINUX -pthread -Iinclude/ -fsanitize=thread + gcc src/arp.c src/ip.c src/icmp.c src/tcp.c src/microtcp.c src/microtcp_linux.c test/loop.c -o loop -Wall -Wextra -g -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_LINUX -pthread -Iinclude/ # -fsanitize=thread test_arp: gcc src/arp.c test/test_arp.c test/test_arp_util.c -o test_arp -Wall -Wextra -g -Iinclude/ @@ -16,7 +19,4 @@ report_arp_cov: test_arp_cov rm *.gcda *.gcno coverage.info clean: - rm -f *.gcda *.gcno coverage.info - rm -f test_arp test_arp_cov - rm -f loop - rm -rf report_arp_cov \ No newline at end of file + rm -f loop \ No newline at end of file diff --git a/src/defs.h b/src/defs.h index 3d02022..f77cde6 100644 --- a/src/defs.h +++ b/src/defs.h @@ -16,5 +16,6 @@ static_assert(sizeof(ip_address_t) == 4); #define MAC_BROADCAST (mac_address_t) {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}} #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif /* MICROTCP_DEFS_H */ \ No newline at end of file diff --git a/src/ip.h b/src/ip.h index 715282c..d3cfa5e 100644 --- a/src/ip.h +++ b/src/ip.h @@ -1,18 +1,16 @@ #include #include #include -#include #include "defs.h" #include "icmp.h" #define IP_PLUGGED_PROTOCOLS_MAX 4 typedef struct { -#if __BYTE_ORDER == __LITTLE_ENDIAN +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ uint8_t header_length: 4; uint8_t version: 4; -#endif -#if __BYTE_ORDER == __BIG_ENDIAN +#else uint8_t version: 4; uint8_t header_length: 4; #endif diff --git a/src/microtcp.c b/src/microtcp.c index 595a96a..7a2f6cd 100644 --- a/src/microtcp.c +++ b/src/microtcp.c @@ -4,12 +4,16 @@ #include #include #include - +#include // inet_pton #include "ip.h" #include "arp.h" #include "tcp.h" #include +#ifdef MICROTCP_USING_TAP +#include +#endif + #ifdef MICROTCP_BACKGROUND_THREAD #include #endif @@ -22,23 +26,8 @@ #endif #ifdef MICROTCP_BACKGROUND_THREAD -#define LOCK_WHEN_THREADED(mtcp) do { \ - fprintf(stderr, "--- %s before lock\n", __func__); \ - fflush(stderr); \ - pthread_mutex_lock(&(mtcp)->lock); \ - fprintf(stderr, "--- %s after lock\n", __func__); \ - fflush(stderr); \ - } while (0); - -#define UNLOCK_WHEN_THREADED(mtcp) do { \ - fprintf(stderr, "--- %s before unlock\n", __func__); \ - fflush(stderr); \ - pthread_mutex_unlock(&(mtcp)->lock); \ - fprintf(stderr, "--- %s after unlock\n", __func__); \ - fflush(stderr); \ - } while (0); - -//#define UNLOCK_WHEN_THREADED(mtcp) do { pthread_mutex_unlock(&(mtcp)->lock); } while (0); +#define LOCK_WHEN_THREADED(mtcp) do { pthread_mutex_lock(&(mtcp)->lock); } while (0); +#define UNLOCK_WHEN_THREADED(mtcp) do { pthread_mutex_unlock(&(mtcp)->lock); } while (0); #else #define LOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0); #define UNLOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0); @@ -68,7 +57,13 @@ struct microtcp_socket_t { tcp_connection_t *connection; }; #ifdef MICROTCP_BACKGROUND_THREAD - pthread_cond_t something_to_accept; + union { + pthread_cond_t something_to_accept; + struct { + pthread_cond_t something_to_recv; + pthread_cond_t something_to_send; + }; + }; #endif }; @@ -110,7 +105,6 @@ const char *microtcp_strerror(microtcp_errcode_t errcode) case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error"; case MICROTCP_ERRCODE_NOTLISTENER: return "Invalid operation on a non-listener socket"; case MICROTCP_ERRCODE_CANTBLOCK: return "Can't execute a blocking call for this function"; - case MICROTCP_ERRCODE_NOTHINGTOACCEPT: return "Accept queue is empty"; case MICROTCP_ERRCODE_NOTCONNECTION: return "Invalid operation on a non-connection socket"; } return "???"; @@ -353,7 +347,7 @@ process_packet(microtcp_t *mtcp, const void *packet, size_t len) default: // Unsupported ethertype - MICROTCP_DEBUG_LOG("Ignoring packet with ethertype %4x", frame->proto); + //MICROTCP_DEBUG_LOG("Ignoring packet with ethertype %4x", frame->proto); break; } } @@ -390,7 +384,6 @@ void microtcp_step(microtcp_t *mtcp) mtcp->last_update_time = current_time; } } -unlock_and_exit: UNLOCK_WHEN_THREADED(mtcp); } @@ -404,19 +397,107 @@ static void *loop(void *data) } #endif -microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac, +static bool is_hex_digit(char c) +{ + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); +} + +static int int_from_hex_digit(char c) +{ + assert(is_hex_digit(c)); + if (c >= 'A' || c <= 'F') + return c - 'A' + 10; + if (c >= 'a' || c <= 'f') + return c - 'a' + 10; + return c - '0'; +} + +static bool parse_mac(const char *src, size_t len, + mac_address_t *mac) +{ + if (src == NULL || len != 17 + || !is_hex_digit(src[0]) + || !is_hex_digit(src[1]) + || src[2] != ':' + || !is_hex_digit(src[3]) + || !is_hex_digit(src[4]) + || src[5] != ':' + || !is_hex_digit(src[6]) + || !is_hex_digit(src[7]) + || src[8] != ':' + || !is_hex_digit(src[9]) + || !is_hex_digit(src[10]) + || src[11] != ':' + || !is_hex_digit(src[12]) + || !is_hex_digit(src[13]) + || src[14] != ':' + || !is_hex_digit(src[15]) + || !is_hex_digit(src[16])) + return false; + + static const char max_char_map[] = "0123456789ABCDEF"; + + if (mac) { + mac->data[0] = max_char_map[int_from_hex_digit(src[ 0])] << 4 + | max_char_map[int_from_hex_digit(src[ 1])]; + mac->data[1] = max_char_map[int_from_hex_digit(src[ 3])] << 4 + | max_char_map[int_from_hex_digit(src[ 4])]; + mac->data[2] = max_char_map[int_from_hex_digit(src[ 6])] << 4 + | max_char_map[int_from_hex_digit(src[ 7])]; + mac->data[3] = max_char_map[int_from_hex_digit(src[ 9])] << 4 + | max_char_map[int_from_hex_digit(src[10])]; + mac->data[4] = max_char_map[int_from_hex_digit(src[12])] << 4 + | max_char_map[int_from_hex_digit(src[13])]; + mac->data[5] = max_char_map[int_from_hex_digit(src[15])] << 4 + | max_char_map[int_from_hex_digit(src[16])]; + } + return true; +} + +static mac_address_t generate_random_mac() +{ + mac_address_t mac = { + .data = { + rand() & 0xff, + rand() & 0xff, + rand() & 0xff, + rand() & 0xff, + rand() & 0xff, + rand() & 0xff, + }, + }; + return mac; +} + +static bool parse_ip(const char *ip, ip_address_t *parsed_ip) +{ + return inet_pton(AF_INET, ip, parsed_ip) == 1; +} + +microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_callbacks_t callbacks) { + mac_address_t parsed_mac; + if (mac == NULL) { + // Generate a random MAC + parsed_mac = generate_random_mac(); + } else { + if (!parse_mac(mac, mac ? strlen(mac) : 0, &parsed_mac)) + return NULL; + } + + ip_address_t parsed_ip; + if (!parse_ip(ip, &parsed_ip)) + return NULL; + microtcp_t *mtcp = malloc(sizeof(microtcp_t)); if (mtcp == NULL) return NULL; - mac_address_t mac2; - static_assert(sizeof(mac2) == sizeof(mac)); - memcpy(&mac2, &mac, sizeof(mac)); - - mtcp->ip = ip; - mtcp->mac = mac2; + mtcp->ip = parsed_ip; + mtcp->mac = parsed_mac; mtcp->callbacks = callbacks; mtcp->last_update_time = time(NULL); @@ -443,15 +524,15 @@ microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].prev = NULL; mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].next = NULL; - ip_init(&mtcp->ip_state, ip, mtcp, send_ip_packet); + ip_init(&mtcp->ip_state, parsed_ip, mtcp, send_ip_packet); if (!ip_plug_protocol(&mtcp->ip_state, IP_PROTOCOL_TCP, &mtcp->tcp_state, tcp_process_segment_wrapper)) { free(mtcp); return NULL; } - arp_init(&mtcp->arp_state, ip, mac2, mtcp, send_arp_packet); + arp_init(&mtcp->arp_state, parsed_ip, parsed_mac, mtcp, send_arp_packet); - tcp_init(&mtcp->tcp_state, (tcp_callbacks_t) { + tcp_init(&mtcp->tcp_state, parsed_ip, (tcp_callbacks_t) { .data = mtcp, .send = send_tcp_segment, }); @@ -496,6 +577,51 @@ microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac return mtcp; } + +#ifdef MICROTCP_USING_TAP + +bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac, + microtcp_callbacks_t *callbacks) +{ + assert(ip); + + struct device *dev = tuntap_init(); + if (!dev) + return false; + + int netmask = 24; // TODO: Make this configurable + + if (tuntap_start(dev, TUNTAP_MODE_ETHERNET, TUNTAP_ID_ANY) || + tuntap_set_ip(dev, ip, netmask) || + tuntap_set_hwaddr(dev, mac ? mac : "random") || + tuntap_up(dev)) { + tuntap_release(dev); + return false; + } + + *callbacks = (microtcp_callbacks_t) { + .data = dev, + .free = tuntap_release, + .recv = tuntap_read, + .send = tuntap_write, + }; + + return true; +} + +microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip, + const char *tap_mac, const char *stack_mac) +{ + microtcp_callbacks_t callbacks; + if (!microtcp_callbacks_create_for_tap(tap_ip, tap_mac, &callbacks)) + return NULL; + microtcp_t *mtcp = microtcp_create_using_callbacks(stack_ip, stack_mac, callbacks); + if (!mtcp) + callbacks.free(callbacks.data); + return mtcp; +} +#endif + void microtcp_destroy(microtcp_t *mtcp) { #ifdef MICROTCP_BACKGROUND_THREAD @@ -522,12 +648,6 @@ pop_socket_struct_from_free_list(microtcp_t *mtcp) return socket; } -static bool -have_unused_socket_structs(microtcp_t *mtcp) -{ - return mtcp->free_socket_list != NULL; -} - static void push_unlinked_socket_into_used_list(microtcp_socket_t *socket) { @@ -647,6 +767,26 @@ void microtcp_close(microtcp_socket_t *socket) UNLOCK_WHEN_THREADED(mtcp); } +static void ready_to_recv(void *data) +{ +#ifdef MICROTCP_BACKGROUND_THREAD + microtcp_socket_t *socket = data; + pthread_cond_signal(&socket->something_to_recv); +#else + (void) data; +#endif +} + +static void ready_to_send(void *data) +{ +#ifdef MICROTCP_BACKGROUND_THREAD + microtcp_socket_t *socket = data; + pthread_cond_signal(&socket->something_to_send); +#else + (void) data; +#endif +} + microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, bool no_block, microtcp_errcode_t *errcode) @@ -662,40 +802,26 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, goto unlock_and_exit; // Can't accept from a non-listening socket } - if (!have_unused_socket_structs(mtcp)) { + socket2 = pop_socket_struct_from_free_list(mtcp); + if (!socket2) { errcode2 = MICROTCP_ERRCODE_SOCKETLIMIT; goto unlock_and_exit; // Socket limit reached } - tcp_connection_t *connection = tcp_listener_accept(socket->listener); - - if (!connection) { + tcp_connection_t *connection = tcp_listener_accept(socket->listener, socket2, ready_to_recv, ready_to_send); #ifdef MICROTCP_BACKGROUND_THREAD - - if (no_block) { - errcode2 = MICROTCP_ERRCODE_NOTHINGTOACCEPT; - goto unlock_and_exit; - } - - do { - pthread_cond_wait(&socket->something_to_accept, &mtcp->lock); - connection = tcp_listener_accept(socket->listener); - } while (!connection); - -#else - - if (!no_block) - errcode2 = MICROTCP_ERRCODE_CANTBLOCK; - else - errcode2 = MICROTCP_ERRCODE_NOTHINGTOACCEPT; - goto unlock_and_exit; - -#endif + while (!connection && !no_block) { + pthread_cond_wait(&socket->something_to_accept, &mtcp->lock); + connection = tcp_listener_accept(socket->listener, socket2, ready_to_recv, ready_to_send); } - - socket2 = pop_socket_struct_from_free_list(mtcp); - assert(socket2); // Because we checked at the start +#else + if (!connection && !no_block) { + push_unlinked_socket_into_free_list(mtcp, socket2); + errcode2 = MICROTCP_ERRCODE_CANTBLOCK; + goto unlock_and_exit; + } +#endif socket2->mtcp = mtcp; socket2->prev = NULL; @@ -703,6 +829,21 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, socket2->type = SOCKET_CONNECTION; socket2->connection = connection; +#ifdef MICROTCP_BACKGROUND_THREAD + if (pthread_cond_init(&socket2->something_to_recv, NULL)) { + errcode2 = MICROTCP_ERRCODE_BADCONDVAR; + push_unlinked_socket_into_free_list(mtcp, socket2); + tcp_connection_destroy(connection); + goto unlock_and_exit; + } + if (pthread_cond_init(&socket2->something_to_send, NULL)) { + errcode2 = MICROTCP_ERRCODE_BADCONDVAR; + push_unlinked_socket_into_free_list(mtcp, socket2); + tcp_connection_destroy(connection); + goto unlock_and_exit; + } +#endif + push_unlinked_socket_into_used_list(socket2); } @@ -717,6 +858,7 @@ unlock_and_exit: size_t microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len, + bool no_block, microtcp_errcode_t *errcode) { if (!socket || socket->type != SOCKET_CONNECTION) { @@ -725,19 +867,37 @@ size_t microtcp_recv(microtcp_socket_t *socket, return 0; } + size_t num; microtcp_t *mtcp = socket->mtcp; + microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; LOCK_WHEN_THREADED(mtcp); - size_t num = tcp_connection_recv(socket->connection, dst, len); + { + num = tcp_connection_recv(socket->connection, dst, len); + +#ifdef MICROTCP_BACKGROUND_THREAD + while (num == 0 && !no_block) { + pthread_cond_wait(&socket->something_to_recv, &mtcp->lock); + num = tcp_connection_recv(socket->connection, dst, len); + } +#else + if (num == 0 && !no_block) { + errcode2 = MICROTCP_ERRCODE_CANTBLOCK; + goto unlock_and_exit; + } +#endif + } +unlock_and_exit: UNLOCK_WHEN_THREADED(mtcp); if (errcode) - *errcode = MICROTCP_ERRCODE_NONE; + *errcode = errcode2; return num; } size_t microtcp_send(microtcp_socket_t *socket, const void *src, size_t len, + bool no_block, microtcp_errcode_t *errcode) { if (!socket || socket->type != SOCKET_CONNECTION) { @@ -746,13 +906,33 @@ size_t microtcp_send(microtcp_socket_t *socket, return 0; } + + size_t num; microtcp_t *mtcp = socket->mtcp; + microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; LOCK_WHEN_THREADED(mtcp); - size_t num = tcp_connection_send(socket->connection, src, len); + { + num = tcp_connection_send(socket->connection, src, len); + +#ifdef MICROTCP_BACKGROUND_THREAD + while (num == 0 && !no_block) { + pthread_cond_wait(&socket->something_to_send, &mtcp->lock); + num = tcp_connection_send(socket->connection, src, len); + } +#else + if (num == 0 && !no_block) { + errcode2 = MICROTCP_ERRCODE_CANTBLOCK; + goto unlock_and_exit; + } +#endif + + } + +unlock_and_exit: UNLOCK_WHEN_THREADED(mtcp); if (errcode) - *errcode = MICROTCP_ERRCODE_NONE; + *errcode = errcode2; return num; } diff --git a/src/microtcp_linux.c b/src/microtcp_linux.c index 3e453e9..1c54f7a 100644 --- a/src/microtcp_linux.c +++ b/src/microtcp_linux.c @@ -1,3 +1,4 @@ +/* #ifdef MICROTCP_LINUX #include @@ -85,7 +86,7 @@ static void free_callback(void *data) close(tap_fd); } -/* + static bool get_ip_address(const char *dev, microtcp_ip_t *ip) { int fd = socket(AF_INET, SOCK_DGRAM, 0); @@ -104,7 +105,7 @@ static bool get_ip_address(const char *dev, microtcp_ip_t *ip) close(fd); return true; } -*/ + static bool get_mac_address(const char *dev, microtcp_mac_t *mac) { diff --git a/src/tcp.c b/src/tcp.c index aadc24b..998af39 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -1,4 +1,5 @@ #include +#include #include #include "tcp.h" @@ -15,8 +16,9 @@ static int tcp_send(tcp_state_t *tcp_state, ip_address_t ip, return tcp_state->callbacks.send(tcp_state->callbacks.data, ip, src, len); } -void tcp_init(tcp_state_t *tcp_state, tcp_callbacks_t callbacks) +void tcp_init(tcp_state_t *tcp_state, ip_address_t ip, tcp_callbacks_t callbacks) { + tcp_state->ip = ip; tcp_state->callbacks = callbacks; for (size_t i = 0; i < TCP_MAX_SOCKETS-1; i++) @@ -53,30 +55,47 @@ connection_create_waiting_for_ack(tcp_listener_t *listener, tcp_state_t *state = listener->state; // Pop a connection structure from the free list - if (state->free_connection_list == NULL) - // ERROR: Reached connection limit - return NULL; - tcp_connection_t *connection = state->free_connection_list; - state->free_connection_list = connection->next; + tcp_connection_t *connection; + { + if (state->free_connection_list == NULL) + // ERROR: Reached connection limit + return NULL; + connection = state->free_connection_list; + state->free_connection_list = connection->next; + } // Initialize connection structure - connection->listener = listener; - connection->seq_no = seq_no; - connection->ack_no = ack_no; - connection->peer_port = peer_port; - connection->peer_ip = peer_ip; - connection->in_used = 0; - connection->out_used = 0; - connection->prev = NULL; - connection->next = NULL; + { + connection->listener = listener; + + connection->callback_data = NULL; + connection->callback_ready_to_recv = NULL; + connection->callback_ready_to_send = NULL; + connection->peer_port = peer_port; + connection->peer_ip = peer_ip; + + connection->rcv_unread = ack_no; + connection->rcv_nxt = ack_no; + connection->rcv_wnd = TCP_INPUT_BUFFER_SIZE; + + connection->snd_una = seq_no; + connection->snd_wnd = 0; + connection->snd_nxt = 0; + + connection->prev = NULL; + connection->next = NULL; + } // Append the connection to the list of connections // waiting for the ACK message - if (listener->connections_waiting_for_ack) - listener->connections_waiting_for_ack->prev = connection; - connection->next = listener->connections_waiting_for_ack; - listener->connections_waiting_for_ack = connection; + { + if (listener->connections_waiting_for_ack) + listener->connections_waiting_for_ack->prev = connection; + connection->prev = NULL; + connection->next = listener->connections_waiting_for_ack; + listener->connections_waiting_for_ack = connection; + } return connection; } @@ -84,11 +103,8 @@ connection_create_waiting_for_ack(tcp_listener_t *listener, static tcp_listener_t* find_listener_with_port(tcp_state_t *state, uint16_t port) { - TCP_DEBUG_LOG("Looking for listener with port %d", port); - tcp_listener_t *cursor = state->used_listener_list; while (cursor) { - TCP_DEBUG_LOG("port=%d, seeking=%d", cursor->port, port); if (cursor->port == port) return cursor; cursor = cursor->next; @@ -121,31 +137,182 @@ static connection_state_t find_connection_associated_to(tcp_listener_t *listener { tcp_connection_t *connection2 = find_connection(listener->connections, peer_ip, peer_port); if (connection2) { - *connection = connection2; + if (connection) + *connection = connection2; return SOCKET_IDLE; } connection2 = find_connection(listener->connections_waiting_for_accept_head, peer_ip, peer_port); if (connection2) { - *connection = connection2; + if (connection) + *connection = connection2; return SOCKET_IDLE; } connection2 = find_connection(listener->connections_waiting_for_ack, peer_ip, peer_port); if (connection2) { - *connection = connection2; + if (connection) + *connection = connection2; return SOCKET_WAIT; } - *connection = NULL; + if (connection) + *connection = NULL; return SOCKET_NONE; } -static uint32_t choose_ack() +static uint32_t choose_sequence_no() { return 0; } +typedef struct { + ip_address_t src_addr; + ip_address_t dst_addr; + uint8_t reserved; + uint8_t protocol; + uint16_t tcp_length; +} tcp_pseudoheader_t; + +static uint16_t +calculate_checksum_tcp(const void *a, const void *b, + size_t a_len, size_t b_len) +{ + assert((a_len & 1) == 0 + && (b_len & 1) == 0); + + const uint16_t *a2 = a; + const uint16_t *b2 = b; + + uint32_t sum = 0xffff; + for (size_t i = 0; i < a_len/2; i++) { + sum += ntohs(a2[i]); + if (sum > 0xffff) + sum -= 0xffff; + } + for (size_t i = 0; i < b_len/2; i++) { + sum += ntohs(b2[i]); + if (sum > 0xffff) + sum -= 0xffff; + } + + return htons(~sum); +} + +static void +move_connection_from_wait_for_ack_to_wait_for_accept(tcp_connection_t *connection) +{ + tcp_listener_t *listener = connection->listener; + + // Unlink it from the current list + if (connection->prev) + connection->prev->next = connection->next; + else + listener->connections_waiting_for_ack = connection->next; + + if (connection->next) + connection->next->prev = connection->prev; + + + // Push it to the new one + connection->prev = NULL; + connection->next = listener->connections_waiting_for_accept_head; + + if (listener->connections_waiting_for_accept_head) + // Accept queue isn't empty + listener->connections_waiting_for_accept_head->prev = connection; + else + // Accept queue is empty + listener->connections_waiting_for_accept_tail = connection; + listener->connections_waiting_for_accept_head = connection; + + + if (listener->callback_ready_to_accept) + listener->callback_ready_to_accept(listener->callback_data); +} + +static void emit_segment(tcp_connection_t *connection, bool ack, bool syn, size_t payload) +{ + tcp_listener_t *listener = connection->listener; + tcp_state_t *state = listener->state; + + size_t payload_being_sent = MIN(payload, connection->snd_wnd); + size_t total_segment_size = sizeof(tcp_segment_t) + payload_being_sent; + + uint8_t flags = 0; + uint32_t ack_no = 0; + if (ack) { + flags |= TCP_FLAG_ACK; + ack_no = connection->rcv_nxt; + } + if (syn) + flags |= TCP_FLAG_SYN; + + uint32_t seq_no = connection->snd_una; + //if (payload_being_sent > 0) + // seq_no++; + + connection->out_header = (tcp_segment_t) { + .src_port = htons(listener->port), + .dst_port = htons(connection->peer_port), + .flags = flags, + .seq_no = htonl(seq_no), + .ack_no = htonl(ack_no), + .offset = 5, // No options + .unused = 0, + .window = htons(connection->rcv_wnd), + .checksum = 0, // Will be calculated later + .urgent_pointer = 0, + }; + + tcp_pseudoheader_t pseudo_header = { + .src_addr = state->ip, + .dst_addr = connection->peer_ip, + .reserved = 0, + .protocol = 6, // TCP + .tcp_length = htons(total_segment_size), + }; + + connection->out_header.checksum = calculate_checksum_tcp(&pseudo_header, &connection->out_header, sizeof(pseudo_header), total_segment_size); + + int result = tcp_send(state, connection->peer_ip, &connection->out_header, total_segment_size); + + if (result < 0) { + // It wasn't possible to send out bytes. We'll try again later! + } else { + size_t actually_sent_bytes = (size_t) result; + + if (actually_sent_bytes < sizeof(tcp_segment_t)) { + // Not even the TCP header was sent. I hope this + // doesn't ever happen! + assert(0); + } else { + size_t actually_sent_payload_bytes = actually_sent_bytes - sizeof(tcp_segment_t); + connection->snd_nxt = MAX(connection->snd_nxt, connection->snd_una + actually_sent_payload_bytes); + } + } + +} + +static void handle_received_data(tcp_connection_t *connection, + const void *data, size_t size) +{ + size_t considered = MIN(size, connection->rcv_wnd); + + if (considered > 0) { + size_t input_buffer_usage = TCP_INPUT_BUFFER_SIZE - connection->rcv_wnd; + memcpy(connection->in_buffer + input_buffer_usage, data, considered); + connection->rcv_wnd -= considered; + connection->rcv_nxt += considered; + + emit_segment(connection, true, false, SIZE_MAX); + + // Data is ready to be received by the parent application + if (connection->callback_ready_to_recv) + connection->callback_ready_to_recv(connection->callback_data); + } +} + void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_segment_t *segment, size_t len) { @@ -171,119 +338,96 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_connection_t *connection; connection_state_t connection_state = find_connection_associated_to(listener, sender, reordered_src_port, &connection); + + if (segment->flags & TCP_FLAG_SYN) { - if ((segment->flags & TCP_FLAG_SYN) && !(segment->flags & TCP_FLAG_ACK)) { + if (segment->flags & TCP_FLAG_ACK) { - /* Connection request */ + // Drop the packet. We only do servers for now! + #warning "TODO: Handle TCP second message of three way handshake" - if (connection_state != SOCKET_NONE) { - // Peer wants to connect, but a connection was already created.. - // What to do? - #warning "TODO: Handle case where an existing connection recieved the SYN message" - return; + } else { + + /* Connection request */ + + if (connection_state != SOCKET_NONE) { + // Peer wants to connect, but a connection was already created.. + // What to do? + #warning "TODO: Handle case where an existing connection recieved the SYN message" + return; + } + + // Temporary + uint32_t seq_no = choose_sequence_no(); + uint32_t ack_no = ntohl(segment->seq_no)+1; + + tcp_connection_t *connection = connection_create_waiting_for_ack(listener, seq_no, ack_no, sender, reordered_src_port); + if (connection == NULL) { + // ERROR: Socket limit reached. Drop the connection silently + #warning "TODO: Handle connection limit reached (RST?)" + return; + } + + emit_segment(connection, true, true, 0); + connection->snd_una++; } - // Temporary - uint32_t seq_no = ntohs(segment->seq_no); - uint32_t ack_no = choose_ack(); - - tcp_connection_t *connection = connection_create_waiting_for_ack(listener, seq_no, ack_no, sender, reordered_dst_port); - if (connection == NULL) { - // ERROR: Socket limit reached. Drop the connection silently - #warning "TODO: Handle connection limit reached (RST?)" - return; - } - - tcp_segment_t segment2 = { - .src_port = segment->dst_port, - .dst_port = segment->src_port, - .flags = TCP_FLAG_SYN | TCP_FLAG_ACK, // No need for fixing endianess, it's just one byte! - .seq_no = htons(ack_no), - .ack_no = htons(seq_no), - .offset = 5, - .unused = 0, - .window = htons(TCP_INPUT_BUFFER_SIZE - connection->in_used), - .checksum = 0, - .urgent_pointer = 0, - }; - - #warning "TODO: Calculare checksum" - - tcp_send(state, sender, &segment2, sizeof(segment2)); - - #warning "TODO: Handle TCP connection creation" - - } else if ((segment->flags & TCP_FLAG_SYN) && (segment->flags & TCP_FLAG_ACK)) { - - // Drop the packet. We only do servers for now! - #warning "TODO: Handle TCP second message of three way handshake" - - } else if (!(segment->flags & TCP_FLAG_SYN) && (segment->flags & TCP_FLAG_ACK)) { - - if (connection_state != SOCKET_WAIT) { - // Either there is no connection or the connection wasn't - // waiting for an ACK segment. What to do? - #warning "TODO: Handle case where an existing connection recieved the SYN message" - return; - } - - // Move connection from the waiting-for-ack list - // to the waiting-for-accept queue. - - // Unlink it from the current list - { - if (connection->prev) - connection->prev->next = connection->next; - else - listener->connections_waiting_for_ack = connection->next; - - if (connection->next) - connection->next->prev = connection->prev; - } - - // Push it to the new one - { - connection->prev = NULL; - connection->next = listener->connections_waiting_for_accept_head; - - if (listener->connections_waiting_for_accept_head) - // Accept queue isn't empty - listener->connections_waiting_for_accept_head->prev = connection; - else - // Accept queue is empty - listener->connections_waiting_for_accept_tail = connection; - listener->connections_waiting_for_accept_head = connection; - } - - if (listener->callback) - listener->callback(listener->data); - - // TODO: What about the payload? - #warning "TODO: Handle payload of TCP ACK message" - } else { - - if (connection_state != SOCKET_IDLE) { - // Either there is no connection associated to this peer - // at this port, or the connection is waiting for an ACK. - // What to do? - #warning "TODO: Handle case where unexpected TCP data message is received" - return; + + if (segment->flags & TCP_FLAG_ACK) { + + if (connection_state == SOCKET_WAIT) + move_connection_from_wait_for_ack_to_wait_for_accept(connection); + else if (connection_state == SOCKET_IDLE) { + + } else { + #warning "TODO: Handle case where no connection exists" + assert(connection_state == SOCKET_NONE); + return; + } + + uint32_t ack_no = ntohl(segment->ack_no); + + if (ack_no <= connection->snd_una) + TCP_DEBUG_LOG("Received segment acknowledged again %d", ack_no); + else { + if (ack_no > connection->snd_nxt) { + TCP_DEBUG_LOG("Received segment acknowledged unsent data with sequence number %d, but %d still wasn't sent", ack_no, connection->snd_nxt); + return; // Acknowledged unsent data + } + + size_t newly_acked_bytes = ack_no - connection->snd_una; + memmove(connection->out_buffer, connection->out_buffer + newly_acked_bytes, connection->snd_wnd - newly_acked_bytes); + connection->snd_wnd -= newly_acked_bytes; + connection->snd_una = ack_no; + + // Now there's space available in the output buffer + if (connection->callback_ready_to_send) + connection->callback_ready_to_send(connection->callback_data); + } + + } else { + + if (connection_state != SOCKET_IDLE) { + // Either there is no connection associated to this peer + // at this port, or the connection is waiting for an ACK. + // What to do? + #warning "TODO: Handle case where unexpected TCP data message is received" + return; + } } + handle_received_data(connection, segment->payload, + len - sizeof(tcp_segment_t)); - char *payload = segment->payload; - size_t payload_arrived = len - sizeof(tcp_segment_t); - size_t payload_capacity = TCP_INPUT_BUFFER_SIZE - connection->in_used; - size_t payload_considered = MIN(payload_arrived, payload_capacity); - - memcpy(connection->in_buffer + connection->in_used, payload, payload_considered); - connection->in_used += payload_considered; - connection->ack_no += payload_considered; // Is this right? + if (segment->flags & TCP_FLAG_FIN) { + #warning "TODO: Handle FIN segment" + } } } tcp_listener_t* -tcp_listener_create(tcp_state_t *state, uint16_t port, void *data, void (*callback)(void*)) +tcp_listener_create(tcp_state_t *state, uint16_t port, void *callback_data, + void (*callback_ready_to_accept)(void*)) { if (find_listener_with_port(state, port)) { // ERROR: A connection is already listening on this port @@ -307,8 +451,8 @@ tcp_listener_create(tcp_state_t *state, uint16_t port, void *data, void (*callba listener->connections_waiting_for_ack = NULL; listener->connections_waiting_for_accept_head = NULL; listener->connections_waiting_for_accept_tail = NULL; - listener->data = data; - listener->callback = callback; + listener->callback_data = callback_data; + listener->callback_ready_to_accept = callback_ready_to_accept; // Push listener connection structure to the used list listener->prev = NULL; @@ -346,7 +490,9 @@ void tcp_listener_destroy(tcp_listener_t *listener) state->free_listener_list = listener; } -tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener) +tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data, + void (*callback_ready_to_recv)(void*), + void (*callback_ready_to_send)(void*)) { (void) listener; @@ -374,6 +520,11 @@ tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener) listener->connections->prev = connection; listener->connections = connection; } + + connection->callback_data = callback_data; + connection->callback_ready_to_recv = callback_ready_to_recv; + connection->callback_ready_to_send = callback_ready_to_send; + return connection; } @@ -399,13 +550,20 @@ void tcp_connection_destroy(tcp_connection_t *connection) } size_t tcp_connection_recv(tcp_connection_t *connection, - void *dst, size_t len) + void *dst, size_t len) { - size_t num = MIN(len, connection->in_used); + size_t unread = connection->rcv_nxt - connection->rcv_unread; + size_t num = MIN(len, unread); memcpy(dst, connection->in_buffer, num); - memmove(connection->in_buffer, connection->in_buffer + num, connection->in_used - num); - connection->in_used -= num; + + size_t input_buffer_usage = TCP_INPUT_BUFFER_SIZE - connection->rcv_wnd; + + memmove(connection->in_buffer, connection->in_buffer + num, input_buffer_usage - num); + connection->rcv_unread += num; + connection->rcv_wnd += num; + + assert(connection->rcv_wnd <= TCP_INPUT_BUFFER_SIZE); return num; } @@ -414,55 +572,18 @@ static size_t append_to_output_buffer(tcp_connection_t *connection, const void *src, size_t len) { - size_t num = MIN(len, TCP_OUTPUT_BUFFER_SIZE - connection->out_used); + size_t capacity = TCP_OUTPUT_BUFFER_SIZE - connection->snd_wnd; + size_t num = MIN(len, capacity); - memcpy(connection->out_buffer + connection->out_used, src, num); - connection->out_used += len; + memcpy(connection->out_buffer + connection->snd_wnd, src, num); + connection->snd_wnd += num; return num; } -static uint32_t calculate_checksum(const void *data, size_t size) -{ - (void) data, - (void) size; - - #warning "TODO: Calculate TCP checksum" - return 0; -} - -static void -try_flushing_output_buffer(tcp_connection_t *connection) -{ - tcp_state_t *tcp_state = connection->listener->state; - - tcp_segment_t *segment = &connection->out_header; - segment->src_port = htons(connection->listener->port); - segment->dst_port = htons(connection->peer_port); - segment->seq_no = htons(connection->seq_no); // Should this be increased by the segment size? - segment->ack_no = htons(connection->ack_no); - segment->unused = 0; - segment->offset = 5; // No options - segment->flags = 0; - segment->window = htons(TCP_INPUT_BUFFER_SIZE - connection->in_used); - segment->checksum = 0; // Temporary value - segment->urgent_pointer = 0; // Don't support urgent data - - segment->checksum = calculate_checksum(segment, sizeof(tcp_segment_t)); - - int sent_bytes = tcp_send(tcp_state, connection->peer_ip, segment, sizeof(tcp_segment_t) + connection->out_used); - - if (sent_bytes < 0) { - // It wasn't possible to send out bytes. We'll try again later! - } else { - memmove(connection->out_buffer, connection->out_buffer + sent_bytes, connection->out_used - sent_bytes); - connection->out_used -= sent_bytes; - } -} - size_t tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len) { size_t num = append_to_output_buffer(connection, src, len); - try_flushing_output_buffer(connection); + emit_segment(connection, false, false, SIZE_MAX); return num; } diff --git a/src/tcp.h b/src/tcp.h index ad9052e..24f1e2e 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -1,6 +1,5 @@ #include #include -#include #include "defs.h" #define TCP_MAX_LISTENERS 32 @@ -22,11 +21,10 @@ typedef struct { uint16_t dst_port; uint32_t seq_no; uint32_t ack_no; -#if __BYTE_ORDER == __LITTLE_ENDIAN +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ uint8_t unused: 4; uint8_t offset: 4; -#endif -#if __BYTE_ORDER == __BIG_ENDIAN +#else uint8_t offset: 4; uint8_t unused: 4; #endif @@ -49,20 +47,52 @@ struct tcp_listener_t { tcp_connection_t *connections_waiting_for_ack; tcp_connection_t *connections_waiting_for_accept_head; tcp_connection_t *connections_waiting_for_accept_tail; - void (*callback)(void*); - void *data; + void (*callback_ready_to_accept)(void*); + void *callback_data; }; struct tcp_connection_t { tcp_listener_t *listener; // Listener that accepted this connection tcp_connection_t *next; tcp_connection_t *prev; - ip_address_t peer_ip; - uint16_t peer_port; - uint32_t seq_no; - uint32_t ack_no; - size_t in_used; - size_t out_used; + + void *callback_data; + void (*callback_ready_to_recv)(void*); + void (*callback_ready_to_send)(void*); + + ip_address_t peer_ip; // Network byte order + uint16_t peer_port; // CPU byte order + + uint32_t rcv_unread; // It's the sequence number of the first + // byte stored in the input buffer, such + // that [rcv_next - rcv_unread] is the + // number of bytes that the parent application + // can read from the socket. + + uint32_t rcv_nxt; // RCV.NXT from RFC 793 + // It's the sequence number of the next + // byte waiting to be received. + + uint32_t rcv_wnd; // RCV.WND from RFC 793 + // It's the size of the portion of input + // buffer that's currently free. + + uint32_t snd_wnd; // SND.WND from RFC 793 + // It's the number of bytes stored in + // the [out_buffer] output buffer, both + // sent but not acknowledged and not sent. + + uint32_t snd_nxt; // SND.NXT from RFC 793 + // It's the sequence number of the first + // not yet sent byte in the output buffer. + // By subtracting [snd_una] from this value, + // you get the amount of bytes sent out but + // not yet acknowledged. + + uint32_t snd_una; // SND.UNA from RFC 793 + // It's the sequence number of the last + // byte sent and acknowledged by the peer. + tcp_segment_t out_header; // There must be no padding between char out_buffer[TCP_OUTPUT_BUFFER_SIZE]; // these two char in_buffer[TCP_INPUT_BUFFER_SIZE]; @@ -75,6 +105,7 @@ typedef struct { } tcp_callbacks_t; struct tcp_state_t { + ip_address_t ip; tcp_callbacks_t callbacks; tcp_connection_t *used_connection_list; tcp_connection_t *free_connection_list; @@ -84,13 +115,13 @@ struct tcp_state_t { tcp_listener_t listener_pool[TCP_MAX_LISTENERS]; }; -void tcp_init(tcp_state_t *tcp_state, tcp_callbacks_t callbacks); +void tcp_init(tcp_state_t *tcp_state, ip_address_t ip, tcp_callbacks_t callbacks); void tcp_free(tcp_state_t *tcp_state); void tcp_seconds_passed(tcp_state_t *state, size_t seconds); void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_segment_t *segment, size_t len); tcp_listener_t *tcp_listener_create(tcp_state_t *state, uint16_t port, void *data, void (*callback)(void*)); void tcp_listener_destroy(tcp_listener_t *listener); -tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener); +tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data, void (*callback_ready_to_recv)(void*), void (*callback_ready_to_send)(void*)); void tcp_connection_destroy(tcp_connection_t *connection); size_t tcp_connection_recv(tcp_connection_t *connection, void *dst, size_t len); size_t tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len); diff --git a/test/loop2.c b/test/loop2.c new file mode 100644 index 0000000..318b551 --- /dev/null +++ b/test/loop2.c @@ -0,0 +1,62 @@ +#include +#include + +int main(void) +{ + microtcp_errcode_t errcode; + + microtcp_t *mtcp = microtcp_create("10.0.0.5", "10.0.0.4", NULL, NULL); + if (mtcp == NULL) + return -1; + + uint16_t port = 80; + microtcp_socket_t *server = microtcp_open(mtcp, port, &errcode); + if (errcode) { + fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode)); + microtcp_destroy(mtcp); + return -1; + } + assert(server); + + fprintf(stderr, "Listening on port %d\n", port); + + while (1) { + fprintf(stderr, "About to accept\n"); + microtcp_socket_t *client = microtcp_accept(server, false, &errcode); + if (errcode) { + fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode)); + break; + } + + fprintf(stderr, "Accepted a connection\n"); + + char buffer[1024]; + size_t num = microtcp_recv(client, buffer, sizeof(buffer), false, &errcode); + if (errcode) { + fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode)); + goto handled; + } + fprintf(stderr, "(%ld bytes received)\n", num); + + size_t sent1 = microtcp_send(client, "echo: ", 6, false, &errcode); + if (errcode) { + fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode)); + goto handled; + } + fprintf(stderr, "(%ld bytes sent 1)\n", sent1); + + size_t sent2 = microtcp_send(client, buffer, num, false, &errcode); + if (errcode) { + fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode)); + goto handled; + } + fprintf(stderr, "(%ld bytes sent 2)\n", sent2); + +handled: + microtcp_close(client); + } + + microtcp_close(server); + microtcp_destroy(mtcp); + return 0; +} diff --git a/test/test_arp.c b/test/test_arp.c deleted file mode 100644 index ee347c5..0000000 --- a/test/test_arp.c +++ /dev/null @@ -1,161 +0,0 @@ -#include -#include -#include "test_arp_util.h" - -/* - WHITE BOX TEST CASES - - A) Peer requests host's MAC given its IP and host - replies. - - B) Peer requests host's non MAC level 2 address - given its IP and host doesn't reply because it - only supports MAC. - - C) Peer requests host's MAC address given its non - IP level 3 address and host doesn't reply - because it only supports IP. - - D) Peer requests host's MAC given its IP, but the - MAC address length field isn't 6, therefore - host doesn't reply. - - E) Peer requests host's MAC given its IP, but the - IP address length field isn't 4, therefore host - doesn't reply. - - F) Peer sends a request/reply that doesn't refer - to host and is from a sender with IP never seen - by the host (no entry in the translation table). - It's expected that no entry is added to the - translation table. - - G) Peer sends a request/reply that doesn't refer - to host but is from a sender with IP already - in the ARP translation table, therefore is - expected that host updates the entry. - - H) Program queries the ARP module for a MAC that - isn't cached, therefore an ARP request is - expected to be generated and, when replied to, - the ARP module is expected to resolve the - program's query. - - I) Program queries the ARP module for a MAC that's - cached, therefore the ARP module is expected to - resolve it without sending packets. -*/ - -static arp_testcase_result_t test_000(char *msg, size_t msgmax) -{ - char host_ip[4] = {0xc0, 0xa8, 0x01, 0x05}; - char host_mac[6] = {0xcc, 0x6b, 0x1e, 0x13, 0xa8, 0x93}; - - arp_testcase_t testcase; - arp_testcase_init(&testcase, host_mac, host_ip); - - arp_testcase_send(&testcase, (char[]) { - 0x00, 0x01, // hardware_type=ethernet - 0x08, 0x00, // protocol_type=ip - 0x06, // hardware_len=6 - 0x04, // protocol_len=4 - 0x00, 0x01, // operation_type=request - 0xbc, 0x15, 0xac, 0x29, 0xe5, 0x61, // sender MAC - 0xc0, 0xa8, 0x01, 0x01, // sender IP - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target MAC (empty) - 0xc0, 0xa8, 0x01, 0x05, // target IP - }); - - arp_testcase_recv(&testcase, (char[]) { - 0x00, 0x01, // hardware_type=ethernet - 0x08, 0x00, // protocol_type=ip - 0x06, // hardware_len=6 - 0x04, // protocol_len=4 - 0x00, 0x02, // operation_type=reply - 0xcc, 0x6b, 0x1e, 0x13, 0xa8, 0x93, // sender MAC - 0xc0, 0xa8, 0x01, 0x05, // sender IP - 0xbc, 0x15, 0xac, 0x29, 0xe5, 0x61, // target MAC - 0xc0, 0xa8, 0x01, 0x01, // target IP - }); - - arp_testcase_result_t result = - arp_testcase_run(testcase, msg, msgmax); - - arp_testcase_free(&testcase); - return result; -} - - -static arp_testcase_result_t test_001(char *msg, size_t msgmax) -{ - /* This testcase simulates the host receiving - * an ARP request not associated to it, therefore - * the ARP module is expected to not reply. - */ - - char host_ip[4] = {0xc0, 0xa8, 0x01, 0x05}; - char host_mac[6] = {0xcc, 0x6b, 0x1e, 0x13, 0xa8, 0x93}; - - arp_testcase_t testcase; - arp_testcase_init(&testcase, host_mac, host_ip); - - arp_testcase_send(&testcase, (char[]) { - 0x00, 0x01, // hardware_type=ethernet - 0x08, 0x00, // protocol_type=ip - 0x06, // hardware_len=6 - 0x04, // protocol_len=4 - 0x00, 0x01, // operation_type=request - 0xbc, 0x15, 0xac, 0x29, 0xe5, 0x61, // sender MAC - 0xc0, 0xa8, 0x01, 0x01, // sender IP - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target MAC (empty) - 0xc0, 0xa8, 0x01, 0x10, // target IP (different to the host's) - }); - - arp_testcase_result_t result = - arp_testcase_run(testcase, msg, msgmax); - - arp_testcase_free(&testcase); - return result; -} - -typedef arp_testcase_result_t (*arp_testcase_routine_t)(char*, size_t); - -int main(void) -{ - static const arp_testcase_routine_t routines[] = { - test_000, - test_001, - NULL, - }; - - size_t passed = 0; - size_t failed = 0; - size_t aborted = 0; - - for (size_t i = 0; routines[i]; i++) { - - char message[1024]; - arp_testcase_result_t result = routines[i](message, sizeof(message)); - - switch (result) { - case ARP_TESTCASE_PASSED: - fprintf(stdout, "Test %ld ... PASSED\n", i); - passed++; - break; - - case ARP_TESTCASE_FAILED: - fprintf(stdout, "Test %ld ... FAILED: %s\n", i, message); - failed++; - break; - - case ARP_TESTCASE_ABORTED: - fprintf(stdout, "Test %ld ... ABORTED: %s\n", i, message); - aborted++; - break; - } - } - - fprintf(stdout, "SUMMARY: %ld passed, %ld failed and %ld aborted\n", - passed, failed, aborted); - return 0; -} \ No newline at end of file diff --git a/test/test_arp_util.c b/test/test_arp_util.c deleted file mode 100644 index ed651a5..0000000 --- a/test/test_arp_util.c +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include "test_arp_util.h" - -void arp_testcase_init(arp_testcase_t *tcase, - const char mac[static 6], - const char ip[static 4]) -{ - memcpy(&tcase->self_ip, ip, 4); - memcpy(&tcase->self_mac, mac, 6); - tcase->failed = false; - tcase->count = 0; -} - -void arp_testcase_free(arp_testcase_t *tcase) -{ - (void) tcase; -} - -void arp_testcase_send(arp_testcase_t *tcase, const char data[static sizeof(arp_packet_t)]) -{ - if (tcase->failed) - return; - - if (tcase->count == ARP_TESTCASE_MAX_PACKETS) { - tcase->failed = true; - return; - } - - arp_testcase_packet_t *packet = tcase->packets + tcase->count; - packet->sender = ARP_TESTCASE_SENDER_PEER; - packet->data = data; - - tcase->count++; -} - -void arp_testcase_recv(arp_testcase_t *tcase, const char data[static sizeof(arp_packet_t)]) -{ - if (tcase->failed) - return; - - if (tcase->count == ARP_TESTCASE_MAX_PACKETS) { - tcase->failed = true; - return; - } - - arp_testcase_packet_t *packet = tcase->packets + tcase->count; - packet->sender = ARP_TESTCASE_SENDER_HOST; - packet->data = data; - - tcase->count++; -} - -typedef struct { - char *msg; - size_t msgmax; - const arp_packet_t *output; - const arp_testcase_t *tcase; - size_t cursor; - bool output_as_expected; - bool aborted_while_checking_sent_packets; -} testcase_contex_t; - -static void send_packet(void *data, mac_address_t dest) -{ - (void) dest; // Is this ok? - - testcase_contex_t *context = data; - - context->aborted_while_checking_sent_packets = false; - - if (context->cursor == context->tcase->count) { - snprintf(context->msg, context->msgmax, "ARP module sent an unexpected packet"); - context->output_as_expected = false; - return; - } - - const arp_testcase_packet_t *packet = context->tcase->packets + context->cursor; - - if (packet->sender != ARP_TESTCASE_SENDER_HOST) { - snprintf(context->msg, context->msgmax, "ARP module sent an unexpected packet"); - context->output_as_expected = false; - return; - } - context->cursor++; - - if (!memcmp(context->output, packet->data, sizeof(arp_packet_t))) - context->output_as_expected = true; - else { - snprintf(context->msg, context->msgmax, "ARP module sent a different packet than expected"); - context->output_as_expected = false; - } -} - -arp_testcase_result_t arp_testcase_run(arp_testcase_t tcase, char *msg, size_t msgmax) -{ - if (tcase.failed) - return ARP_TESTCASE_ABORTED; - - arp_packet_t output; - - testcase_contex_t context = { - .output = &output, - .tcase = &tcase, - .cursor = 0, - .msg = msg, - .msgmax = msgmax, - }; - - if (msgmax > 0) - msg[0] = '\0'; - - arp_state_t state; - arp_init(&state, tcase.self_ip, tcase.self_mac, &context, send_packet); - arp_change_output_buffer(&state, &output, sizeof(output)); - - while (context.cursor < tcase.count) { - - arp_testcase_packet_t *packet = tcase.packets + context.cursor++; - - if (packet->sender != ARP_TESTCASE_SENDER_PEER) { - snprintf(msg, msgmax, "ARP module didn't send packet"); - return ARP_TESTCASE_FAILED; - } - - // Initialize these - context.output_as_expected = true; - context.aborted_while_checking_sent_packets = false; - - arp_process_result_t status = arp_process_packet(&state, packet->data, sizeof(arp_packet_t)); - - (void) status; // Not useful yet - - // Before this point the arp_process_packet will have - // sent some packets that were validated in the send_packet - // callback. If the testcase didn't fail, the next - // packet in the list will be sent by the peer. - if (context.aborted_while_checking_sent_packets) - return ARP_TESTCASE_ABORTED; - if (!context.output_as_expected) - return ARP_TESTCASE_FAILED; - } - - arp_free(&state); - return ARP_TESTCASE_PASSED; -} \ No newline at end of file diff --git a/test/test_arp_util.h b/test/test_arp_util.h deleted file mode 100644 index e7dee5b..0000000 --- a/test/test_arp_util.h +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include "../src/arp.h" - -#define ARP_TESTCASE_MAX_PACKETS 1024 - -typedef enum { - ARP_TESTCASE_SENDER_HOST, - ARP_TESTCASE_SENDER_PEER, -} arp_testcase_packet_sender_t; - -typedef struct { - arp_testcase_packet_sender_t sender; - const char *data; -} arp_testcase_packet_t; - -typedef struct { - bool failed; - - ip_address_t self_ip; - mac_address_t self_mac; - - size_t count; - arp_testcase_packet_t packets[ARP_TESTCASE_MAX_PACKETS]; -} arp_testcase_t; - -typedef enum { - ARP_TESTCASE_PASSED, - ARP_TESTCASE_FAILED, - ARP_TESTCASE_ABORTED, -} arp_testcase_result_t; - -void arp_testcase_init(arp_testcase_t *tcase, const char mac[static 6], const char ip[static 4]); -void arp_testcase_free(arp_testcase_t *tcase); -void arp_testcase_send(arp_testcase_t *tcase, const char data[static sizeof(arp_packet_t)]); -void arp_testcase_recv(arp_testcase_t *tcase, const char data[static sizeof(arp_packet_t)]); -arp_testcase_result_t arp_testcase_run(arp_testcase_t tcase, char *msg, size_t msgmax);