better TCP and started using libtuntap

This commit is contained in:
Francesco Cozzuto
2023-03-25 20:18:14 +01:00
parent 57c2f61ea3
commit 092ba2d1ad
17 changed files with 807 additions and 626 deletions
+1
View File
@@ -1,5 +1,6 @@
*.pcapng *.pcapng
loop loop
loop2
test_arp test_arp
test_arp_cov test_arp_cov
report_arp_cov report_arp_cov
+42
View File
@@ -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 */
+89
View File
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2012 Tristan Le Guern <leguern AT medu DOT se>
*
* 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 <sys/types.h>
#include <tuntap-export.h>
#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
Binary file not shown.
Submodule
+1
Submodule 3p/libtuntap added at ef7ed7b444
+8 -11
View File
@@ -10,12 +10,6 @@ typedef struct microtcp_socket_t microtcp_socket_t;
#define MICROTCP_MAX_BUFFERS 8 #define MICROTCP_MAX_BUFFERS 8
#define MICROTCP_MAX_SOCKETS 32 #define MICROTCP_MAX_SOCKETS 32
typedef struct {
uint8_t data[6];
} microtcp_mac_t;
typedef uint32_t microtcp_ip_t;
typedef enum { typedef enum {
MICROTCP_ERRCODE_NONE = 0, MICROTCP_ERRCODE_NONE = 0,
@@ -30,7 +24,6 @@ typedef enum {
// Returned by microtcp_accept // Returned by microtcp_accept
MICROTCP_ERRCODE_NOTLISTENER, MICROTCP_ERRCODE_NOTLISTENER,
MICROTCP_ERRCODE_CANTBLOCK, MICROTCP_ERRCODE_CANTBLOCK,
MICROTCP_ERRCODE_NOTHINGTOACCEPT,
// Returned by microtcp_recv and microtcp_send // Returned by microtcp_recv and microtcp_send
MICROTCP_ERRCODE_NOTCONNECTION, MICROTCP_ERRCODE_NOTCONNECTION,
@@ -44,14 +37,18 @@ typedef struct {
int (*recv)(void *data, void *dst, size_t len); int (*recv)(void *data, void *dst, size_t len);
} microtcp_callbacks_t; } microtcp_callbacks_t;
microtcp_t *microtcp_create(); #ifdef MICROTCP_USING_TAP
microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac, microtcp_callbacks_t callbacks); 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); void microtcp_destroy(microtcp_t *mtcp);
const char *microtcp_strerror(microtcp_errcode_t errcode); 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_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); microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, bool no_block, microtcp_errcode_t *errcode);
void microtcp_close(microtcp_socket_t *socket); 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_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, 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_step(microtcp_t *mtcp);
void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len); void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len);
+5 -5
View File
@@ -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: 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: test_arp:
gcc src/arp.c test/test_arp.c test/test_arp_util.c -o test_arp -Wall -Wextra -g -Iinclude/ 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 rm *.gcda *.gcno coverage.info
clean: clean:
rm -f *.gcda *.gcno coverage.info
rm -f test_arp test_arp_cov
rm -f loop rm -f loop
rm -rf report_arp_cov
+1
View File
@@ -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 MAC_BROADCAST (mac_address_t) {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
#endif /* MICROTCP_DEFS_H */ #endif /* MICROTCP_DEFS_H */
+2 -4
View File
@@ -1,18 +1,16 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <endian.h>
#include "defs.h" #include "defs.h"
#include "icmp.h" #include "icmp.h"
#define IP_PLUGGED_PROTOCOLS_MAX 4 #define IP_PLUGGED_PROTOCOLS_MAX 4
typedef struct { typedef struct {
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint8_t header_length: 4; uint8_t header_length: 4;
uint8_t version: 4; uint8_t version: 4;
#endif #else
#if __BYTE_ORDER == __BIG_ENDIAN
uint8_t version: 4; uint8_t version: 4;
uint8_t header_length: 4; uint8_t header_length: 4;
#endif #endif
+249 -69
View File
@@ -4,12 +4,16 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> // inet_pton
#include "ip.h" #include "ip.h"
#include "arp.h" #include "arp.h"
#include "tcp.h" #include "tcp.h"
#include <microtcp.h> #include <microtcp.h>
#ifdef MICROTCP_USING_TAP
#include <tuntap.h>
#endif
#ifdef MICROTCP_BACKGROUND_THREAD #ifdef MICROTCP_BACKGROUND_THREAD
#include <pthread.h> #include <pthread.h>
#endif #endif
@@ -22,23 +26,8 @@
#endif #endif
#ifdef MICROTCP_BACKGROUND_THREAD #ifdef MICROTCP_BACKGROUND_THREAD
#define LOCK_WHEN_THREADED(mtcp) do { \ #define LOCK_WHEN_THREADED(mtcp) do { pthread_mutex_lock(&(mtcp)->lock); } while (0);
fprintf(stderr, "--- %s before lock\n", __func__); \ #define UNLOCK_WHEN_THREADED(mtcp) do { pthread_mutex_unlock(&(mtcp)->lock); } while (0);
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);
#else #else
#define LOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0); #define LOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0);
#define UNLOCK_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; tcp_connection_t *connection;
}; };
#ifdef MICROTCP_BACKGROUND_THREAD #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 #endif
}; };
@@ -110,7 +105,6 @@ const char *microtcp_strerror(microtcp_errcode_t errcode)
case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error"; case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error";
case MICROTCP_ERRCODE_NOTLISTENER: return "Invalid operation on a non-listener socket"; 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_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"; case MICROTCP_ERRCODE_NOTCONNECTION: return "Invalid operation on a non-connection socket";
} }
return "???"; return "???";
@@ -353,7 +347,7 @@ process_packet(microtcp_t *mtcp, const void *packet, size_t len)
default: default:
// Unsupported ethertype // Unsupported ethertype
MICROTCP_DEBUG_LOG("Ignoring packet with ethertype %4x", frame->proto); //MICROTCP_DEBUG_LOG("Ignoring packet with ethertype %4x", frame->proto);
break; break;
} }
} }
@@ -390,7 +384,6 @@ void microtcp_step(microtcp_t *mtcp)
mtcp->last_update_time = current_time; mtcp->last_update_time = current_time;
} }
} }
unlock_and_exit:
UNLOCK_WHEN_THREADED(mtcp); UNLOCK_WHEN_THREADED(mtcp);
} }
@@ -404,19 +397,107 @@ static void *loop(void *data)
} }
#endif #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) 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)); microtcp_t *mtcp = malloc(sizeof(microtcp_t));
if (mtcp == NULL) if (mtcp == NULL)
return NULL; return NULL;
mac_address_t mac2; mtcp->ip = parsed_ip;
static_assert(sizeof(mac2) == sizeof(mac)); mtcp->mac = parsed_mac;
memcpy(&mac2, &mac, sizeof(mac));
mtcp->ip = ip;
mtcp->mac = mac2;
mtcp->callbacks = callbacks; mtcp->callbacks = callbacks;
mtcp->last_update_time = time(NULL); 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].prev = NULL;
mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].next = 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)) { if (!ip_plug_protocol(&mtcp->ip_state, IP_PROTOCOL_TCP, &mtcp->tcp_state, tcp_process_segment_wrapper)) {
free(mtcp); free(mtcp);
return NULL; 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, .data = mtcp,
.send = send_tcp_segment, .send = send_tcp_segment,
}); });
@@ -496,6 +577,51 @@ microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac
return mtcp; 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) void microtcp_destroy(microtcp_t *mtcp)
{ {
#ifdef MICROTCP_BACKGROUND_THREAD #ifdef MICROTCP_BACKGROUND_THREAD
@@ -522,12 +648,6 @@ pop_socket_struct_from_free_list(microtcp_t *mtcp)
return socket; return socket;
} }
static bool
have_unused_socket_structs(microtcp_t *mtcp)
{
return mtcp->free_socket_list != NULL;
}
static void static void
push_unlinked_socket_into_used_list(microtcp_socket_t *socket) 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); 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, microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
bool no_block, bool no_block,
microtcp_errcode_t *errcode) 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 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; errcode2 = MICROTCP_ERRCODE_SOCKETLIMIT;
goto unlock_and_exit; // Socket limit reached goto unlock_and_exit; // Socket limit reached
} }
tcp_connection_t *connection = tcp_listener_accept(socket->listener); tcp_connection_t *connection = tcp_listener_accept(socket->listener, socket2, ready_to_recv, ready_to_send);
if (!connection) {
#ifdef MICROTCP_BACKGROUND_THREAD #ifdef MICROTCP_BACKGROUND_THREAD
while (!connection && !no_block) {
if (no_block) { pthread_cond_wait(&socket->something_to_accept, &mtcp->lock);
errcode2 = MICROTCP_ERRCODE_NOTHINGTOACCEPT; connection = tcp_listener_accept(socket->listener, socket2, ready_to_recv, ready_to_send);
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
} }
#else
socket2 = pop_socket_struct_from_free_list(mtcp); if (!connection && !no_block) {
assert(socket2); // Because we checked at the start push_unlinked_socket_into_free_list(mtcp, socket2);
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
goto unlock_and_exit;
}
#endif
socket2->mtcp = mtcp; socket2->mtcp = mtcp;
socket2->prev = NULL; socket2->prev = NULL;
@@ -703,6 +829,21 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
socket2->type = SOCKET_CONNECTION; socket2->type = SOCKET_CONNECTION;
socket2->connection = 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); push_unlinked_socket_into_used_list(socket2);
} }
@@ -717,6 +858,7 @@ unlock_and_exit:
size_t microtcp_recv(microtcp_socket_t *socket, size_t microtcp_recv(microtcp_socket_t *socket,
void *dst, size_t len, void *dst, size_t len,
bool no_block,
microtcp_errcode_t *errcode) microtcp_errcode_t *errcode)
{ {
if (!socket || socket->type != SOCKET_CONNECTION) { if (!socket || socket->type != SOCKET_CONNECTION) {
@@ -725,19 +867,37 @@ size_t microtcp_recv(microtcp_socket_t *socket,
return 0; return 0;
} }
size_t num;
microtcp_t *mtcp = socket->mtcp; microtcp_t *mtcp = socket->mtcp;
microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE;
LOCK_WHEN_THREADED(mtcp); 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); UNLOCK_WHEN_THREADED(mtcp);
if (errcode) if (errcode)
*errcode = MICROTCP_ERRCODE_NONE; *errcode = errcode2;
return num; return num;
} }
size_t microtcp_send(microtcp_socket_t *socket, size_t microtcp_send(microtcp_socket_t *socket,
const void *src, size_t len, const void *src, size_t len,
bool no_block,
microtcp_errcode_t *errcode) microtcp_errcode_t *errcode)
{ {
if (!socket || socket->type != SOCKET_CONNECTION) { if (!socket || socket->type != SOCKET_CONNECTION) {
@@ -746,13 +906,33 @@ size_t microtcp_send(microtcp_socket_t *socket,
return 0; return 0;
} }
size_t num;
microtcp_t *mtcp = socket->mtcp; microtcp_t *mtcp = socket->mtcp;
microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE;
LOCK_WHEN_THREADED(mtcp); 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); UNLOCK_WHEN_THREADED(mtcp);
if (errcode) if (errcode)
*errcode = MICROTCP_ERRCODE_NONE; *errcode = errcode2;
return num; return num;
} }
+3 -2
View File
@@ -1,3 +1,4 @@
/*
#ifdef MICROTCP_LINUX #ifdef MICROTCP_LINUX
#include <poll.h> #include <poll.h>
@@ -85,7 +86,7 @@ static void free_callback(void *data)
close(tap_fd); close(tap_fd);
} }
/*
static bool get_ip_address(const char *dev, microtcp_ip_t *ip) static bool get_ip_address(const char *dev, microtcp_ip_t *ip)
{ {
int fd = socket(AF_INET, SOCK_DGRAM, 0); 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); close(fd);
return true; return true;
} }
*/
static bool get_mac_address(const char *dev, microtcp_mac_t *mac) static bool get_mac_address(const char *dev, microtcp_mac_t *mac)
{ {
+297 -176
View File
@@ -1,4 +1,5 @@
#include <string.h> #include <string.h>
#include <stdbool.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "tcp.h" #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); 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; tcp_state->callbacks = callbacks;
for (size_t i = 0; i < TCP_MAX_SOCKETS-1; i++) 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; tcp_state_t *state = listener->state;
// Pop a connection structure from the free list // Pop a connection structure from the free list
if (state->free_connection_list == NULL) tcp_connection_t *connection;
// ERROR: Reached connection limit {
return NULL; if (state->free_connection_list == NULL)
tcp_connection_t *connection = state->free_connection_list; // ERROR: Reached connection limit
state->free_connection_list = connection->next; return NULL;
connection = state->free_connection_list;
state->free_connection_list = connection->next;
}
// Initialize connection structure // Initialize connection structure
connection->listener = listener; {
connection->seq_no = seq_no; connection->listener = listener;
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->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 // Append the connection to the list of connections
// waiting for the ACK message // waiting for the ACK message
if (listener->connections_waiting_for_ack) {
listener->connections_waiting_for_ack->prev = connection; if (listener->connections_waiting_for_ack)
connection->next = listener->connections_waiting_for_ack; listener->connections_waiting_for_ack->prev = connection;
listener->connections_waiting_for_ack = connection; connection->prev = NULL;
connection->next = listener->connections_waiting_for_ack;
listener->connections_waiting_for_ack = connection;
}
return connection; return connection;
} }
@@ -84,11 +103,8 @@ connection_create_waiting_for_ack(tcp_listener_t *listener,
static tcp_listener_t* static tcp_listener_t*
find_listener_with_port(tcp_state_t *state, uint16_t port) 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; tcp_listener_t *cursor = state->used_listener_list;
while (cursor) { while (cursor) {
TCP_DEBUG_LOG("port=%d, seeking=%d", cursor->port, port);
if (cursor->port == port) if (cursor->port == port)
return cursor; return cursor;
cursor = cursor->next; 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); tcp_connection_t *connection2 = find_connection(listener->connections, peer_ip, peer_port);
if (connection2) { if (connection2) {
*connection = connection2; if (connection)
*connection = connection2;
return SOCKET_IDLE; return SOCKET_IDLE;
} }
connection2 = find_connection(listener->connections_waiting_for_accept_head, peer_ip, peer_port); connection2 = find_connection(listener->connections_waiting_for_accept_head, peer_ip, peer_port);
if (connection2) { if (connection2) {
*connection = connection2; if (connection)
*connection = connection2;
return SOCKET_IDLE; return SOCKET_IDLE;
} }
connection2 = find_connection(listener->connections_waiting_for_ack, peer_ip, peer_port); connection2 = find_connection(listener->connections_waiting_for_ack, peer_ip, peer_port);
if (connection2) { if (connection2) {
*connection = connection2; if (connection)
*connection = connection2;
return SOCKET_WAIT; return SOCKET_WAIT;
} }
*connection = NULL; if (connection)
*connection = NULL;
return SOCKET_NONE; return SOCKET_NONE;
} }
static uint32_t choose_ack() static uint32_t choose_sequence_no()
{ {
return 0; 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, void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
tcp_segment_t *segment, size_t len) tcp_segment_t *segment, size_t len)
{ {
@@ -172,118 +339,95 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
tcp_connection_t *connection; tcp_connection_t *connection;
connection_state_t connection_state = find_connection_associated_to(listener, sender, reordered_src_port, &connection); connection_state_t connection_state = find_connection_associated_to(listener, sender, reordered_src_port, &connection);
if ((segment->flags & TCP_FLAG_SYN) && !(segment->flags & TCP_FLAG_ACK)) { if (segment->flags & TCP_FLAG_SYN) {
/* Connection request */ if (segment->flags & TCP_FLAG_ACK) {
if (connection_state != SOCKET_NONE) { // Drop the packet. We only do servers for now!
// Peer wants to connect, but a connection was already created.. #warning "TODO: Handle TCP second message of three way handshake"
// What to do?
#warning "TODO: Handle case where an existing connection recieved the SYN message" } else {
return;
/* 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 { } else {
if (connection_state != SOCKET_IDLE) { if (segment->flags & TCP_FLAG_ACK) {
// Either there is no connection associated to this peer
// at this port, or the connection is waiting for an ACK. if (connection_state == SOCKET_WAIT)
// What to do? move_connection_from_wait_for_ack_to_wait_for_accept(connection);
#warning "TODO: Handle case where unexpected TCP data message is received" else if (connection_state == SOCKET_IDLE) {
return;
} 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; if (segment->flags & TCP_FLAG_FIN) {
size_t payload_arrived = len - sizeof(tcp_segment_t); #warning "TODO: Handle FIN segment"
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?
} }
} }
tcp_listener_t* 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)) { if (find_listener_with_port(state, port)) {
// ERROR: A connection is already listening on this 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_ack = NULL;
listener->connections_waiting_for_accept_head = NULL; listener->connections_waiting_for_accept_head = NULL;
listener->connections_waiting_for_accept_tail = NULL; listener->connections_waiting_for_accept_tail = NULL;
listener->data = data; listener->callback_data = callback_data;
listener->callback = callback; listener->callback_ready_to_accept = callback_ready_to_accept;
// Push listener connection structure to the used list // Push listener connection structure to the used list
listener->prev = NULL; listener->prev = NULL;
@@ -346,7 +490,9 @@ void tcp_listener_destroy(tcp_listener_t *listener)
state->free_listener_list = 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; (void) listener;
@@ -374,6 +520,11 @@ tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener)
listener->connections->prev = connection; listener->connections->prev = connection;
listener->connections = 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; return connection;
} }
@@ -399,13 +550,20 @@ void tcp_connection_destroy(tcp_connection_t *connection)
} }
size_t tcp_connection_recv(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); 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; return num;
} }
@@ -414,55 +572,18 @@ static size_t
append_to_output_buffer(tcp_connection_t *connection, append_to_output_buffer(tcp_connection_t *connection,
const void *src, size_t len) 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); memcpy(connection->out_buffer + connection->snd_wnd, src, num);
connection->out_used += len; connection->snd_wnd += num;
return 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 tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len)
{ {
size_t num = append_to_output_buffer(connection, src, 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; return num;
} }
+45 -14
View File
@@ -1,6 +1,5 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <endian.h>
#include "defs.h" #include "defs.h"
#define TCP_MAX_LISTENERS 32 #define TCP_MAX_LISTENERS 32
@@ -22,11 +21,10 @@ typedef struct {
uint16_t dst_port; uint16_t dst_port;
uint32_t seq_no; uint32_t seq_no;
uint32_t ack_no; uint32_t ack_no;
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint8_t unused: 4; uint8_t unused: 4;
uint8_t offset: 4; uint8_t offset: 4;
#endif #else
#if __BYTE_ORDER == __BIG_ENDIAN
uint8_t offset: 4; uint8_t offset: 4;
uint8_t unused: 4; uint8_t unused: 4;
#endif #endif
@@ -49,20 +47,52 @@ struct tcp_listener_t {
tcp_connection_t *connections_waiting_for_ack; tcp_connection_t *connections_waiting_for_ack;
tcp_connection_t *connections_waiting_for_accept_head; tcp_connection_t *connections_waiting_for_accept_head;
tcp_connection_t *connections_waiting_for_accept_tail; tcp_connection_t *connections_waiting_for_accept_tail;
void (*callback)(void*); void (*callback_ready_to_accept)(void*);
void *data; void *callback_data;
}; };
struct tcp_connection_t { struct tcp_connection_t {
tcp_listener_t *listener; // Listener that accepted this connection tcp_listener_t *listener; // Listener that accepted this connection
tcp_connection_t *next; tcp_connection_t *next;
tcp_connection_t *prev; tcp_connection_t *prev;
ip_address_t peer_ip;
uint16_t peer_port; void *callback_data;
uint32_t seq_no; void (*callback_ready_to_recv)(void*);
uint32_t ack_no; void (*callback_ready_to_send)(void*);
size_t in_used;
size_t out_used; 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 tcp_segment_t out_header; // There must be no padding between
char out_buffer[TCP_OUTPUT_BUFFER_SIZE]; // these two char out_buffer[TCP_OUTPUT_BUFFER_SIZE]; // these two
char in_buffer[TCP_INPUT_BUFFER_SIZE]; char in_buffer[TCP_INPUT_BUFFER_SIZE];
@@ -75,6 +105,7 @@ typedef struct {
} tcp_callbacks_t; } tcp_callbacks_t;
struct tcp_state_t { struct tcp_state_t {
ip_address_t ip;
tcp_callbacks_t callbacks; tcp_callbacks_t callbacks;
tcp_connection_t *used_connection_list; tcp_connection_t *used_connection_list;
tcp_connection_t *free_connection_list; tcp_connection_t *free_connection_list;
@@ -84,13 +115,13 @@ struct tcp_state_t {
tcp_listener_t listener_pool[TCP_MAX_LISTENERS]; 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_free(tcp_state_t *tcp_state);
void tcp_seconds_passed(tcp_state_t *state, size_t seconds); 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); 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*)); 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); 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); 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_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); size_t tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len);
+62
View File
@@ -0,0 +1,62 @@
#include <stdio.h>
#include <microtcp.h>
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;
}
-161
View File
@@ -1,161 +0,0 @@
#include <stdio.h>
#include <string.h>
#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;
}
-146
View File
@@ -1,146 +0,0 @@
#include <stdio.h>
#include <string.h>
#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;
}
-36
View File
@@ -1,36 +0,0 @@
#include <stdbool.h>
#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);