diff --git a/lib/minip/arp.c b/lib/minip/arp.c index f4807aa6..1b2aa2fe 100644 --- a/lib/minip/arp.c +++ b/lib/minip/arp.c @@ -16,26 +16,19 @@ #include #include -typedef union { - uint32_t u; - uint8_t b[4]; -} ipv4_t; - #define LOCAL_TRACE 0 -static struct list_node arp_list; typedef struct { struct list_node node; uint32_t addr; uint8_t mac[6]; } arp_entry_t; +static struct list_node arp_list = LIST_INITIAL_VALUE(arp_list); static mutex_t arp_mutex = MUTEX_INITIAL_VALUE(arp_mutex); -void arp_cache_init(void) { - list_initialize(&arp_list); -} +void arp_cache_init(void) {} -static inline void mru_update(struct list_node *entry) { +static void mru_update(struct list_node *entry) { if (arp_list.next == entry) return; @@ -77,7 +70,7 @@ void arp_cache_update(uint32_t addr, const uint8_t mac[6]) { } arp->addr = addr; - memcpy(arp->mac, mac, sizeof(arp->mac)); + mac_addr_copy(arp->mac, mac); list_add_head(&arp_list, &arp->node); } diff --git a/lib/minip/dhcp.cpp b/lib/minip/dhcp.cpp index 0be8881e..64a87a51 100644 --- a/lib/minip/dhcp.cpp +++ b/lib/minip/dhcp.cpp @@ -253,6 +253,9 @@ void dhcp::udp_callback(void *data, size_t sz, uint32_t srcip, uint16_t srcport) #endif sz -= sizeof(dhcp_msg_t); opt = msg->options; +#if TRACE_DHCP + printf("\toptions: "); +#endif while (sz >= 2) { sz -= 2; if (opt[1] > sz) { diff --git a/lib/minip/include/lib/minip.h b/lib/minip/include/lib/minip.h index 5ae8b74f..bfee41cc 100644 --- a/lib/minip/include/lib/minip.h +++ b/lib/minip/include/lib/minip.h @@ -8,15 +8,14 @@ */ #pragma once -#include #include +#include +#include +#include #include #include -#include #include -#include - __BEGIN_CDECLS #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); 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); const char *minip_get_hostname(void); @@ -69,6 +73,7 @@ status_t udp_close(udp_socket_t *handle); /* tcp */ 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_accept_timeout(tcp_socket_t *listen_socket, tcp_socket_t **accept_socket, lk_time_t timeout); status_t tcp_close(tcp_socket_t *socket); diff --git a/lib/minip/minip-internal.h b/lib/minip/minip-internal.h index 64a78bf7..f5c22c3b 100644 --- a/lib/minip/minip-internal.h +++ b/lib/minip/minip-internal.h @@ -89,7 +89,13 @@ enum { extern tx_func_t minip_tx_handler; typedef struct udp_hdr udp_hdr_t; 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_update(uint32_t addr, const uint8_t mac[6]); 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 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_ipv4_hdr(struct ipv4_hdr *ipv4, uint32_t dst, uint8_t proto, uint16_t len); diff --git a/lib/minip/minip.c b/lib/minip/minip.c index 0c74fb23..87d1b810 100644 --- a/lib/minip/minip.c +++ b/lib/minip/minip.c @@ -22,8 +22,6 @@ #include #include -static struct list_node arp_list = LIST_INITIAL_VALUE(arp_list); - // TODO // 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_arg = tx_arg; - minip_ip = ip; - minip_netmask = mask; - minip_gateway = gateway; - compute_broadcast_address(); + minip_set_ipaddr(ip); + minip_set_netmask(mask); + minip_set_gateway(gateway); arp_cache_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 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) { dst_mac = bcast_mac; 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) { pktbuf_free(p, true); ret = -EHOSTUNREACH; diff --git a/lib/minip/tcp.c b/lib/minip/tcp.c index e05b08ea..c811f55c 100644 --- a/lib/minip/tcp.c +++ b/lib/minip/tcp.c @@ -25,8 +25,6 @@ #define LOCAL_TRACE 0 -typedef uint32_t ipv4_addr; - typedef struct tcp_header { uint16_t source_port; uint16_t dest_port; @@ -114,6 +112,9 @@ typedef struct tcp_socket { struct tcp_socket *accepted; net_timer_t time_wait_timer; + + /* connect waiting */ + event_t connect_event; } tcp_socket_t; #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) { - 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); @@ -286,7 +287,7 @@ static void inc_socket_ref(tcp_socket_t *s) { DEBUG_ASSERT(s); __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); } @@ -294,12 +295,13 @@ static bool dec_socket_ref(tcp_socket_t *s) { DEBUG_ASSERT(s); 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) { LTRACEF("destroying socket\n"); event_destroy(&s->tx_event); event_destroy(&s->rx_event); + event_destroy(&s->connect_event); free(s->rx_buffer_raw); free(s->tx_buffer); @@ -482,6 +484,44 @@ void tcp_input(pktbuf_t *p, uint32_t src_ip, uint32_t dst_ip) { 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: if (packet_flags & PKT_ACK) { /* they're acking us */ @@ -565,9 +605,6 @@ fin_wait_2: case STATE_TIME_WAIT: /* /dev/null of packets */ break; - - case STATE_SYN_SENT: - PANIC_UNIMPLEMENTED; } done: @@ -887,6 +924,7 @@ static void tcp_wakeup_waiters(tcp_socket_t *s) { // wake up any waiters event_signal(&s->rx_event, true); event_signal(&s->tx_event, true); + event_signal(&s->connect_event, true); } 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); + event_init(&s->connect_event, false, 0); return s; } /* 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) { tcp_socket_t *s; @@ -1150,6 +1249,7 @@ status_t tcp_close(tcp_socket_t *socket) { // XXX set up fin retransmit timer here break; + case STATE_SYN_SENT: case STATE_FIN_WAIT_1: case STATE_FIN_WAIT_2: case STATE_CLOSING: diff --git a/scripts/do-qemuriscv b/scripts/do-qemuriscv index 2a04ce5c..21b4dcb2 100755 --- a/scripts/do-qemuriscv +++ b/scripts/do-qemuriscv @@ -104,7 +104,7 @@ fi 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_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" DISPLAY_ARGS=" -device virtio-gpu-device -serial stdio" @@ -130,7 +130,7 @@ if (( $DO_NET )); then fi if (( $DO_NET_TAP )); then ARGS+=$NET_TAP_ARGS - SUDO="sudo " + #SUDO="sudo " fi if (( $DO_DISPLAY )); then ARGS+=$DISPLAY_ARGS