aos/lib/aos/ump_net_client.c
2022-06-02 16:06:49 +02:00

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