the TAP IP was the same as the microtcp instance, which wasn't right. Now the TAP is at 10.0.0.5 and the TCP at 10.0.0.4; fixed some bugs in the TCP state machine and made it so it always ACKs; fixed TCP checksum calculation

This commit is contained in:
cozis
2023-05-20 21:50:47 +02:00
parent ad65c26035
commit a272e8601e
9 changed files with 110 additions and 90 deletions
+4
View File
@@ -48,3 +48,7 @@ int main(void)
## Contributing
The build result is a header and a .c obtained as an amalgamation of all the source files. Any header included in the source files must be guarded by a `#ifndef MICROTCP_AMALGAMATION`.
## Functionalities
MicroTCP will not support
* TCP options
+1 -1
View File
@@ -55,7 +55,7 @@ int main(void)
return -1;
}
tuntap_set_ip(dev, ip, netmask);
tuntap_set_ip(dev, "10.0.0.5", netmask);
tuntap_set_hwaddr(dev, mac);
if (tuntap_up(dev)) {
+6 -1
View File
@@ -135,6 +135,7 @@ struct conn_t {
typedef struct {
bool exiting;
microtcp_t *mtcp;
microtcp_socket_t *sock;
microtcp_mux_t *mux;
int connum;
@@ -1416,6 +1417,7 @@ static const char *init(context_t *context, unsigned short port,
microtcp_t *mtcp = microtcp_create_using_callbacks(config.ip, config.mac, callbacks);
if (!mtcp)
return "Failed to initialize TCP";
context->mtcp = mtcp;
microtcp_errcode_t errcode;
@@ -1469,6 +1471,8 @@ const char *xhttp(unsigned short port, xh_callback callback,
while(!context.exiting)
{
microtcp_step(context.mtcp);
microtcp_muxevent_t ev;
if (!microtcp_mux_wait(context.mux, &ev))
continue;
@@ -1492,7 +1496,7 @@ const char *xhttp(unsigned short port, xh_callback callback,
if(ev.userp == NULL)
{
#ifdef DEBUG
fprintf(stderr, "XHTTP :: New connection\n");
fprintf(stderr, "XHTTP :: New connection\n");
#endif
// New connection.
conn_t *newly_accepted = accept_connection(&context);
@@ -1559,6 +1563,7 @@ const char *xhttp(unsigned short port, xh_callback callback,
(void) microtcp_close(context.sock);
(void) microtcp_mux_destroy(context.mux);
microtcp_destroy(context.mtcp);
return NULL;
}
+4 -4
View File
@@ -48,7 +48,7 @@ MEMDBG=valgrind
LIBDIR = 3p/lib
INCDIR = 3p/include
CFLAGS = $(CFLAGS_PLATFORM) -I$(INCDIR) -Ibuild/ -Wall -Wextra -DDEBUG=1 -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_USING_TAP -DMICROTCP_USING_MUX
CFLAGS = $(CFLAGS_PLATFORM) -I$(INCDIR) -Ibuild/ -Wall -Wextra
LFLAGS = -ltuntap $(LFLAGS_PLATFORM) -L$(LIBDIR)
ifeq ($(MEMDBG),drmemory)
@@ -89,7 +89,7 @@ all: build/microtcp.h build/microtcp.c build/echo_tcp build/echo_http
build/echo_tcp: examples/echo_tcp.c $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.c build/microtcp.h
mkdir -p $(@D)
gcc build/microtcp.c examples/echo_tcp.c -o $@ $(CFLAGS) $(LFLAGS)
gcc build/microtcp.c examples/echo_tcp.c -o $@ $(CFLAGS) $(LFLAGS) -DDEBUG=1 -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_USING_TAP -DMICROTCP_USING_MUX
build/microtcp.h: src/microtcp.h
mkdir -p $(@D)
@@ -132,8 +132,8 @@ build/microtcp.c: 3p/include/tinycthread.h 3p/src/tinycthread.c $(wildcard src/*
printf "\n#line 1 \"src/microtcp.c\"\n" >> $@
cat src/microtcp.c >> $@
build/echo_http: $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.h build/microtcp.c
gcc examples/microhttp/main.c examples/microhttp/xhttp.c build/microtcp.c -o $@ $(CFLAGS) $(LFLAGS)
build/echo_http: examples/microhttp/main.c $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.h build/microtcp.c
gcc examples/microhttp/main.c examples/microhttp/xhttp.c build/microtcp.c -o $@ $(CFLAGS) $(LFLAGS) -DDEBUG=1 -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_USING_MUX
clean:
rm -fr build
+15
View File
@@ -0,0 +1,15 @@
from http.server import BaseHTTPRequestHandler, HTTPServer
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
message = "Hello world!"
self.wfile.write(bytes(message, "utf8"))
return
addr = ('127.0.0.1', 8080)
HTTPServer(addr, RequestHandler).serve_forever()
+1
View File
@@ -206,6 +206,7 @@ int ip_send_2(ip_state_t *state, ip_protocol_t protocol, ip_address_t dst,
packet->checksum = calculate_checksum_ip((uint16_t*) packet, 4 * header_length);
// Sending updates the [state->output_ptr] and [state->output_len]
IP_DEBUG_LOG("sending %d", sizeof(ip_packet_t) + considered_payload);
state->send(state->send_data, dst, sizeof(ip_packet_t) + considered_payload);
managed_payload += considered_payload;
+1
View File
@@ -249,6 +249,7 @@ static void mac_resolved(void *data, arp_resolution_status_t status, mac_address
ethernet_frame_t *frame = (ethernet_frame_t*) buffer->data;
frame->dst = mac;
MICROTCP_DEBUG_LOG("sending %d", buffer->used);
int n = mtcp->callbacks.send(mtcp->callbacks.data, buffer->data, buffer->used);
if (n < 0)
MICROTCP_DEBUG_LOG("Couldn't send (%s)", strerror(errno));
+54 -81
View File
@@ -82,7 +82,7 @@ connection_create(tcp_listener_t *listener,
connection->snd_una = seq_no;
connection->snd_wnd = 0;
connection->snd_nxt = 0;
connection->snd_nxt = seq_no;
connection->prev = NULL;
connection->next = NULL;
@@ -154,26 +154,21 @@ calculate_checksum(const slice_list_t *slices, size_t num_slices)
return cpu_to_net_u16(~sum);
}
static void emit_segment(tcp_connection_t *connection, bool ack, bool syn, size_t payload)
static void emit_segment(tcp_connection_t *connection, uint8_t flags, 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);
// [snd_wnd + snd_una - snd_nxt] is the number of bytes that
// are in the output buffer but weren't sent yet
size_t payload_being_sent = MIN(payload, connection->snd_wnd + connection->snd_una - connection->snd_nxt);
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;
if (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++;
uint32_t seq_no = connection->snd_nxt;
int offset = 5; // No options
tcp_segment_t header = {
@@ -197,15 +192,19 @@ static void emit_segment(tcp_connection_t *connection, bool ack, bool syn, size_
.tcp_length = cpu_to_net_u16(total_segment_size),
};
const void *send_ptr = connection->out_buffer + connection->snd_nxt - connection->snd_una;
size_t send_len = payload_being_sent;
header.checksum = calculate_checksum((slice_list_t[]) {
{&pseudo_header, sizeof(tcp_pseudoheader_t)},
{&header, sizeof(tcp_segment_t)},
{connection->out_buffer, connection->snd_wnd},
{send_ptr, send_len},
}, 3);
TCP_DEBUG_LOG("sending %d+%d=%d", sizeof(tcp_segment_t), send_len, sizeof(tcp_segment_t)+send_len);
int result = state->callbacks.send(state->callbacks.data, connection->peer_ip, (slice_list_t[]) {
{&header, sizeof(tcp_segment_t)},
{connection->out_buffer, payload_being_sent},
{send_ptr, send_len},
}, 2);
if (result < 0) {
@@ -213,13 +212,13 @@ static void emit_segment(tcp_connection_t *connection, bool ack, bool syn, size_
} else {
size_t actually_sent_bytes = (size_t) result;
if (actually_sent_bytes < sizeof(tcp_segment_t)) {
if (actually_sent_bytes < sizeof(tcp_segment_t)) { // What about options??
// 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);
connection->snd_nxt += actually_sent_payload_bytes;
}
}
@@ -236,7 +235,7 @@ static void handle_received_data(tcp_connection_t *connection,
connection->rcv_wnd -= considered;
connection->rcv_nxt += considered;
emit_segment(connection, true, false, SIZE_MAX);
emit_segment(connection, TCP_FLAG_ACK, SIZE_MAX);
TCP_DEBUG_LOG("Received %d bytes", considered);
@@ -398,8 +397,11 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
}
connection->state = TCP_STATE_SYN_RCVD;
emit_segment(connection, true, true, 0);
connection->snd_una++;
emit_segment(connection, TCP_FLAG_SYN | TCP_FLAG_ACK, 0);
// Instead of incrementing the snd_una for the SYN we just send,
// we'll ignore it and also ignore it when the respective ACK si
// received.
connection->snd_nxt++;
TCP_DEBUG_LOG("Connection request handled");
@@ -443,7 +445,9 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
"the SYN we sent. This is valid TCP but we don't support "
"this case yet. We'll just ignore the data. Hopefully "
"the peer will retransmit it");
// The connection is now established.
connection->snd_una = net_to_cpu_u32(segment->ack_no);
connection->state = TCP_STATE_ESTAB;
move_from_non_established_list_to_non_accepted_queue(connection);
@@ -485,18 +489,36 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
if (segment->flags & TCP_FLAG_FIN) {
// Send ACK for the FIN
emit_segment(connection, true, false, 0);
connection->snd_una++; // emit_segment doesn't increment the "snd_una" for ghost bytes
emit_segment(connection, TCP_FLAG_ACK, 0);
connection->state = TCP_STATE_CLOSE_WAIT;
}
break;
case TCP_STATE_FIN_WAIT_1:break;
case TCP_STATE_FIN_WAIT_1:
{
// The FIN segment was sent after the user called "close".
// In this state we're expecting the ACK flag for the fin.
// The same message could also contain the peer's FIN.
}
break;
case TCP_STATE_FIN_WAIT_2:break;
case TCP_STATE_CLOSE_WAIT:break;
case TCP_STATE_LAST_ACK:break;
case TCP_STATE_TIME_WAIT:break;
case TCP_STATE_TIME_WAIT:
{
// Pop connection from the accepted_list
if (connection->prev)
connection->prev->next = connection->next;
else
listener->accepted_list = connection->next;
// Push it into the free connection list
connection->prev = NULL;
connection->next = state->free_connection_list;
state->free_connection_list = connection;
}
break;
}
}
}
@@ -646,25 +668,15 @@ void tcp_connection_destroy(tcp_connection_t *connection)
assert(connection_was_accepted(connection));
#endif
// Make sure the connection was first finished
// by being moved from the idle list to the
// waiting-for-fin list.
tcp_connection_finish(connection);
// You can only call destroy on connections
// that were at least established
assert(connection->state == TCP_STATE_ESTAB);
tcp_listener_t *listener = connection->listener;
// Send the FIN message
//emit_segment(connection, TCP_FLAG_FIN, 0);
//connection->state = TCP_STATE_FIN_WAIT_1;
// Pop connection from the waiting-for-fin list
if (connection->prev)
connection->prev->next = connection->next;
else
listener->accepted_list = connection->next;
// Push it into the free connection list
tcp_state_t *state = listener->state;
connection->prev = NULL;
connection->next = state->free_connection_list;
state->free_connection_list = connection;
// TODO: Should start a timeout here prolly
}
size_t tcp_connection_recv(tcp_connection_t *connection,
@@ -702,45 +714,6 @@ append_to_output_buffer(tcp_connection_t *connection,
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);
emit_segment(connection, false, false, SIZE_MAX);
emit_segment(connection, TCP_FLAG_ACK, SIZE_MAX);
return num;
}
void tcp_connection_finish(tcp_connection_t *connection)
{
/*
if (!connection->read_only) {
// Move connection from idle list to
// waiting-for-fin list
tcp_listener_t *listener = connection->listener;
// Pop it from the idle list
{
if (connection->prev)
connection->prev->next = connection->next;
else
listener->connections = connection->next;
if (connection->next)
connection->next->prev = connection->prev;
connection->prev = NULL;
connection->next = NULL;
}
#warning "The FIN segment should be sent here"
// Push it to the waiting-for-fin list
{
connection->prev = NULL;
connection->next = listener->connections_waiting_for_fin;
listener->connections_waiting_for_fin = connection;
}
// Now mark the connection as read-only
connection->read_only = true;
}
*/
}
+21
View File
@@ -76,6 +76,27 @@ struct tcp_connection_t {
ip_address_t peer_ip; // Network byte order
uint16_t peer_port; // CPU byte order
// Send Sequence Space
//
// 1 2 3 4
// ----------|----------|----------|----------
// SND.UNA SND.NXT SND.UNA
// +SND.WND
//
// 1 - old sequence numbers which have been acknowledged
// 2 - sequence numbers of unacknowledged data
// 3 - sequence numbers allowed for new data transmission
// 4 - future sequence numbers which are not yet allowed
//
// Receive Sequence Space
//
// 1 2 3
// ----------|----------|----------
// RCV.NXT RCV.NXT
// +RCV.WND
//
// (From RFC 793 section 3.2, // https://www.ietf.org/rfc/rfc793.txt)
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