[lib][minip] start of TCP connect state machine

A few miscellaneous tweaks outside of that.
This commit is contained in:
Travis Geiselbrecht
2021-02-06 14:28:46 -08:00
committed by Travis Geiselbrecht
parent dc09bac586
commit 9caf62273c
7 changed files with 149 additions and 34 deletions

View File

@@ -16,26 +16,19 @@
#include <kernel/mutex.h> #include <kernel/mutex.h>
#include <lk/trace.h> #include <lk/trace.h>
typedef union {
uint32_t u;
uint8_t b[4];
} ipv4_t;
#define LOCAL_TRACE 0 #define LOCAL_TRACE 0
static struct list_node arp_list;
typedef struct { typedef struct {
struct list_node node; struct list_node node;
uint32_t addr; uint32_t addr;
uint8_t mac[6]; uint8_t mac[6];
} arp_entry_t; } arp_entry_t;
static struct list_node arp_list = LIST_INITIAL_VALUE(arp_list);
static mutex_t arp_mutex = MUTEX_INITIAL_VALUE(arp_mutex); static mutex_t arp_mutex = MUTEX_INITIAL_VALUE(arp_mutex);
void arp_cache_init(void) { void arp_cache_init(void) {}
list_initialize(&arp_list);
}
static inline void mru_update(struct list_node *entry) { static void mru_update(struct list_node *entry) {
if (arp_list.next == entry) if (arp_list.next == entry)
return; return;
@@ -77,7 +70,7 @@ void arp_cache_update(uint32_t addr, const uint8_t mac[6]) {
} }
arp->addr = addr; arp->addr = addr;
memcpy(arp->mac, mac, sizeof(arp->mac)); mac_addr_copy(arp->mac, mac);
list_add_head(&arp_list, &arp->node); list_add_head(&arp_list, &arp->node);
} }

View File

@@ -253,6 +253,9 @@ void dhcp::udp_callback(void *data, size_t sz, uint32_t srcip, uint16_t srcport)
#endif #endif
sz -= sizeof(dhcp_msg_t); sz -= sizeof(dhcp_msg_t);
opt = msg->options; opt = msg->options;
#if TRACE_DHCP
printf("\toptions: ");
#endif
while (sz >= 2) { while (sz >= 2) {
sz -= 2; sz -= 2;
if (opt[1] > sz) { if (opt[1] > sz) {

View File

@@ -8,15 +8,14 @@
*/ */
#pragma once #pragma once
#include <lk/compiler.h>
#include <endian.h> #include <endian.h>
#include <iovec.h>
#include <lib/pktbuf.h>
#include <lk/compiler.h>
#include <lk/list.h> #include <lk/list.h>
#include <stdint.h> #include <stdint.h>
#include <iovec.h>
#include <sys/types.h> #include <sys/types.h>
#include <lib/pktbuf.h>
__BEGIN_CDECLS __BEGIN_CDECLS
#define IPV4(a,b,c,d) (((a)&0xFF)|(((b)&0xFF)<<8)|(((c)&0xFF)<<16)|(((d)&0xFF)<<24)) #define IPV4(a,b,c,d) (((a)&0xFF)|(((b)&0xFF)<<8)|(((c)&0xFF)<<16)|(((d)&0xFF)<<24))
@@ -45,6 +44,11 @@ void minip_set_macaddr(const uint8_t *addr);
uint32_t minip_get_ipaddr(void); uint32_t minip_get_ipaddr(void);
void minip_set_ipaddr(const uint32_t addr); void minip_set_ipaddr(const uint32_t addr);
uint32_t minip_get_netmask(void);
void minip_set_netmask(const uint32_t mask);
uint32_t minip_get_broadcast(void); // computed from ipaddr & netmask
uint32_t minip_get_gateway(void);
void minip_set_gateway(const uint32_t addr);
void minip_set_hostname(const char *name); void minip_set_hostname(const char *name);
const char *minip_get_hostname(void); const char *minip_get_hostname(void);
@@ -69,6 +73,7 @@ status_t udp_close(udp_socket_t *handle);
/* tcp */ /* tcp */
typedef struct tcp_socket tcp_socket_t; typedef struct tcp_socket tcp_socket_t;
status_t tcp_connect(tcp_socket_t **handle, uint32_t addr, uint16_t port);
status_t tcp_open_listen(tcp_socket_t **handle, uint16_t port); status_t tcp_open_listen(tcp_socket_t **handle, uint16_t port);
status_t tcp_accept_timeout(tcp_socket_t *listen_socket, tcp_socket_t **accept_socket, lk_time_t timeout); status_t tcp_accept_timeout(tcp_socket_t *listen_socket, tcp_socket_t **accept_socket, lk_time_t timeout);
status_t tcp_close(tcp_socket_t *socket); status_t tcp_close(tcp_socket_t *socket);

View File

@@ -89,7 +89,13 @@ enum {
extern tx_func_t minip_tx_handler; extern tx_func_t minip_tx_handler;
typedef struct udp_hdr udp_hdr_t; typedef struct udp_hdr udp_hdr_t;
static const uint8_t bcast_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static const uint8_t bcast_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
typedef uint32_t ipv4_addr;
typedef union {
uint32_t u;
uint8_t b[4];
} ipv4_t;
// ARP cache
void arp_cache_init(void); void arp_cache_init(void);
void arp_cache_update(uint32_t addr, const uint8_t mac[6]); void arp_cache_update(uint32_t addr, const uint8_t mac[6]);
uint8_t *arp_cache_lookup(uint32_t addr); uint8_t *arp_cache_lookup(uint32_t addr);
@@ -101,7 +107,7 @@ uint16_t rfc1701_chksum(const uint8_t *buf, size_t len);
uint16_t rfc768_chksum(struct ipv4_hdr *ipv4, udp_hdr_t *udp); uint16_t rfc768_chksum(struct ipv4_hdr *ipv4, udp_hdr_t *udp);
uint16_t ones_sum16(uint32_t sum, const void *_buf, int len); uint16_t ones_sum16(uint32_t sum, const void *_buf, int len);
/* Helper methods for building headers */ // Helper methods for building headers
void minip_build_mac_hdr(struct eth_hdr *pkt, const uint8_t *dst, uint16_t type); void minip_build_mac_hdr(struct eth_hdr *pkt, const uint8_t *dst, uint16_t type);
void minip_build_ipv4_hdr(struct ipv4_hdr *ipv4, uint32_t dst, uint8_t proto, uint16_t len); void minip_build_ipv4_hdr(struct ipv4_hdr *ipv4, uint32_t dst, uint8_t proto, uint16_t len);

View File

@@ -22,8 +22,6 @@
#include <lk/list.h> #include <lk/list.h>
#include <kernel/thread.h> #include <kernel/thread.h>
static struct list_node arp_list = LIST_INITIAL_VALUE(arp_list);
// TODO // TODO
// 1. Tear endian code out into something that flips words before/after tx/rx calls // 1. Tear endian code out into something that flips words before/after tx/rx calls
@@ -109,10 +107,9 @@ void minip_init(tx_func_t tx_handler, void *tx_arg,
minip_tx_handler = tx_handler; minip_tx_handler = tx_handler;
minip_tx_arg = tx_arg; minip_tx_arg = tx_arg;
minip_ip = ip; minip_set_ipaddr(ip);
minip_netmask = mask; minip_set_netmask(mask);
minip_gateway = gateway; minip_set_gateway(gateway);
compute_broadcast_address();
arp_cache_init(); arp_cache_init();
net_timer_init(); net_timer_init();
@@ -209,13 +206,24 @@ status_t minip_ipv4_send(pktbuf_t *p, uint32_t dest_addr, uint8_t proto) {
struct ipv4_hdr *ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr)); struct ipv4_hdr *ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr));
struct eth_hdr *eth = pktbuf_prepend(p, sizeof(struct eth_hdr)); struct eth_hdr *eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
// are we sending a broadcast packet?
if (dest_addr == IPV4_BCAST || dest_addr == minip_broadcast) { if (dest_addr == IPV4_BCAST || dest_addr == minip_broadcast) {
dst_mac = bcast_mac; dst_mac = bcast_mac;
goto ready; goto ready;
} }
dst_mac = get_dest_mac(dest_addr); // is this a local subnet packet or do we need to send to the router?
uint32_t target_addr = dest_addr;
if ((dest_addr & minip_netmask) != (minip_ip & minip_netmask)) {
// need to use the gateway
if (minip_gateway == IPV4_NONE) {
return ERR_NOT_FOUND; // TODO: better error code
}
target_addr = minip_gateway;
}
dst_mac = arp_get_dest_mac(target_addr);
if (!dst_mac) { if (!dst_mac) {
pktbuf_free(p, true); pktbuf_free(p, true);
ret = -EHOSTUNREACH; ret = -EHOSTUNREACH;

View File

@@ -25,8 +25,6 @@
#define LOCAL_TRACE 0 #define LOCAL_TRACE 0
typedef uint32_t ipv4_addr;
typedef struct tcp_header { typedef struct tcp_header {
uint16_t source_port; uint16_t source_port;
uint16_t dest_port; uint16_t dest_port;
@@ -114,6 +112,9 @@ typedef struct tcp_socket {
struct tcp_socket *accepted; struct tcp_socket *accepted;
net_timer_t time_wait_timer; net_timer_t time_wait_timer;
/* connect waiting */
event_t connect_event;
} tcp_socket_t; } tcp_socket_t;
#define DEFAULT_MSS (1460) #define DEFAULT_MSS (1460)
@@ -216,7 +217,7 @@ static void dump_socket(tcp_socket_t *s) {
} }
static tcp_socket_t *lookup_socket(ipv4_addr remote_ip, ipv4_addr local_ip, uint16_t remote_port, uint16_t local_port) { static tcp_socket_t *lookup_socket(ipv4_addr remote_ip, ipv4_addr local_ip, uint16_t remote_port, uint16_t local_port) {
LTRACEF("remote ip 0x%x local ip 0x%x remote port %u local port %u\n", remote_ip, local_ip, remote_port, local_port); LTRACEF_LEVEL(2, "remote ip 0x%x local ip 0x%x remote port %u local port %u\n", remote_ip, local_ip, remote_port, local_port);
mutex_acquire(&tcp_socket_list_lock); mutex_acquire(&tcp_socket_list_lock);
@@ -286,7 +287,7 @@ static void inc_socket_ref(tcp_socket_t *s) {
DEBUG_ASSERT(s); DEBUG_ASSERT(s);
__UNUSED int oldval = atomic_add(&s->ref, 1); __UNUSED int oldval = atomic_add(&s->ref, 1);
LTRACEF("caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval + 1); LTRACEF_LEVEL(2, "caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval + 1);
DEBUG_ASSERT(oldval > 0); DEBUG_ASSERT(oldval > 0);
} }
@@ -294,12 +295,13 @@ static bool dec_socket_ref(tcp_socket_t *s) {
DEBUG_ASSERT(s); DEBUG_ASSERT(s);
int oldval = atomic_add(&s->ref, -1); int oldval = atomic_add(&s->ref, -1);
LTRACEF("caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval - 1); LTRACEF_LEVEL(2, "caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval - 1);
if (oldval == 1) { if (oldval == 1) {
LTRACEF("destroying socket\n"); LTRACEF("destroying socket\n");
event_destroy(&s->tx_event); event_destroy(&s->tx_event);
event_destroy(&s->rx_event); event_destroy(&s->rx_event);
event_destroy(&s->connect_event);
free(s->rx_buffer_raw); free(s->rx_buffer_raw);
free(s->tx_buffer); free(s->tx_buffer);
@@ -482,6 +484,44 @@ void tcp_input(pktbuf_t *p, uint32_t src_ip, uint32_t dst_ip) {
break; break;
/* active connection state */
case STATE_SYN_SENT:
if ((packet_flags & PKT_SYN) == 0) {
// we got data on the packet without a syn, reset
goto send_reset;
}
if ((packet_flags & PKT_ACK) == 0) {
// simultaneous SYN/ACK
// TODO: handle
goto send_reset;
}
LTRACEF("ack num %d tx win_low %d\n", header->ack_num, s->tx_win_low);
if (header->ack_num != s->tx_win_low + 1) {
// they didn't ack our syn
goto send_reset;
}
// remember their sequence
s->rx_win_low = header->seq_num + 1;
s->rx_win_high = s->rx_win_low + s->rx_win_size - 1;
s->tx_win_low++;
s->tx_win_high = s->tx_win_low + header->win_size;
s->tx_highest_seq = s->tx_win_low;
s->state = STATE_ESTABLISHED;
send_ack(s);
event_signal(&s->connect_event, true);
break;
/* established state */
case STATE_ESTABLISHED: case STATE_ESTABLISHED:
if (packet_flags & PKT_ACK) { if (packet_flags & PKT_ACK) {
/* they're acking us */ /* they're acking us */
@@ -565,9 +605,6 @@ fin_wait_2:
case STATE_TIME_WAIT: case STATE_TIME_WAIT:
/* /dev/null of packets */ /* /dev/null of packets */
break; break;
case STATE_SYN_SENT:
PANIC_UNIMPLEMENTED;
} }
done: done:
@@ -887,6 +924,7 @@ static void tcp_wakeup_waiters(tcp_socket_t *s) {
// wake up any waiters // wake up any waiters
event_signal(&s->rx_event, true); event_signal(&s->rx_event, true);
event_signal(&s->tx_event, true); event_signal(&s->tx_event, true);
event_signal(&s->connect_event, true);
} }
static void tcp_remote_close(tcp_socket_t *s) { static void tcp_remote_close(tcp_socket_t *s) {
@@ -938,11 +976,72 @@ static tcp_socket_t *create_tcp_socket(bool alloc_buffers) {
} }
sem_init(&s->accept_sem, 0); sem_init(&s->accept_sem, 0);
event_init(&s->connect_event, false, 0);
return s; return s;
} }
/* user api */ /* user api */
status_t tcp_connect(tcp_socket_t **handle, uint32_t addr, uint16_t port) {
tcp_socket_t *s;
if (!handle)
return ERR_INVALID_ARGS;
s = create_tcp_socket(true);
if (!s)
return ERR_NO_MEMORY;
// XXX add some entropy to try to better randomize things
lk_bigtime_t t = current_time_hires();
printf("%lld\n", t);
rand_add_entropy(&t, sizeof(t));
// set up the socket for outgoing connections
s->local_ip = minip_get_ipaddr();
s->local_port = (rand() + 1024) & 0xffff; // TODO: allocate sanely
DEBUG_ASSERT(s->local_port <= 0xffff);
s->remote_ip = addr;
s->remote_port = port;
if (LOCAL_TRACE) {
dump_socket(s);
}
// send a SYN packet
mutex_acquire(&s->lock);
s->state = STATE_SYN_SENT;
add_socket_to_list(s);
/* set up a mss option for sending back */
tcp_mss_option_t mss_option;
mss_option.kind = 0x2;
mss_option.len = 0x4;
mss_option.mss = ntohs(s->mss);
tcp_send(s->remote_ip, s->remote_port, s->local_ip, s->local_port, NULL, 0, PKT_SYN, &mss_option, 0x4, 0, s->tx_win_low, s->rx_win_size);
// TODO: handle retransmit
mutex_release(&s->lock);
// block to wait for a successful connection
if (event_wait(&s->connect_event) == ERR_TIMED_OUT) {
return ERR_TIMED_OUT;
}
status_t err = NO_ERROR;
mutex_acquire(&s->lock);
if (s->state != STATE_ESTABLISHED) {
err = ERR_CHANNEL_CLOSED;
}
mutex_release(&s->lock);
*handle = s;
return err;
}
status_t tcp_open_listen(tcp_socket_t **handle, uint16_t port) { status_t tcp_open_listen(tcp_socket_t **handle, uint16_t port) {
tcp_socket_t *s; tcp_socket_t *s;
@@ -1150,6 +1249,7 @@ status_t tcp_close(tcp_socket_t *socket) {
// XXX set up fin retransmit timer here // XXX set up fin retransmit timer here
break; break;
case STATE_SYN_SENT:
case STATE_FIN_WAIT_1: case STATE_FIN_WAIT_1:
case STATE_FIN_WAIT_2: case STATE_FIN_WAIT_2:
case STATE_CLOSING: case STATE_CLOSING:

View File

@@ -104,7 +104,7 @@ fi
BLOCK_ARGS=" -drive if=none,file=blk.bin,id=blk,format=raw -device virtio-blk-device,drive=blk" BLOCK_ARGS=" -drive if=none,file=blk.bin,id=blk,format=raw -device virtio-blk-device,drive=blk"
NET_ARGS=" -netdev user,id=vmnic,hostname=qemu -device virtio-net-device,netdev=vmnic" NET_ARGS=" -netdev user,id=vmnic,hostname=qemu -device virtio-net-device,netdev=vmnic"
NET_TAP_ARGS=" -netdev tap,id=vmnic -device virtio-net-device,netdev=vmnic" NET_TAP_ARGS=" -netdev tap,id=vmnic,ifname=qemu0,script=no -device virtio-net-device,netdev=vmnic"
NO_DISPLAY_ARGS=" -nographic" NO_DISPLAY_ARGS=" -nographic"
DISPLAY_ARGS=" -device virtio-gpu-device -serial stdio" DISPLAY_ARGS=" -device virtio-gpu-device -serial stdio"
@@ -130,7 +130,7 @@ if (( $DO_NET )); then
fi fi
if (( $DO_NET_TAP )); then if (( $DO_NET_TAP )); then
ARGS+=$NET_TAP_ARGS ARGS+=$NET_TAP_ARGS
SUDO="sudo " #SUDO="sudo "
fi fi
if (( $DO_DISPLAY )); then if (( $DO_DISPLAY )); then
ARGS+=$DISPLAY_ARGS ARGS+=$DISPLAY_ARGS