Merge branch 'main' of gitlab.ethz.ch:rueegges/aos into main
This commit is contained in:
commit
db31cdd70a
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
45
include/netutil/tcp.h
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -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,9 +117,71 @@ static void handle_udp_recv (void *arg, struct ump_net_ev_udp_recv *udp_recv, si
|
||||
ump_net_recv_payload(NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
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[]) {
|
||||
@ -144,18 +200,11 @@ 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 call2;
|
||||
ump_net_udp_send(&call2, 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;
|
||||
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 call_tcp_listen;
|
||||
ump_net_tcp_listen(&call_tcp_listen, PORT_ECHO, handle_tcp_connect, NULL, tcp_listen_callback, NULL);
|
||||
|
||||
while (true) {
|
||||
err = event_dispatch(default_ws);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user