diff --git a/README.md b/README.md index 62ea7b2..fb04184 100644 --- a/README.md +++ b/README.md @@ -47,4 +47,8 @@ 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`. \ No newline at end of file +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 \ No newline at end of file diff --git a/examples/microhttp/main.c b/examples/microhttp/main.c index 02302fe..4d38321 100644 --- a/examples/microhttp/main.c +++ b/examples/microhttp/main.c @@ -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)) { diff --git a/examples/microhttp/xhttp.c b/examples/microhttp/xhttp.c index 3f53f57..a3f0cf1 100644 --- a/examples/microhttp/xhttp.c +++ b/examples/microhttp/xhttp.c @@ -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; } diff --git a/makefile b/makefile index 12782ac..f7f7220 100644 --- a/makefile +++ b/makefile @@ -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 diff --git a/misc/serv.py b/misc/serv.py new file mode 100644 index 0000000..2e38463 --- /dev/null +++ b/misc/serv.py @@ -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() \ No newline at end of file diff --git a/src/ip.c b/src/ip.c index d4306a0..98f5231 100644 --- a/src/ip.c +++ b/src/ip.c @@ -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; diff --git a/src/microtcp.c b/src/microtcp.c index d7d915e..403afff 100644 --- a/src/microtcp.c +++ b/src/microtcp.c @@ -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)); diff --git a/src/tcp.c b/src/tcp.c index 18efc94..87872b4 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -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; - } -*/ } \ No newline at end of file diff --git a/src/tcp.h b/src/tcp.h index 25fd645..2beb5b9 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -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