698 lines
24 KiB
C
698 lines
24 KiB
C
#include <aos/aos.h>
|
|
#include <aos/deferred.h>
|
|
#include <aos/aos_rpc.h>
|
|
#include <aos/ump_binding.h>
|
|
#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;
|
|
static struct ump_recv_chan *net_recv_chan;
|
|
|
|
static struct ump_net_call *call_queue_head = NULL;
|
|
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);
|
|
|
|
errval_t ump_net_init (struct waitset *ws) {
|
|
if (net_initialized) return LIB_ERR_NET_ALREADY_INIT;
|
|
net_initialized = true;
|
|
|
|
errval_t err;
|
|
struct aos_rpc *rpc = aos_rpc_get_init_channel();
|
|
struct capref net_cap;
|
|
for (int attempts = 0; attempts < 60; attempts++) {
|
|
err = aos_rpc_ump_connect(rpc, UMP_SERVER_NET, &net_cap);
|
|
if (err != LIB_ERR_UMP_NOT_REGISTERED) break;
|
|
barrelfish_usleep(500000);
|
|
}
|
|
if (err_is_fail(err)) return err;
|
|
|
|
// Create the two uni-directional channels
|
|
err = ump_chan_init(UMP_ROLE_CLIENT, &net_send_chan, &net_recv_chan,
|
|
sizeof(struct ump_net_in_header), net_cap, ws);
|
|
if (err_is_fail(err)) return err;
|
|
|
|
net_recv_next();
|
|
|
|
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;
|
|
}
|
|
|
|
static void net_recv_next (void) {
|
|
ump_recv_header(net_recv_chan, handle_net_header_recv, NULL);
|
|
}
|
|
|
|
static void net_recv_ignore_payload (void *arg, size_t payload_size, void *payload) {
|
|
net_recv_next();
|
|
}
|
|
|
|
// This is called twice, once from the send callback, once from the receive
|
|
// handler. We do it this way because we don't know in which order these
|
|
// two events happen.
|
|
static void ump_net_op_callback (void *arg, struct ump_send_queue_entry *entry) {
|
|
struct ump_net_call *call = arg;
|
|
if (call->half_done == false) {
|
|
call->half_done = true;
|
|
return;
|
|
}
|
|
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
|
|
);
|
|
} 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.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
|
|
);
|
|
} 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->ev.udp_recv.dest_port);
|
|
if (entry != NULL) {
|
|
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);
|
|
call_queue_head->in_header = *header;
|
|
ump_net_op_callback(call_queue_head, NULL);
|
|
call_queue_head = call_queue_head->next;
|
|
if (call_queue_head == NULL) call_queue_tail = NULL;
|
|
}
|
|
ump_recv_payload(net_recv_chan, NULL, net_recv_ignore_payload, NULL);
|
|
}
|
|
|
|
static void ump_net_send_call (
|
|
struct ump_net_call *call,
|
|
size_t payload_size, const void *payload,
|
|
void *cb, void *cb_arg
|
|
) {
|
|
call->half_done = false;
|
|
call->cb = cb;
|
|
call->cb_arg = cb_arg;
|
|
if (call_queue_head == NULL) {
|
|
call_queue_head = call;
|
|
} else {
|
|
call_queue_tail->next = call;
|
|
}
|
|
call_queue_tail = call;
|
|
ump_send(net_send_chan, &call->ump_entry,
|
|
sizeof(struct ump_net_out_header), &call->out_header,
|
|
payload_size, payload,
|
|
ump_net_op_callback, call);
|
|
}
|
|
|
|
void ump_net_udp_send (
|
|
struct ump_net_call *call,
|
|
uint32_t dest_ip,
|
|
uint16_t src_port,
|
|
uint16_t dest_port,
|
|
size_t payload_size, const void *payload,
|
|
ump_net_udp_send_callback_t cb, void *cb_arg
|
|
) {
|
|
call->out_header = (struct ump_net_out_header){
|
|
.op = UMP_NET_OP_UDP_SEND,
|
|
.d = { .udp_send = {
|
|
.dest_ip = dest_ip,
|
|
.src_port = src_port,
|
|
.dest_port = dest_port,
|
|
} }
|
|
};
|
|
ump_net_send_call(call, payload_size, payload, cb, cb_arg);
|
|
}
|
|
|
|
void ump_net_udp_listen (
|
|
struct ump_net_call *call,
|
|
uint16_t dest_port,
|
|
ump_net_udp_recv_handler_t recv_handler, void *recv_handler_arg,
|
|
ump_net_udp_listen_callback_t cb, void *cb_arg
|
|
) {
|
|
if (collections_hash_find(udp_listen_table, dest_port) != NULL) {
|
|
cb(cb_arg, LIB_ERR_NET_PORT_IN_USE, 0);
|
|
return;
|
|
}
|
|
struct udp_listen_entry *entry = malloc(sizeof(struct udp_listen_entry));
|
|
if (entry == NULL) {
|
|
cb(cb_arg, LIB_ERR_MALLOC_FAIL, 0);
|
|
return;
|
|
}
|
|
entry->recv_handler = recv_handler;
|
|
entry->recv_handler_arg = recv_handler_arg;
|
|
collections_hash_insert(udp_listen_table, dest_port, entry);
|
|
|
|
call->out_header = (struct ump_net_out_header){
|
|
.op = UMP_NET_OP_UDP_LISTEN,
|
|
.d = { .udp_listen = { .dest_port = dest_port } }
|
|
};
|
|
ump_net_send_call(call, 0, NULL, cb, cb_arg);
|
|
}
|
|
|
|
void ump_net_udp_listen_stop (
|
|
struct ump_net_call *call,
|
|
uint16_t dest_port,
|
|
ump_net_udp_listen_stop_callback_t cb, void *cb_arg
|
|
) {
|
|
if (collections_hash_find(udp_listen_table, dest_port) == NULL) {
|
|
cb(cb_arg, LIB_ERR_NET_NOT_LISTENING);
|
|
return;
|
|
}
|
|
collections_hash_delete(udp_listen_table, dest_port);
|
|
|
|
call->out_header = (struct ump_net_out_header){
|
|
.op = UMP_NET_OP_UDP_LISTEN_STOP,
|
|
.d = { .udp_listen_stop = { .dest_port = dest_port } }
|
|
};
|
|
ump_net_send_call(call, 0, NULL, cb, cb_arg);
|
|
}
|
|
|
|
static ump_recv_payload_callback_fn_t recv_payload_callback;
|
|
static void *recv_payload_callback_arg;
|
|
|
|
static void recv_payload_callback_wrap (void *arg, size_t payload_size, void *payload) {
|
|
if (recv_payload_callback != NULL) {
|
|
recv_payload_callback(recv_payload_callback_arg, payload_size, payload);
|
|
}
|
|
net_recv_next();
|
|
}
|
|
|
|
void ump_net_recv_payload (
|
|
void *payload,
|
|
ump_recv_payload_callback_fn_t callback, void *callback_arg
|
|
) {
|
|
recv_payload_callback = callback;
|
|
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;
|
|
}
|