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
@@ -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 */
+2 -4
View File
@@ -1,18 +1,16 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <endian.h>
#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
+249 -69
View File
@@ -4,12 +4,16 @@
#include <stdint.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h> // inet_pton
#include "ip.h"
#include "arp.h"
#include "tcp.h"
#include <microtcp.h>
#ifdef MICROTCP_USING_TAP
#include <tuntap.h>
#endif
#ifdef MICROTCP_BACKGROUND_THREAD
#include <pthread.h>
#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;
}
+3 -2
View File
@@ -1,3 +1,4 @@
/*
#ifdef MICROTCP_LINUX
#include <poll.h>
@@ -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)
{
+298 -177
View File
@@ -1,4 +1,5 @@
#include <string.h>
#include <stdbool.h>
#include <arpa/inet.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);
}
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;
}
+45 -14
View File
@@ -1,6 +1,5 @@
#include <stddef.h>
#include <stdint.h>
#include <endian.h>
#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);