enet: Unfinished TCP implementation

This commit is contained in:
Jan Schär 2022-06-02 16:06:49 +02:00
parent de5c0d664d
commit a6ad10d9e0
11 changed files with 1097 additions and 59 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

45
include/netutil/tcp.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef _TCP_H_
#define _TCP_H_
#include <netutil/ip.h>
#include <netutil/types.h>
//#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

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -5,13 +5,36 @@
#include <aos/ump_chan.h>
#include <aos/ump_net.h>
#include <aos/ump_net_client.h>
#include <aos/systime.h>
#include <aos/simpleslab.h>
#include <collections/hash_table.h>
#include <netutil/tcp.h>
#include <netutil/types.h>
// 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;
}

View File

@ -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

View File

@ -12,6 +12,7 @@
#include <netutil/ip.h>
#include <netutil/icmp.h>
#include <netutil/udp.h>
#include <netutil/tcp.h>
#include <netutil/checksum.h>
#include <netutil/types.h>
@ -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));

View File

@ -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);