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:
@@ -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
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user