From a6ad10d9e02d5ea53ac30d46121fed985e70dbd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Thu, 2 Jun 2022 16:06:49 +0200 Subject: [PATCH 1/2] enet: Unfinished TCP implementation --- include/aos/ump_net.h | 50 +++- include/aos/ump_net_client.h | 150 ++++++++++ include/netutil/icmp.h | 2 +- include/netutil/ip.h | 2 + include/netutil/tcp.h | 45 +++ include/netutil/udp.h | 2 +- lib/aos/paging.c | 10 +- lib/aos/ump_net_client.c | 502 +++++++++++++++++++++++++++++++++- usr/drivers/enet/enet.h | 28 ++ usr/drivers/enet/enet_proto.c | 269 ++++++++++++++++-- usr/echoserver/main.c | 96 ++++++- 11 files changed, 1097 insertions(+), 59 deletions(-) create mode 100644 include/netutil/tcp.h diff --git a/include/aos/ump_net.h b/include/aos/ump_net.h index 53b1ad8..91c4c21 100644 --- a/include/aos/ump_net.h +++ b/include/aos/ump_net.h @@ -8,9 +8,13 @@ enum ump_net_op { UMP_NET_OP_UDP_SEND, UMP_NET_OP_UDP_LISTEN, UMP_NET_OP_UDP_LISTEN_STOP, + UMP_NET_OP_TCP_SEND, + UMP_NET_OP_TCP_LISTEN, + UMP_NET_OP_TCP_LISTEN_STOP, // events (in only) UMP_NET_EV_UDP_RECV, + UMP_NET_EV_TCP_RECV, }; struct ump_net_op_udp_send { @@ -31,6 +35,23 @@ struct ump_net_op_udp_listen_stop { uint16_t dest_port; }; +struct ump_net_op_tcp_send { + uint32_t dest_ip; + uint8_t tcp_header[0xf * 4]; +}; + +struct ump_net_op_tcp_listen { + uint16_t dest_port; // If 0, allocate a port +}; + +struct ump_net_ret_tcp_listen { + uint16_t dest_port; +}; + +struct ump_net_op_tcp_listen_stop { + uint16_t dest_port; +}; + struct ump_net_ev_udp_recv { uint32_t src_ip; uint32_t dest_ip; @@ -38,22 +59,39 @@ struct ump_net_ev_udp_recv { uint16_t dest_port; }; +struct ump_net_ev_tcp_recv { + uint32_t src_ip; + uint32_t dest_ip; + uint8_t tcp_header[0xf * 4]; +}; + struct ump_net_out_header { enum ump_net_op op; - union ump_net_out_d { + union { struct ump_net_op_udp_send udp_send; struct ump_net_op_udp_listen udp_listen; struct ump_net_op_udp_listen_stop udp_listen_stop; + struct ump_net_op_tcp_send tcp_send; + struct ump_net_op_tcp_listen tcp_listen; + struct ump_net_op_tcp_listen_stop tcp_listen_stop; } d; }; struct ump_net_in_header { enum ump_net_op op; - errval_t ret_err; - union ump_net_in_d { - struct ump_net_ret_udp_listen udp_listen; - struct ump_net_ev_udp_recv udp_recv; - } d; + union { + struct { + errval_t err; + union { + struct ump_net_ret_udp_listen udp_listen; + struct ump_net_ret_tcp_listen tcp_listen; + } d; + } ret; + union { + struct ump_net_ev_udp_recv udp_recv; + struct ump_net_ev_tcp_recv tcp_recv; + } ev; + }; }; #endif // _LIB_BARRELFISH_UMP_NET_H diff --git a/include/aos/ump_net_client.h b/include/aos/ump_net_client.h index 1371627..003c9da 100644 --- a/include/aos/ump_net_client.h +++ b/include/aos/ump_net_client.h @@ -28,6 +28,68 @@ struct ump_net_call { struct ump_net_call *next; }; +// internal +struct tcp_send_call { + struct tcp_tcb *tcb; + struct ump_send_queue_entry ump_entry; + struct ump_net_out_header out_header; +}; + +// internal +enum tcp_state { + TCP_STATE_SYN_RECEIVED, + TCP_STATE_ESTABLISHED, + TCP_STATE_FIN_WAIT_1, + TCP_STATE_FIN_WAIT_2, +}; + +#define TCP_MSS (1500 - 20 - 20) // 1500 - IP_HLEN_MIN - TCP_HLEN_MIN + +typedef void (*ump_net_tcp_receive_available_handler_t)(void *arg); +typedef void (*ump_net_tcp_send_available_handler_t)(void *arg); +typedef void (*ump_net_tcp_destroy_handler_t)(void *arg); + +/** + * @brief Internal data structure. You need to allocate this space when + * creating or accepting a TCP connection. + */ +struct tcp_tcb { + ump_net_tcp_receive_available_handler_t receive_available_handler; + ump_net_tcp_send_available_handler_t send_available_handler; + ump_net_tcp_destroy_handler_t destroy_handler; + void *handler_arg; + + uint8_t *rcv_buf; + uint32_t rcv_buf_size; + uint32_t rcv_available; + uint32_t rcv_readpos; + + uint8_t *snd_buf; + uint32_t snd_buf_size; + uint32_t snd_available; + uint32_t snd_readpos; + + enum tcp_state state; + uint32_t remote_ip; + uint16_t remote_port; + uint16_t local_port; + uint16_t mss; + + uint32_t snd_nxt; + uint32_t snd_una; + uint32_t snd_wnd; + uint32_t snd_wl1; + uint32_t snd_wl2; + uint32_t rcv_nxt; + uint32_t rcv_wnd; + + bool ack_needed; + + uint32_t refcount; + // struct tcp_send_call synack_call; + // bool synack_call_inuse; +}; + typedef void (*ump_net_udp_send_callback_t)(void *arg, errval_t err); /** * @brief Send an UDP packet. @@ -71,6 +133,7 @@ void ump_net_udp_listen_stop ( uint16_t dest_port, ump_net_udp_listen_stop_callback_t cb, void *cb_arg ); + /** * @brief Receive payload. * @param payload Buffer to write data into. @@ -84,4 +147,91 @@ void ump_net_recv_payload ( ump_recv_payload_callback_fn_t callback, void *callback_arg ); +typedef void (*ump_net_tcp_connect_handler_t)( + void *arg, struct ump_net_ev_tcp_recv *tcp_recv, + uint32_t remote_ip, uint16_t remote_port, uint16_t local_port +); +typedef void (*ump_net_tcp_listen_callback_t)(void *arg, errval_t err, uint16_t dest_port); +/** + * @brief Listen for incoming TCP connections. + * @param call You need to allocate this space until cb is called. + * @param dest_port TCP port to listen on. If 0, an ephemeral port is allocated, and passed to the callback. + * @param connect_handler This will be called when a connection arrives. + * When the callback is called, you should call ump_net_tcp_accept or ump_net_tcp_refuse. + * Ideally you do it immediately. If you wait, you may get retransmissions of the connection request. + * @param cb This will be called when the operation is complete, with cb_arg, an error value, and the port. + */ +void ump_net_tcp_listen ( + struct ump_net_call *call, + uint16_t dest_port, + ump_net_tcp_connect_handler_t connect_handler, void *connect_handler_arg, + ump_net_tcp_listen_callback_t cb, void *cb_arg +); + +typedef void (*ump_net_tcp_listen_stop_callback_t)(void *arg, errval_t err); +/** + * @brief Stop listening for incoming TCP connections. + * @param call You need to allocate this space until cb is called. + * @param cb This will be called when the operation is complete, with cb_arg and an error value. + */ +void ump_net_tcp_listen_stop ( + struct ump_net_call *call, + uint16_t dest_port, + ump_net_tcp_listen_stop_callback_t cb, void *cb_arg +); + +/** + * @brief Accept an incoming connection. + * @param tcp_recv This was passed to the connect_handler. + * @param tcb You need to allocate this space. + * @param receive_available_handler This is called when more data has become available for reading. + * @param send_available_handler This is called when more space has become available for sending. + * @param destroy_handler This is called when the connection is destroyed and you can deallocate tcb. + * @param handler_arg Handlers will be called with this argument. + * @param rcv_buf You need to allocate this, it will be used for receiving data. + * @param snd_buf You need to allocate this, it will be used for sending data. + */ +void ump_net_tcp_accept ( + const struct ump_net_ev_tcp_recv *tcp_recv, + struct tcp_tcb *tcb, + ump_net_tcp_receive_available_handler_t receive_available_handler, + ump_net_tcp_send_available_handler_t send_available_handler, + ump_net_tcp_destroy_handler_t destroy_handler, + void *handler_arg, + uint8_t *rcv_buf, + uint32_t rcv_buf_size, + uint8_t *snd_buf, + uint32_t snd_buf_size +); + +/** + * @brief Refuse an incoming connection. + * @param tcp_recv This was passed to the connect_handler. + */ +void ump_net_tcp_refuse ( + struct ump_net_ev_tcp_recv *tcp_recv +); + +/** + * @returns number of bytes that can be read. + */ +uint32_t ump_net_tcp_receive_available (struct tcp_tcb *tcb); + +/** + * @returns number of bytes that can be written before the buffer is full. + */ +uint32_t ump_net_tcp_send_available (struct tcp_tcb *tcb); + +/** + * Receives at most buf_size bytes into buf. + * @returns number of bytes that were read. + */ +uint32_t ump_net_tcp_receive (struct tcp_tcb *tcb, void *buf, uint32_t buf_size); + +/** + * Sends at most buf_size bytes from buf. + * @returns number of bytes that were read. + */ +uint32_t ump_net_tcp_send (struct tcp_tcb *tcb, const void *buf, uint32_t buf_size); + #endif // _LIB_BARRELFISH_UMP_NET_CLIENT_H diff --git a/include/netutil/icmp.h b/include/netutil/icmp.h index b78a50c..4607a11 100644 --- a/include/netutil/icmp.h +++ b/include/netutil/icmp.h @@ -9,7 +9,7 @@ //#define ICMP_DEBUG_OPTION 1 #if defined(ICMP_DEBUG_OPTION) -#define ICMP_DEBUG(x...) debug_printf("[ip] " x); +#define ICMP_DEBUG(x...) debug_printf("[icmp] " x); #else #define ICMP_DEBUG(fmt, ...) ((void)0) #endif diff --git a/include/netutil/ip.h b/include/netutil/ip.h index 0bbee48..b7193b3 100644 --- a/include/netutil/ip.h +++ b/include/netutil/ip.h @@ -32,6 +32,8 @@ #define IPH_HL(hdr) ((hdr)->v_hl & 0x0f) #define IPH_VHL_SET(hdr, v, hl) (hdr)->v_hl = (((v) << 4) | (hl)) +#define IP_HLEN_MIN 20 + struct ip_hdr { /* version / header length */ uint8_t v_hl; diff --git a/include/netutil/tcp.h b/include/netutil/tcp.h new file mode 100644 index 0000000..1c35c32 --- /dev/null +++ b/include/netutil/tcp.h @@ -0,0 +1,45 @@ +#ifndef _TCP_H_ +#define _TCP_H_ + +#include +#include + + +//#define TCP_DEBUG_OPTION 1 + +#if defined(TCP_DEBUG_OPTION) +#define TCP_DEBUG(x...) debug_printf("[tcp] " x); +#else +#define TCP_DEBUG(fmt, ...) ((void)0) +#endif + +/** + * TCP header + */ + +#define TCP_FIN 0x0001 +#define TCP_SYN 0x0002 +#define TCP_RST 0x0004 +#define TCP_PSH 0x0008 +#define TCP_ACK 0x0010 +#define TCP_URG 0x0020 + +#define TCP_OPTION_END 0 +#define TCP_OPTION_NOOP 1 +#define TCP_OPTION_MSS 2 + +struct tcp_hdr { + struct uint16_net src; + struct uint16_net dest; /* src/dest TCP ports */ + struct uint32_net seq; + struct uint32_net ack; + struct uint16_net flags; + struct uint16_net wnd; + struct uint16_net chksum; + struct uint16_net up; +} __attribute__((__packed__)); + +#define TCP_HLEN_MIN 20 +#define TCP_HLEN(hdr) ((uint16_rd((hdr)->flags) >> 12) * 4) + +#endif diff --git a/include/netutil/udp.h b/include/netutil/udp.h index deaddc1..b158e80 100644 --- a/include/netutil/udp.h +++ b/include/netutil/udp.h @@ -8,7 +8,7 @@ //#define UDP_DEBUG_OPTION 1 #if defined(UDP_DEBUG_OPTION) -#define UDP_DEBUG(x...) debug_printf("[ip] " x); +#define UDP_DEBUG(x...) debug_printf("[udp] " x); #else #define UDP_DEBUG(fmt, ...) ((void)0) #endif diff --git a/lib/aos/paging.c b/lib/aos/paging.c index b9ce76d..814af3f 100644 --- a/lib/aos/paging.c +++ b/lib/aos/paging.c @@ -129,13 +129,13 @@ static void pt_exception_handler(enum exception_type type, int subtype, if (vaddr_reg == NULL) { thread_mutex_unlock(&st->lock); static char str[256]; - snprintf(str, sizeof(str), "[ERROR] Page fault ouside valid virtual address space: type=%s, addr=%p, ip=%p, page_addr=0x%x\n", pt_exception_type_to_string(type, subtype), addr, ip, page_addr); + snprintf(str, sizeof(str), "[ERROR] Page fault outside valid virtual address space: type=%s, addr=%p, ip=%p, page_addr=0x%x\n", pt_exception_type_to_string(type, subtype), addr, ip, page_addr); USER_PANIC(str); } if (vaddr_reg->free) { thread_mutex_unlock(&st->lock); static char str[256]; - snprintf(str, sizeof(str), "[ERROR] Page fault ouside allocated address space: type=%s, addr=%p, ip=%p, page_addr=0x%x, base=0x%x, size=0x%x\n", pt_exception_type_to_string(type, subtype), addr, ip, page_addr, vaddr_reg->base, vaddr_reg->size); + snprintf(str, sizeof(str), "[ERROR] Page fault outside allocated address space: type=%s, addr=%p, ip=%p, page_addr=0x%x, base=0x%x, size=0x%x\n", pt_exception_type_to_string(type, subtype), addr, ip, page_addr, vaddr_reg->base, vaddr_reg->size); USER_PANIC(str); } if (!vaddr_reg->heap) { @@ -1062,9 +1062,9 @@ errval_t paging_unmap(struct paging_state *st, const void *region) /** * @brief Function to recursively free all the slab space. NOT THREAD SAFE - * + * * @param pt page table to recursively free the slab space for - * @return errval_t + * @return errval_t */ static void paging_free_page_table_tree(struct paging_state *st, struct pt_t *pt) { if(pt == NULL) return; @@ -1086,4 +1086,4 @@ void paging_free_slabs(struct paging_state *st) { slab_free(&st->pt_slabs, reg); } -} \ No newline at end of file +} diff --git a/lib/aos/ump_net_client.c b/lib/aos/ump_net_client.c index 44e2306..57700ad 100644 --- a/lib/aos/ump_net_client.c +++ b/lib/aos/ump_net_client.c @@ -5,13 +5,36 @@ #include #include #include +#include +#include #include +#include +#include + +// a =< b +#define WRAP_LTE(a, b) ((b) - (a) <= 0x7fffffff) +// a < b +#define WRAP_LT(a, b) ((b) - (a) - 1 <= 0x7fffffff) + +static uint32_t rcv_buf_wrap (struct tcp_tcb *tcb, uint32_t pos) { + if (pos >= tcb->rcv_buf_size) { + pos -= tcb->rcv_buf_size; + } + return pos; +} + struct udp_listen_entry { ump_net_udp_recv_handler_t recv_handler; void *recv_handler_arg; }; +struct tcp_listen_entry { + ump_net_tcp_connect_handler_t connect_handler; + void *connect_handler_arg; +}; + + static bool net_initialized = false; static struct ump_send_chan *net_send_chan; @@ -22,6 +45,12 @@ static struct ump_net_call *call_queue_tail = NULL; static collections_hash_table* udp_listen_table; +static collections_hash_table* tcp_tcb_table; +static collections_hash_table* tcp_listen_table; + +struct simpleslab_allocator tcp_rst_slab; +uint8_t tcp_rst_slab_buf[sizeof(struct tcp_send_call) * 128]; + static void net_recv_next (void); static void handle_net_header_recv (void *arg, size_t header_size, void *header_raw, size_t payload_size); @@ -48,6 +77,12 @@ errval_t ump_net_init (struct waitset *ws) { collections_hash_create_with_buckets(&udp_listen_table, 100, free); + collections_hash_create_with_buckets(&tcp_listen_table, 100, free); + collections_hash_create_with_buckets(&tcp_tcb_table, 500, NULL); + + simpleslab_init(&tcp_rst_slab, sizeof(struct tcp_send_call), + &tcp_rst_slab_buf, sizeof(tcp_rst_slab_buf)); + return SYS_ERR_OK; } @@ -70,29 +105,44 @@ static void ump_net_op_callback (void *arg, struct ump_send_queue_entry *entry) } if (call->in_header.op == UMP_NET_OP_UDP_SEND) { ((ump_net_udp_send_callback_t)call->cb)( - call->cb_arg, call->in_header.ret_err + call->cb_arg, call->in_header.ret.err ); } else if (call->in_header.op == UMP_NET_OP_UDP_LISTEN) { ((ump_net_udp_listen_callback_t)call->cb)( - call->cb_arg, call->in_header.ret_err, - call->in_header.d.udp_listen.dest_port + call->cb_arg, call->in_header.ret.err, + call->in_header.ret.d.udp_listen.dest_port ); } else if (call->in_header.op == UMP_NET_OP_UDP_LISTEN_STOP) { ((ump_net_udp_listen_stop_callback_t)call->cb)( - call->cb_arg, call->in_header.ret_err + call->cb_arg, call->in_header.ret.err + ); + } else if (call->in_header.op == UMP_NET_OP_TCP_LISTEN) { + ((ump_net_tcp_listen_callback_t)call->cb)( + call->cb_arg, call->in_header.ret.err, + call->in_header.ret.d.tcp_listen.dest_port + ); + } else if (call->in_header.op == UMP_NET_OP_TCP_LISTEN_STOP) { + ((ump_net_tcp_listen_stop_callback_t)call->cb)( + call->cb_arg, call->in_header.ret.err ); } } +static bool handle_tcp_header_recv (struct ump_net_ev_tcp_recv *tcp_recv, size_t payload_size); + static void handle_net_header_recv (void *arg, size_t header_size, void *header_raw, size_t payload_size) { struct ump_net_in_header *header = header_raw; if (header->op == UMP_NET_EV_UDP_RECV) { - struct udp_listen_entry *entry = collections_hash_find(udp_listen_table, header->d.udp_recv.dest_port); + struct udp_listen_entry *entry = collections_hash_find(udp_listen_table, header->ev.udp_recv.dest_port); if (entry != NULL) { - entry->recv_handler(entry->recv_handler_arg, &header->d.udp_recv, payload_size); + entry->recv_handler(entry->recv_handler_arg, &header->ev.udp_recv, payload_size); return; } + } else if (header->op == UMP_NET_EV_TCP_RECV) { + if (handle_tcp_header_recv(&header->ev.tcp_recv, payload_size)) return; + } else if (header->op == UMP_NET_OP_TCP_SEND) { + // ignore } else { assert(call_queue_head != NULL); assert(call_queue_head->out_header.op == header->op); @@ -205,3 +255,443 @@ void ump_net_recv_payload ( recv_payload_callback_arg = callback_arg; ump_recv_payload(net_recv_chan, payload, recv_payload_callback_wrap, NULL); } + +// TCP + +// Note: This assumes there is only one local IP address. +// Ideally we would also include it in the key. +static inline uint64_t tcp_tcb_key (uint32_t remote_ip, uint16_t remote_port, uint16_t local_port) { + return (uint64_t)remote_ip | ((uint64_t)remote_port << 32) | ((uint64_t)remote_port << 48); +} + +// decrement refcount +// returns true if the tcb was destroyed +// TODO: this is wrong, there should be a separate destroy operation and a destroyed flag. +static bool deref_tcb (struct tcp_tcb *tcb) { + if (tcb->refcount == 0) { + tcb->destroy_handler(tcb->handler_arg); + return true; + } else { + tcb->refcount--; + return false; + } +} + +static void tcp_send ( + struct tcp_send_call *call, + size_t payload_size, const void *payload, + ump_send_callback_fn_t cb, void *cb_arg +) { + ump_send(net_send_chan, &call->ump_entry, + sizeof(struct ump_net_out_header), &call->out_header, + payload_size, payload, + cb, cb_arg); +} + +static void tcp_rst_callback (void *arg, struct ump_send_queue_entry *entry) { + simpleslab_free(&tcp_rst_slab, arg); +} + +static void tcp_send_rst (const struct ump_net_ev_tcp_recv *tcp_recv, uint16_t payload_len) { + struct tcp_send_call *call = simpleslab_alloc(&tcp_rst_slab); + if (call != NULL) { + const struct tcp_hdr *in_tcp_hdr = (const struct tcp_hdr*)&tcp_recv->tcp_header; + uint16_t in_flags = uint16_rd(in_tcp_hdr->flags); + + uint32_t out_seq; + uint32_t out_ack; + uint16_t out_flags; + if (in_flags & TCP_ACK) { + out_seq = uint32_rd(in_tcp_hdr->ack); + out_ack = 0; + out_flags = TCP_RST; + } else { + out_seq = 0; + uint16_t seg_len = payload_len; + if (in_flags & (TCP_SYN | TCP_FIN)) seg_len++; + out_ack = uint32_rd(in_tcp_hdr->seq) + seg_len; + out_flags = TCP_RST | TCP_ACK; + } + + call->out_header = (struct ump_net_out_header){ + .op = UMP_NET_OP_TCP_SEND, + .d = { .tcp_send = { .dest_ip = tcp_recv->src_ip } } + }; + struct tcp_hdr *out_tcp_hdr = (struct tcp_hdr*)&call->out_header.d.tcp_send.tcp_header; + out_tcp_hdr->src = in_tcp_hdr->dest; + out_tcp_hdr->dest = in_tcp_hdr->src; + out_tcp_hdr->seq = uint32_wr(out_seq); + out_tcp_hdr->ack = uint32_wr(out_ack); + out_tcp_hdr->flags = uint16_wr(out_flags | (5 << 12)); + out_tcp_hdr->wnd = uint16_wr(0); + out_tcp_hdr->up = uint16_wr(0); + tcp_send(call, 0, NULL, tcp_rst_callback, call); + } +} + +static void tcp_send_active_callback (void *arg, struct ump_send_queue_entry *entry) { + struct tcp_send_call *call = arg; + deref_tcb(call->tcb); + free(call); +} + +static void tcp_send_active (struct tcp_tcb *tcb, bool send_syn) { + // TODO: avoid using malloc here + struct tcp_send_call *call = malloc(sizeof(struct tcp_send_call)); + if (call == NULL) return; + call->tcb = tcb; + tcb->refcount++; + + uint16_t flags = TCP_ACK; + uint16_t hwords = 5; + uint32_t seq = tcb->snd_nxt; + if (send_syn) { + hwords = 6; + flags = TCP_SYN | TCP_ACK; + seq = tcb->snd_una; + } + + call->out_header = (struct ump_net_out_header){ + .op = UMP_NET_OP_TCP_SEND, + .d = { .tcp_send = { .dest_ip = tcb->remote_ip } } + }; + struct tcp_hdr *tcp_hdr = (struct tcp_hdr*)&call->out_header.d.tcp_send.tcp_header; + tcp_hdr->src = uint16_wr(tcb->local_port); + tcp_hdr->dest = uint16_wr(tcb->remote_port); + tcp_hdr->seq = uint32_wr(seq); + tcp_hdr->ack = uint32_wr(tcb->rcv_nxt); + tcp_hdr->flags = uint16_wr(flags | (hwords << 12)); + tcp_hdr->wnd = uint16_wr(tcb->rcv_wnd); + tcp_hdr->up = uint16_wr(0); + + if (flags & TCP_SYN) { + uint8_t *options = (uint8_t *)tcp_hdr + TCP_HLEN_MIN; + options[0] = TCP_OPTION_MSS; + options[1] = 4; + *(struct uint16_net*)(options + 2) = uint16_wr(TCP_MSS); + } + + tcb->ack_needed = false; + tcp_send(call, 0, NULL, tcp_send_active_callback, call); +} + +void ump_net_tcp_listen ( + struct ump_net_call *call, + uint16_t dest_port, + ump_net_tcp_connect_handler_t connect_handler, void *connect_handler_arg, + ump_net_tcp_listen_callback_t cb, void *cb_arg +) { + if (collections_hash_find(tcp_listen_table, dest_port) != NULL) { + cb(cb_arg, LIB_ERR_NET_PORT_IN_USE, 0); + return; + } + struct tcp_listen_entry *entry = malloc(sizeof(struct tcp_listen_entry)); + if (entry == NULL) { + cb(cb_arg, LIB_ERR_MALLOC_FAIL, 0); + return; + } + entry->connect_handler = connect_handler; + entry->connect_handler_arg = connect_handler_arg; + collections_hash_insert(tcp_listen_table, dest_port, entry); + + call->out_header = (struct ump_net_out_header){ + .op = UMP_NET_OP_TCP_LISTEN, + .d = { .tcp_listen = { .dest_port = dest_port } } + }; + ump_net_send_call(call, 0, NULL, cb, cb_arg); +} + +void ump_net_tcp_listen_stop ( + struct ump_net_call *call, + uint16_t dest_port, + ump_net_tcp_listen_stop_callback_t cb, void *cb_arg +) { + if (collections_hash_find(tcp_listen_table, dest_port) == NULL) { + cb(cb_arg, LIB_ERR_NET_NOT_LISTENING); + return; + } + collections_hash_delete(tcp_listen_table, dest_port); + + call->out_header = (struct ump_net_out_header){ + .op = UMP_NET_OP_TCP_LISTEN_STOP, + .d = { .tcp_listen_stop = { .dest_port = dest_port } } + }; + ump_net_send_call(call, 0, NULL, cb, cb_arg); +} + +void ump_net_tcp_accept ( + const struct ump_net_ev_tcp_recv *tcp_recv, + struct tcp_tcb *tcb, + ump_net_tcp_receive_available_handler_t receive_available_handler, + ump_net_tcp_send_available_handler_t send_available_handler, + ump_net_tcp_destroy_handler_t destroy_handler, + void *handler_arg, + uint8_t *rcv_buf, + uint32_t rcv_buf_size, + uint8_t *snd_buf, + uint32_t snd_buf_size +) { + tcb->receive_available_handler = receive_available_handler; + tcb->send_available_handler = send_available_handler; + tcb->destroy_handler = destroy_handler; + tcb->handler_arg = handler_arg; + + assert(rcv_buf_size >= TCP_MSS * 2); + assert(snd_buf_size >= TCP_MSS * 2); + tcb->rcv_buf = rcv_buf; + tcb->rcv_buf_size = rcv_buf_size - TCP_MSS + 1; + tcb->rcv_available = 0; + tcb->rcv_readpos = 0; + tcb->snd_buf = snd_buf; + tcb->snd_buf_size = snd_buf_size - TCP_MSS + 1; + tcb->snd_available = 0; + tcb->snd_readpos = 0; + + const struct tcp_hdr *in_tcp_hdr = (const struct tcp_hdr*)&tcp_recv->tcp_header; + uint32_t seg_seq = uint32_rd(in_tcp_hdr->seq); + tcb->state = TCP_STATE_SYN_RECEIVED; + tcb->remote_ip = tcp_recv->src_ip; + tcb->remote_port = uint16_rd(in_tcp_hdr->src); + tcb->local_port = uint16_rd(in_tcp_hdr->dest); + tcb->mss = 536; + + uint32_t iss = (uint32_t)(systime_to_us(systime_now()) / 4); + tcb->snd_nxt = iss + 1; + tcb->snd_una = iss; + tcb->snd_wnd = (uint32_t)uint16_rd(in_tcp_hdr->wnd); + tcb->snd_wl1 = seg_seq; + tcb->snd_wl2 = iss; // not sure if this is correct + tcb->rcv_nxt = seg_seq + 1; + tcb->rcv_wnd = tcb->rcv_buf_size; + if (tcb->rcv_wnd > 0xffff) tcb->rcv_wnd = 0xffff; + + tcb->ack_needed = false; + tcb->refcount = 1; + + const uint8_t *options_pos = (const uint8_t *)in_tcp_hdr + TCP_HLEN_MIN; + const uint8_t *options_end = (const uint8_t *)in_tcp_hdr + TCP_HLEN(in_tcp_hdr); + while (options_pos < options_end) { + uint8_t kind = options_pos[0]; + if (kind == TCP_OPTION_END) { + // End of Option List + break; + } else if (kind == TCP_OPTION_NOOP) { + // No-Operation + options_pos++; + } else { + // Option with length + if (!( + options_pos + 1 < options_end && + options_pos[1] >= 2 && + options_pos + options_pos[1] <= options_end + )) { + // Invalid option length + tcp_send_rst(tcp_recv, 0); + deref_tcb(tcb); + return; + } + uint8_t option_length = options_pos[1]; + if (kind == TCP_OPTION_MSS) { + // Maximum Segment Size + if (option_length != 4) { + // Invalid option length + tcp_send_rst(tcp_recv, 0); + deref_tcb(tcb); + return; + } + tcb->mss = uint16_rd(*(struct uint16_net*)(options_pos + 2)); + if (tcb->mss < 16) { + tcp_send_rst(tcp_recv, 0); + deref_tcb(tcb); + return; + } + if (tcb->mss > TCP_MSS) tcb->mss = TCP_MSS; + } + options_pos += option_length; + } + } + + uint64_t key = tcp_tcb_key(tcb->remote_ip, tcb->remote_port, tcb->local_port); + if (collections_hash_find(tcp_tcb_table, key) != NULL) { + deref_tcb(tcb); + return; + } + collections_hash_insert(tcp_tcb_table, key, tcb); + + tcp_send_active(tcb, true); +} + +void ump_net_tcp_refuse (struct ump_net_ev_tcp_recv *tcp_recv) { + tcp_send_rst(tcp_recv, 0); +} + +uint32_t ump_net_tcp_receive_available (struct tcp_tcb *tcb) { + return tcb->rcv_available; +} + +uint32_t ump_net_tcp_send_available (struct tcp_tcb *tcb) { + return tcb->snd_buf_size - tcb->snd_available; +} + +uint32_t ump_net_tcp_receive (struct tcp_tcb *tcb, void *buf, uint32_t buf_size) { + if (buf_size > tcb->rcv_available) buf_size = tcb->rcv_available; + if (tcb->rcv_readpos + buf_size > tcb->rcv_buf_size) { + // Read across ring boundary + uint32_t part1_len = tcb->rcv_buf_size - tcb->rcv_readpos; + memcpy(buf, tcb->rcv_buf + tcb->rcv_readpos, part1_len); + memcpy(buf + part1_len, tcb->rcv_buf, buf_size - part1_len); + } else { + memcpy(buf, tcb->rcv_buf + tcb->rcv_readpos, buf_size); + } + tcb->rcv_available -= buf_size; + tcb->rcv_readpos = rcv_buf_wrap(tcb, tcb->rcv_readpos + buf_size); + tcb->rcv_wnd += buf_size; + // TODO: send ACK to update window + return buf_size; +} + +uint32_t ump_net_tcp_send (struct tcp_tcb *tcb, const void *buf, uint32_t buf_size) { + // uint32_t avail = ump_net_tcp_send_available(tcb); + // TODO + return 0; +} + +static void tcp_rcv_payload_callback (void *arg, size_t payload_size, void *payload) { + struct tcp_tcb *tcb = arg; + if (!deref_tcb(tcb)) { + uint32_t write_pos = rcv_buf_wrap(tcb, tcb->rcv_readpos + tcb->rcv_available); + if (write_pos + payload_size > tcb->rcv_buf_size) { + // Write across the ring buffer end. Copy rest to the start of the ring. + memcpy(tcb->rcv_buf, tcb->rcv_buf + tcb->rcv_buf_size, + write_pos + payload_size - tcb->rcv_buf_size); + } + tcb->rcv_available += payload_size; + // Notify user + tcb->receive_available_handler(tcb->handler_arg); + // debug_printf("TCP received data: '%.*s'\n", payload_size, payload); + + if (tcb->ack_needed) { + // Send ack + tcp_send_active(tcb, false); + } + } + net_recv_next(); +} + +static bool handle_tcp_header_recv (struct ump_net_ev_tcp_recv *tcp_recv, size_t payload_size) { + const struct tcp_hdr *tcp_hdr = (const struct tcp_hdr*)&tcp_recv->tcp_header; + uint32_t remote_ip = tcp_recv->src_ip; + uint16_t remote_port = uint16_rd(tcp_hdr->src); + uint16_t local_port = uint16_rd(tcp_hdr->dest); + uint16_t flags = uint16_rd(tcp_hdr->flags); + + // RFC 793 page 65 + uint64_t key = tcp_tcb_key(remote_ip, remote_port, local_port); + struct tcp_tcb *tcb = collections_hash_find(tcp_tcb_table, key); + if (tcb != NULL) { + uint32_t seg_seq = uint32_rd(tcp_hdr->seq); + uint32_t seg_ack = uint32_rd(tcp_hdr->ack); + uint32_t seg_len = payload_size; + if (flags & (TCP_SYN | TCP_FIN)) seg_len++; + + // first check sequence number + bool acceptable = false; + if (seg_len == 0 && tcb->rcv_wnd == 0) { + acceptable = seg_seq == tcb->rcv_nxt; + } else if (seg_len == 0 && tcb->rcv_wnd > 0) { + acceptable = + WRAP_LTE(tcb->rcv_nxt, seg_seq) && + WRAP_LT(seg_seq, tcb->rcv_nxt + tcb->rcv_wnd); + } else if (seg_len > 0 && tcb->rcv_wnd > 0) { + acceptable = + (WRAP_LTE(tcb->rcv_nxt, seg_seq) && + WRAP_LT(seg_seq, tcb->rcv_nxt + tcb->rcv_wnd)) || + (WRAP_LTE(tcb->rcv_nxt, seg_seq + seg_len - 1) && + WRAP_LT(seg_seq + seg_len - 1, tcb->rcv_nxt + tcb->rcv_wnd)); + } + if (!acceptable) { + if (!(flags & TCP_RST)) { + // Send ack + tcp_send_active(tcb, false); + } + } else if (seg_seq == tcb->rcv_nxt && seg_len <= tcb->rcv_wnd) { + // Packet is exactly the next expected sequence and fits into the receive window. + if (flags & TCP_RST) { + deref_tcb(tcb); + } else if (flags & TCP_SYN) { + tcp_send_rst(tcp_recv, payload_size); + deref_tcb(tcb); + } else if (!(flags & TCP_ACK)) { + // drop packet + } else { + bool ack_in_range = WRAP_LTE(tcb->snd_una, seg_ack) && WRAP_LTE(seg_ack, tcb->snd_nxt); + if (tcb->state == TCP_STATE_SYN_RECEIVED) { + if (ack_in_range) { + tcb->state = TCP_STATE_ESTABLISHED; + } else { + tcp_send_rst(tcp_recv, payload_size); + } + } + if (tcb->state == TCP_STATE_ESTABLISHED) { + if (ack_in_range && seg_ack != tcb->snd_una) { + tcb->snd_una = seg_ack; + if (WRAP_LT(tcb->snd_wl1, seg_seq) || (tcb->snd_wl1 == seg_seq && WRAP_LTE(tcb->snd_wl2, seg_ack))) { + tcb->snd_wnd = (uint32_t)uint16_rd(tcp_hdr->wnd); + tcb->snd_wl1 = seg_seq; + tcb->snd_wl2 = seg_ack; + } + // we have more send buffer space, notify user + tcb->send_available_handler(tcb->handler_arg); + } + if (WRAP_LT(tcb->snd_nxt, seg_ack)) { + // ack for something not yet sent + // Send ack + tcp_send_active(tcb, false); + return false; + } + } + // TODO: other states (rfc page 73) + + // TODO: check URG bit + + // process the segment text + if ((tcb->state == TCP_STATE_ESTABLISHED || tcb->state == TCP_STATE_FIN_WAIT_1 || + tcb->state == TCP_STATE_FIN_WAIT_2) && payload_size != 0) { + tcb->rcv_nxt += payload_size; + tcb->rcv_wnd -= payload_size; + tcb->ack_needed = true; + uint32_t write_pos = rcv_buf_wrap(tcb, tcb->rcv_readpos + tcb->rcv_available); + tcb->refcount++; + ump_recv_payload(net_recv_chan, tcb->rcv_buf + write_pos, tcp_rcv_payload_callback, tcb); + return true; + } else { + // ignore + } + // TODO: check the FIN bit + } + } else { + // TODO: for now we just drop these packets, but we should process them too + } + } else { + struct tcp_listen_entry *entry = collections_hash_find(tcp_listen_table, local_port); + if (entry != NULL) { + // state LISTEN + if (flags & TCP_RST) { + // ignore + } else if ((flags & TCP_ACK) || payload_size != 0) { + tcp_send_rst(tcp_recv, payload_size); + } else if (flags & TCP_SYN) { + entry->connect_handler(entry->connect_handler_arg, tcp_recv, remote_ip, remote_port, local_port); + } else { + // ignore + } + } else { + // state CLOSED + if (!(flags & TCP_RST)) { + tcp_send_rst(tcp_recv, payload_size); + } + } + } + + return false; +} diff --git a/usr/drivers/enet/enet.h b/usr/drivers/enet/enet.h index dd47042..8081df6 100644 --- a/usr/drivers/enet/enet.h +++ b/usr/drivers/enet/enet.h @@ -137,6 +137,25 @@ struct udp_state { uint16_t next_ephemeral_port; }; +struct tcp_reply_buf { + struct tx_alloc_queue_entry tx_entry; + uint64_t eth_dst; + uint32_t ip_dst; + uint16_t port_src; + uint16_t port_dst; + uint32_t seq; + uint32_t ack; + uint16_t flags; +}; + +struct tcp_state { + collections_hash_table* listen_table; + uint16_t next_ephemeral_port; + + struct simpleslab_allocator tcp_reply_slab; + uint8_t tcp_reply_slab_buf[sizeof(struct tcp_reply_buf) * 128]; +}; + struct ump_client; struct ump_reply_buf { @@ -151,6 +170,7 @@ struct ump_client { struct tx_alloc_queue_entry tx_entry; struct devq_buf tx_buf; struct ip_hdr *tx_ip_hdr; + uint16_t tx_ip_payload_size; // If ump_reply_slab becomes empty, this is set to true and // UMP receiving is blocked. @@ -173,10 +193,17 @@ struct udp_recv { struct ump_net_in_header ump_header; }; +struct tcp_recv { + struct devq_buf rx_buf; + struct ump_send_queue_entry ump_entry; + struct ump_net_in_header ump_header; +}; + // union to obtain max required size union rx_meta_slab_data { struct icmp_echo_reply_meta icmp_echo_reply_meta; struct udp_recv udp_recv; + struct tcp_recv tcp_recv; }; @@ -213,6 +240,7 @@ struct enet_driver_state { struct arp_state arp; struct ip_state ip; struct udp_state udp; + struct tcp_state tcp; }; #define ENET_HASH_BITS 6 diff --git a/usr/drivers/enet/enet_proto.c b/usr/drivers/enet/enet_proto.c index 4b714e7..529577b 100644 --- a/usr/drivers/enet/enet_proto.c +++ b/usr/drivers/enet/enet_proto.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -203,7 +204,7 @@ static void icmp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) { entry->rx_buf.valid_length ); - struct icmp_echo_hdr *icmp_echo_hdr = vaddr + ETH_HLEN + sizeof(struct ip_hdr); + struct icmp_echo_hdr *icmp_echo_hdr = vaddr + ETH_HLEN + IP_HLEN_MIN; memcpy(icmp_echo_hdr, entry->icmp_echo_hdr, entry->rx_buf.valid_length); icmp_echo_hdr->type = ICMP_ER; @@ -257,6 +258,22 @@ static void icmp_handle (struct devq_buf *buf, const void *vaddr, const struct e rx_release(buf); } +// UDP/TCP checksum + +static uint16_t ip_data_checksum (const struct ip_hdr *ip_hdr, const void *data, uint16_t data_len) { + uint32_t chksum = inet_checksum(data, data_len) ^ 0x0000ffffUL; + // add pseudo header + chksum += (ip_hdr->src.val[0] << 8) | ip_hdr->src.val[1]; + chksum += (ip_hdr->src.val[2] << 8) | ip_hdr->src.val[3]; + chksum += (ip_hdr->dest.val[0] << 8) | ip_hdr->dest.val[1]; + chksum += (ip_hdr->dest.val[2] << 8) | ip_hdr->dest.val[3]; + chksum += ip_hdr->proto; + chksum += data_len; + chksum = (chksum >> 16) + (chksum & 0x0000ffffUL); + chksum = (chksum >> 16) + (chksum & 0x0000ffffUL); + return ~(uint16_t)chksum; +} + // UDP: https://datatracker.ietf.org/doc/html/rfc768 static void udp_recv_ump_callback (void *arg, struct ump_send_queue_entry *entry) { @@ -265,21 +282,6 @@ static void udp_recv_ump_callback (void *arg, struct ump_send_queue_entry *entry simpleslab_free(&st->rx_meta_slab, meta); } -static uint16_t udp_checksum (const struct ip_hdr *ip_hdr, const struct udp_hdr *udp_hdr) { - uint16_t udp_len = uint16_rd(udp_hdr->len); - uint32_t chksum = inet_checksum(udp_hdr, udp_len) ^ 0x0000ffffUL; - // add pseudo header - chksum += (ip_hdr->src.val[0] << 8) | ip_hdr->src.val[1]; - chksum += (ip_hdr->src.val[2] << 8) | ip_hdr->src.val[3]; - chksum += (ip_hdr->dest.val[0] << 8) | ip_hdr->dest.val[1]; - chksum += (ip_hdr->dest.val[2] << 8) | ip_hdr->dest.val[3]; - chksum += IP_PROTO_UDP; - chksum += udp_len; - chksum = (chksum >> 16) + (chksum & 0x0000ffffUL); - chksum = (chksum >> 16) + (chksum & 0x0000ffffUL); - return ~(uint16_t)chksum; -} - static void udp_handle (struct devq_buf *buf, const void *vaddr, const struct eth_hdr *eth_hdr, const struct ip_hdr *ip_hdr) { if (buf->valid_length < UDP_HLEN) { ENET_WARN("Received UDP packet too small\n"); @@ -296,7 +298,7 @@ static void udp_handle (struct devq_buf *buf, const void *vaddr, const struct et if ( uint16_rd(udp_hdr->chksum) != 0 && // 0 means no checksum - udp_checksum(ip_hdr, udp_hdr) != 0 + ip_data_checksum(ip_hdr, udp_hdr, uint16_rd(udp_hdr->len)) != 0 ) { ENET_WARN("Received UDP packet with bad checksum\n"); rx_release(buf); @@ -309,7 +311,6 @@ static void udp_handle (struct devq_buf *buf, const void *vaddr, const struct et uint16_t payload_len = udp_len - UDP_HLEN; ENET_DEBUG("Received UDP packet from %d to %d, len %d\n", src_port, dest_port, payload_len); - struct ump_client *client = collections_hash_find(st->udp.listen_table, dest_port); if (client == NULL) { // TODO: send ICMP unreachable @@ -319,7 +320,7 @@ static void udp_handle (struct devq_buf *buf, const void *vaddr, const struct et meta->rx_buf = *buf; meta->ump_header = (struct ump_net_in_header){ .op = UMP_NET_EV_UDP_RECV, - .d = { .udp_recv = { + .ev = { .udp_recv = { .src_ip = uint32_rd(ip_hdr->src), .dest_ip = uint32_rd(ip_hdr->dest), .src_port = src_port, @@ -334,10 +335,117 @@ static void udp_handle (struct devq_buf *buf, const void *vaddr, const struct et } } +// TCP: https://datatracker.ietf.org/doc/html/rfc793 + +static void tcp_recv_ump_callback (void *arg, struct ump_send_queue_entry *entry) { + struct tcp_recv *meta = arg; + rx_release(&meta->rx_buf); + simpleslab_free(&st->rx_meta_slab, meta); +} + +static void tcp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) { + struct tcp_reply_buf *reply = cb_arg; + + write_eth_ip_header( + buf, vaddr, + reply->eth_dst, + reply->ip_dst, + IP_PROTO_TCP, + TCP_HLEN_MIN + ); + struct ip_hdr *ip_hdr = vaddr + ETH_HLEN; + struct tcp_hdr *tcp_hdr = vaddr + ETH_HLEN + IP_HLEN_MIN; + tcp_hdr->src = uint16_wr(reply->port_src); + tcp_hdr->dest = uint16_wr(reply->port_dst); + tcp_hdr->seq = uint32_wr(reply->seq); + tcp_hdr->ack = uint32_wr(reply->ack); + tcp_hdr->flags = uint16_wr(reply->flags | (5 << 12)); + tcp_hdr->wnd = uint16_wr(0); + tcp_hdr->chksum = uint16_wr(0); + tcp_hdr->up = uint16_wr(0); + + uint16_t checksum = ip_data_checksum(ip_hdr, tcp_hdr, TCP_HLEN_MIN); + tcp_hdr->chksum = uint16_wr(checksum); + + simpleslab_free(&st->tcp.tcp_reply_slab, reply); + tx_send(buf); +} + +static void tcp_handle (struct devq_buf *buf, const void *vaddr, const struct eth_hdr *eth_hdr, const struct ip_hdr *ip_hdr) { + if (buf->valid_length < TCP_HLEN_MIN) { + ENET_WARN("Received TCP packet too small\n"); + rx_release(buf); + return; + } + const struct tcp_hdr *tcp_hdr = vaddr + buf->valid_data; + uint16_t hlen = TCP_HLEN(tcp_hdr); + if (hlen < TCP_HLEN_MIN || buf->valid_length < hlen) { + ENET_WARN("Received TCP packet too small\n"); + rx_release(buf); + return; + } + + if (ip_data_checksum(ip_hdr, tcp_hdr, buf->valid_length) != 0) { + ENET_WARN("Received TCP packet with bad checksum\n"); + rx_release(buf); + return; + } + + uint16_t flags = uint16_rd(tcp_hdr->flags); + uint16_t dest_port = uint16_rd(tcp_hdr->dest); + const void *payload = (const void*)tcp_hdr + hlen; + uint16_t payload_len = buf->valid_length - hlen; + ENET_DEBUG("Received TCP packet from %d to %d, len %d\n", uint16_rd(tcp_hdr->src), dest_port, payload_len); + + struct ump_client *client = collections_hash_find(st->tcp.listen_table, dest_port); + if (client == NULL) { + if (!(flags & TCP_RST)) { + // Send RST + struct tcp_reply_buf *reply = simpleslab_alloc(&st->tcp.tcp_reply_slab); + if (reply == NULL) { + ENET_WARN("Too many queued TCP RST replies, not sending reply\n"); + } else { + reply->eth_dst = eth_addr_rd(eth_hdr->src); + reply->ip_dst = uint32_rd(ip_hdr->src); + reply->port_src = uint16_rd(tcp_hdr->dest); + reply->port_dst = uint16_rd(tcp_hdr->src); + if (flags & TCP_ACK) { + reply->seq = uint32_rd(tcp_hdr->ack); + reply->ack = 0; + reply->flags = TCP_RST; + } else { + reply->seq = 0; + uint16_t seg_len = payload_len; + if (flags & (TCP_SYN | TCP_FIN)) seg_len++; + reply->ack = uint32_rd(tcp_hdr->seq) + seg_len; + reply->flags = TCP_RST | TCP_ACK; + } + tx_alloc(&reply->tx_entry, tcp_reply, reply); + } + } + rx_release(buf); + } else { + struct tcp_recv *meta = simpleslab_alloc(&st->rx_meta_slab); + meta->rx_buf = *buf; + meta->ump_header = (struct ump_net_in_header){ + .op = UMP_NET_EV_TCP_RECV, + .ev = { .tcp_recv = { + .src_ip = uint32_rd(ip_hdr->src), + .dest_ip = uint32_rd(ip_hdr->dest), + } } + }; + memcpy(&meta->ump_header.ev.tcp_recv.tcp_header, tcp_hdr, hlen); + ump_send(client->send_chan, &meta->ump_entry, + sizeof(struct ump_net_in_header), &meta->ump_header, + payload_len, payload, + tcp_recv_ump_callback, meta); + } +} + // IP: https://datatracker.ietf.org/doc/html/rfc791#section-3.1 static void ip_handle (struct devq_buf *buf, const void *vaddr, const struct eth_hdr *eth_hdr) { - if (buf->valid_length < sizeof(struct ip_hdr)) { + if (buf->valid_length < IP_HLEN_MIN) { ENET_WARN("Received IP packet too small\n"); rx_release(buf); return; @@ -392,6 +500,8 @@ static void ip_handle (struct devq_buf *buf, const void *vaddr, const struct eth icmp_handle(buf, vaddr, eth_hdr, ip_hdr); } else if (ip_hdr->proto == IP_PROTO_UDP) { udp_handle(buf, vaddr, eth_hdr, ip_hdr); + } else if (ip_hdr->proto == IP_PROTO_TCP) { + tcp_handle(buf, vaddr, eth_hdr, ip_hdr); } else { ENET_WARN("Received IP packet with unknown protocol %d\n", ip_hdr->proto); rx_release(buf); @@ -407,7 +517,7 @@ static void write_eth_ip_header ( uint64_t eth_dst, uint32_t ip_dest, uint8_t proto, size_t payload_len ) { buf->valid_data = 0; - buf->valid_length = ETH_HLEN + sizeof(struct ip_hdr) + payload_len; + buf->valid_length = ETH_HLEN + IP_HLEN_MIN + payload_len; assert(buf->valid_length <= ETH_HLEN + 1500); struct eth_hdr *eth_hdr = vaddr; @@ -418,7 +528,7 @@ static void write_eth_ip_header ( struct ip_hdr *ip_hdr = vaddr + ETH_HLEN; IPH_VHL_SET(ip_hdr, 4, 5); ip_hdr->tos = 0; - ip_hdr->len = uint16_wr(sizeof(struct ip_hdr) + payload_len); + ip_hdr->len = uint16_wr(IP_HLEN_MIN + payload_len); ip_hdr->id = uint16_wr(st->ip.next_id++); ip_hdr->offset = uint16_wr(IP_DF); ip_hdr->ttl = 64; @@ -494,7 +604,7 @@ static void netump_payload_ignore (void *arg, size_t payload_size, void *payload } static void netump_send_reply (struct ump_reply_buf *reply, errval_t err) { - reply->ump_header.ret_err = err; + reply->ump_header.ret.err = err; ump_send(reply->client->send_chan, &reply->ump_entry, sizeof(struct ump_net_in_header), &reply->ump_header, 0, NULL, @@ -504,8 +614,8 @@ static void netump_send_reply (struct ump_reply_buf *reply, errval_t err) { static void netump_udp_tx_payload (void *arg, size_t payload_size, void *payload) { struct ump_client *client = arg; - struct udp_hdr *udp_hdr = (void*)client->tx_ip_hdr + sizeof(struct ip_hdr); - uint16_t checksum = udp_checksum(client->tx_ip_hdr, udp_hdr); + struct udp_hdr *udp_hdr = (void*)client->tx_ip_hdr + IP_HLEN_MIN; + uint16_t checksum = ip_data_checksum(client->tx_ip_hdr, udp_hdr, uint16_rd(udp_hdr->len)); if (checksum == 0) checksum = 0xffff; udp_hdr->chksum = uint16_wr(checksum); @@ -535,7 +645,7 @@ static void netump_udp_tx_allocated (void *arg, struct devq_buf *buf, void *vadd IP_PROTO_UDP, UDP_HLEN + payload_size ); - struct udp_hdr *udp_hdr = vaddr + ETH_HLEN + sizeof(struct ip_hdr); + struct udp_hdr *udp_hdr = vaddr + ETH_HLEN + IP_HLEN_MIN; udp_hdr->src = uint16_wr(header->d.udp_send.src_port); udp_hdr->dest = uint16_wr(header->d.udp_send.dest_port); udp_hdr->len = uint16_wr(UDP_HLEN + payload_size); @@ -548,6 +658,53 @@ static void netump_udp_tx_allocated (void *arg, struct devq_buf *buf, void *vadd ump_recv_payload(client->recv_chan, payload, netump_udp_tx_payload, client); } +static void netump_tcp_tx_payload (void *arg, size_t payload_size, void *payload) { + struct ump_client *client = arg; + + struct tcp_hdr *tcp_hdr = (void*)client->tx_ip_hdr + IP_HLEN_MIN; + tcp_hdr->chksum = uint16_wr(0); + uint16_t checksum = ip_data_checksum(client->tx_ip_hdr, tcp_hdr, client->tx_ip_payload_size); + tcp_hdr->chksum = uint16_wr(checksum); + + tx_send(&client->tx_buf); + netump_next(client); +} + +static void netump_tcp_tx_allocated (void *arg, struct devq_buf *buf, void *vaddr) { + struct ump_reply_buf *reply = arg; + struct ump_client *client = reply->client; + struct ump_net_out_header *header = client->recv_chan->header; + size_t payload_size = client->recv_chan->next_payload_size; + + uint64_t eth_dest = (uintptr_t)collections_hash_find(st->arp.table, header->d.tcp_send.dest_ip); + if (eth_dest == 0) { + ENET_WARN("IP not in ARP table, dropping packet and sending ARP request\n"); + arp_send(buf, vaddr, 0xffffffffffff, + header->d.tcp_send.dest_ip, ARP_OP_REQ); + netump_send_reply(reply, LIB_ERR_NET_ARP_MISS); + ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client); + return; + } + const struct tcp_hdr *ump_tcp_hdr = (const struct tcp_hdr*)&header->d.tcp_send.tcp_header; + uint16_t tcp_hlen = TCP_HLEN(ump_tcp_hdr); + write_eth_ip_header( + buf, vaddr, + eth_dest, + header->d.tcp_send.dest_ip, + IP_PROTO_TCP, + tcp_hlen + payload_size + ); + client->tx_buf = *buf; + client->tx_ip_hdr = vaddr + ETH_HLEN; + client->tx_ip_payload_size = tcp_hlen + payload_size; + void *tcp_header = vaddr + ETH_HLEN + IP_HLEN_MIN; + memcpy(tcp_header, ump_tcp_hdr, tcp_hlen); + void *payload = tcp_header + tcp_hlen; + + netump_send_reply(reply, SYS_ERR_OK); + ump_recv_payload(client->recv_chan, payload, netump_tcp_tx_payload, client); +} + static void netump_header_handle (void *arg, size_t header_size, void *header_raw, size_t payload_size) { struct ump_client *client = arg; struct ump_net_out_header *header = header_raw; @@ -559,7 +716,7 @@ static void netump_header_handle (void *arg, size_t header_size, void *header_ra reply->ump_header.op = header->op; if (header->op == UMP_NET_OP_UDP_SEND) { - if (payload_size > 1500 - sizeof(struct ip_hdr) - UDP_HLEN) { + if (payload_size > 1500 - IP_HLEN_MIN - UDP_HLEN) { err = LIB_ERR_NET_PACKET_TOO_BIG; } else { tx_alloc(&client->tx_entry, netump_udp_tx_allocated, reply); @@ -595,7 +752,7 @@ static void netump_header_handle (void *arg, size_t header_size, void *header_ra err = LIB_ERR_NET_PORT_IN_USE; } else { ENET_DEBUG("Listening on UDP port %d\n", port); - reply->ump_header.d.udp_listen.dest_port = port; + reply->ump_header.ret.d.udp_listen.dest_port = port; collections_hash_insert(st->udp.listen_table, port, client); err = SYS_ERR_OK; } @@ -608,6 +765,56 @@ static void netump_header_handle (void *arg, size_t header_size, void *header_ra collections_hash_delete(st->udp.listen_table, port); err = SYS_ERR_OK; } + } else if (header->op == UMP_NET_OP_TCP_SEND) { + if (payload_size > 1500 - IP_HLEN_MIN - TCP_HLEN((struct tcp_hdr*)&header->d.tcp_send.tcp_header)) { + err = LIB_ERR_NET_PACKET_TOO_BIG; + } else { + tx_alloc(&client->tx_entry, netump_tcp_tx_allocated, reply); + return; + } + } else if (header->op == UMP_NET_OP_TCP_LISTEN) { + uint16_t port = header->d.tcp_listen.dest_port; + if (port == 0) { + // allocate an ephemeral port + uint16_t first = st->tcp.next_ephemeral_port; + port = first; + while (true) { + uint16_t next_port; + if (port == EPHEMERAL_PORT_END) { + next_port = EPHEMERAL_PORT_START; + } else { + next_port = port + 1; + } + if (collections_hash_find(st->tcp.listen_table, port) == NULL) { + st->tcp.next_ephemeral_port = next_port; + break; + } + port = next_port; + if (port == first) { + port = 0; + break; + } + } + } + if (port == 0) { + err = LIB_ERR_NET_ALLOC_PORT; + } else if (collections_hash_find(st->tcp.listen_table, port) != NULL) { + err = LIB_ERR_NET_PORT_IN_USE; + } else { + ENET_DEBUG("Listening on TCP port %d\n", port); + reply->ump_header.ret.d.tcp_listen.dest_port = port; + collections_hash_insert(st->tcp.listen_table, port, client); + err = SYS_ERR_OK; + } + } else if (header->op == UMP_NET_OP_TCP_LISTEN_STOP) { + uint16_t port = header->d.tcp_listen_stop.dest_port; + if (collections_hash_find(st->tcp.listen_table, port) != client) { + err = LIB_ERR_NET_NOT_LISTENING; + } else { + ENET_DEBUG("Stopped listening on TCP port %d\n", port); + collections_hash_delete(st->tcp.listen_table, port); + err = SYS_ERR_OK; + } } else { debug_printf("Error: unknown UMP op\n"); simpleslab_free(&client->ump_reply_slab, reply); @@ -652,6 +859,12 @@ void enet_loop (void) { collections_hash_create_with_buckets(&st->udp.listen_table, 100, NULL); st->udp.next_ephemeral_port = EPHEMERAL_PORT_START; + collections_hash_create_with_buckets(&st->tcp.listen_table, 100, NULL); + st->tcp.next_ephemeral_port = EPHEMERAL_PORT_START; + + simpleslab_init(&st->tcp.tcp_reply_slab, sizeof(struct tcp_reply_buf), + &st->tcp.tcp_reply_slab_buf, sizeof(st->tcp.tcp_reply_slab_buf)); + simpleslab_init(&st->rx_meta_slab, sizeof(union rx_meta_slab_data), &st->rx_meta_slab_buf, sizeof(st->rx_meta_slab_buf)); diff --git a/usr/echoserver/main.c b/usr/echoserver/main.c index d081de1..0be86fa 100644 --- a/usr/echoserver/main.c +++ b/usr/echoserver/main.c @@ -56,7 +56,7 @@ static errval_t connect_server (void *arg, struct capref cap) { } -// net echo server +// UDP echo server #define BUF_SIZE 1500 struct echo_buf { @@ -69,7 +69,7 @@ static struct simpleslab_allocator slabs; static uint8_t slab_buf[sizeof(struct echo_buf) * 128]; -static void net_listen_callback (void *arg, errval_t err, uint16_t src_port) { +static void udp_listen_callback (void *arg, errval_t err, uint16_t src_port) { if (err_is_fail(err)) { DEBUG_ERR(err, "failed to listen on UDP"); return; @@ -77,12 +77,6 @@ static void net_listen_callback (void *arg, errval_t err, uint16_t src_port) { printf("Echo server: Listening on UDP port %d.\n", src_port); } -static void net_send_callback (void *arg, errval_t err) { - if (err_is_fail(err)) { - DEBUG_ERR(err, "failed to send packet"); - } -} - static void net_reply_callback (void *arg, errval_t err) { struct echo_buf *echo_buf = arg; simpleslab_free(&slabs, echo_buf); @@ -123,11 +117,86 @@ static void handle_udp_recv (void *arg, struct ump_net_ev_udp_recv *udp_recv, si ump_net_recv_payload(NULL, NULL, NULL); } +// UDP send test + +static void net_send_callback (void *arg, errval_t err) { + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to send packet"); + } +} + static void send_again (void *arg) { static struct ump_net_call call; ump_net_udp_send(&call, 0xc0a80204, PORT_ECHO, 7000, 12, "hello again\n", net_send_callback, NULL); } +// TCP echo server + +#define TCP_MAX_CONNECTIONS 100 +struct tcp_conn { + struct tcp_tcb tcb; + uint8_t rcv_buf[4*4096]; + uint8_t snd_buf[4*4096]; +}; + +static int tcp_connection_count = 0; + +static void tcp_listen_callback (void *arg, errval_t err, uint16_t src_port) { + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to listen on TCP"); + return; + } + printf("Echo server: Listening on TCP port %d.\n", src_port); +} + +static void tcp_receive_available_handler (void *arg) { + struct tcp_conn *conn = arg; + uint8_t buf[2048]; + while (true) { + uint32_t read_size = ump_net_tcp_receive(&conn->tcb, buf, sizeof(buf)); + if (read_size == 0) break; + debug_printf("TCP received: '%.*s'\n", read_size, buf); + } +} + +static void tcp_send_available_handler (void *arg) { + // struct tcp_conn *conn = arg; + debug_printf("tcp_send_available_handler\n"); +} + +static void tcp_destroy_handler (void *arg) { + struct tcp_conn *conn = arg; + debug_printf("TCP connection destroyed for %x, port %d\n", conn->tcb.remote_ip, conn->tcb.remote_port); + free(conn); + tcp_connection_count--; +} + +static void handle_tcp_connect ( + void *arg, struct ump_net_ev_tcp_recv *tcp_recv, + uint32_t remote_ip, uint16_t remote_port, uint16_t local_port +) { + debug_printf("Received connection attempt from %x, port %d\n", remote_ip, remote_port); + if (tcp_connection_count > TCP_MAX_CONNECTIONS) { + ump_net_tcp_refuse(tcp_recv); + } else { + struct tcp_conn *conn = malloc(sizeof(struct tcp_conn)); + if (conn == NULL) { + ump_net_tcp_refuse(tcp_recv); + } else { + tcp_connection_count++; + ump_net_tcp_accept( + tcp_recv, &conn->tcb, + tcp_receive_available_handler, + tcp_send_available_handler, + tcp_destroy_handler, + conn, + conn->rcv_buf, sizeof(conn->rcv_buf), + conn->snd_buf, sizeof(conn->snd_buf) + ); + } + } +} + int main (int argc, char *argv[]) { errval_t err; struct waitset *default_ws = get_default_waitset(); @@ -144,11 +213,14 @@ int main (int argc, char *argv[]) { err = ump_net_init(default_ws); if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to connect to net server"); - struct ump_net_call call; - ump_net_udp_listen(&call, PORT_ECHO, handle_udp_recv, NULL, net_listen_callback, NULL); + struct ump_net_call call_udp_listen; + ump_net_udp_listen(&call_udp_listen, PORT_ECHO, handle_udp_recv, NULL, udp_listen_callback, NULL); - struct ump_net_call call2; - ump_net_udp_send(&call2, 0xc0a80204, PORT_ECHO, 7000, 6, "hello\n", net_send_callback, NULL); + struct ump_net_call call_tcp_listen; + ump_net_tcp_listen(&call_tcp_listen, PORT_ECHO, handle_tcp_connect, NULL, tcp_listen_callback, NULL); + + struct ump_net_call call; + ump_net_udp_send(&call, 0xc0a80204, PORT_ECHO, 7000, 6, "hello\n", net_send_callback, NULL); struct deferred_event dev; deferred_event_init(&dev); From 06ae3e1827cf0581332f0b2c4ef37f81f1bfe3f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Thu, 2 Jun 2022 16:09:24 +0200 Subject: [PATCH 2/2] enet: Remove UDP send test --- usr/echoserver/main.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/usr/echoserver/main.c b/usr/echoserver/main.c index 0be86fa..2fc7ae6 100644 --- a/usr/echoserver/main.c +++ b/usr/echoserver/main.c @@ -117,19 +117,6 @@ static void handle_udp_recv (void *arg, struct ump_net_ev_udp_recv *udp_recv, si ump_net_recv_payload(NULL, NULL, NULL); } -// UDP send test - -static void net_send_callback (void *arg, errval_t err) { - if (err_is_fail(err)) { - DEBUG_ERR(err, "failed to send packet"); - } -} - -static void send_again (void *arg) { - static struct ump_net_call call; - ump_net_udp_send(&call, 0xc0a80204, PORT_ECHO, 7000, 12, "hello again\n", net_send_callback, NULL); -} - // TCP echo server #define TCP_MAX_CONNECTIONS 100 @@ -219,16 +206,6 @@ int main (int argc, char *argv[]) { struct ump_net_call call_tcp_listen; ump_net_tcp_listen(&call_tcp_listen, PORT_ECHO, handle_tcp_connect, NULL, tcp_listen_callback, NULL); - struct ump_net_call call; - ump_net_udp_send(&call, 0xc0a80204, PORT_ECHO, 7000, 6, "hello\n", net_send_callback, NULL); - - struct deferred_event dev; - deferred_event_init(&dev); - err = deferred_event_register(&dev, default_ws, 1000000, - MKCLOSURE(send_again, NULL)); - if (err_is_fail(err)) return err; - - while (true) { err = event_dispatch(default_ws); if (err_is_fail(err)) {