From 35da2d1260d408e8aa21d6433a3f800e17d073cc Mon Sep 17 00:00:00 2001 From: Travis Geiselbrecht Date: Sat, 6 Feb 2021 14:28:46 -0800 Subject: [PATCH] [minip][dhcp] spiff up the state machine to handle more edge cases -Add TAP option to scripts/qemux86 --- lib/minip/dhcp.c | 136 +++++++++++++++++++++++----------- lib/minip/include/lib/minip.h | 12 ++- lib/minip/lk_console.c | 7 +- lib/minip/minip-internal.h | 1 + lib/minip/minip.c | 50 +++++++++++-- lib/minip/udp.c | 2 + scripts/do-qemuarm | 2 +- scripts/do-qemux86 | 22 ++++-- 8 files changed, 175 insertions(+), 57 deletions(-) diff --git a/lib/minip/dhcp.c b/lib/minip/dhcp.c index 23e0d2c1..e4d63bc8 100644 --- a/lib/minip/dhcp.c +++ b/lib/minip/dhcp.c @@ -8,17 +8,17 @@ #include "minip-internal.h" -#include -#include -#include +#include +#include #include +#include #include #include - -#include -#include -#include +#include +#include +#include #include +#include #define TRACE_DHCP 0 @@ -70,17 +70,18 @@ udp_socket_t *dhcp_udp_handle; #define HW_ETHERNET 1 -static void printip(const char *name, u32 x) { - union { - u32 u; - u8 b[4]; - } ip; - ip.u = x; - printf("%s %d.%d.%d.%d\n", name, ip.b[0], ip.b[1], ip.b[2], ip.b[3]); -} +static struct dhcp_state { + volatile bool configured; + uint32_t xid; + enum { + INITIAL = 0, + DISCOVER_SENT, + RECV_OFFER, + REQUEST_SENT, + CONFIGURED, + } state; +} dhcp; -static volatile int configured = 0; -static int cfgstate = 0; static void dhcp_discover(u32 xid) { struct { @@ -111,6 +112,10 @@ static void dhcp_discover(u32 xid) { *opt++ = OPT_DONE; +#if TRACE_DHCP + printf("sending dhcp discover\n"); +#endif + dhcp.state = DISCOVER_SENT; status_t ret = udp_send(&s.msg, sizeof(dhcp_msg_t) + (opt - s.opt), dhcp_udp_handle); if (ret != NO_ERROR) { printf("DHCP_DISCOVER failed: %d\n", ret); @@ -156,6 +161,10 @@ static void dhcp_request(u32 xid, u32 server, u32 reqip) { *opt++ = OPT_DONE; +#if TRACE_DHCP + printf("sending dhcp request\n"); +#endif + dhcp.state = REQUEST_SENT; status_t ret = udp_send(&s.msg, sizeof(dhcp_msg_t) + (opt - s.opt), dhcp_udp_handle); if (ret != NO_ERROR) { printf("DHCP_REQUEST failed: %d\n", ret); @@ -171,6 +180,15 @@ static void dhcp_cb(void *data, size_t sz, uint32_t srcip, uint16_t srcport, voi u32 server = 0; int op = -1; + // lossy testing for state machine transitions + if (false) { + int r = rand(); + if (r % 3) { + printf("dropping packet for testing r %#x\n", r); + return; + } + } + if (sz < sizeof(dhcp_msg_t)) return; uint8_t mac[6]; @@ -178,22 +196,24 @@ static void dhcp_cb(void *data, size_t sz, uint32_t srcip, uint16_t srcport, voi if (memcmp(msg->chaddr, mac, 6)) return; #if TRACE_DHCP - printf("dhcp op=%d len=%zu from p=%d ip=", msg->opcode, sz, srcport); - printip("", srcip); + printf("received DHCP op %d, len %zu, from p %d, ip=", msg->opcode, sz, srcport); + printip(srcip); + printf("\n"); #endif - if (configured) { + if (dhcp.configured) { printf("already configured\n"); return; } #if TRACE_DHCP - printip("ciaddr", msg->ciaddr); - printip("yiaddr", msg->yiaddr); - printip("siaddr", msg->siaddr); - printip("giaddr", msg->giaddr); - printf("chaddr %02x:%02x:%02x:%02x:%02x:%02x\n", + printip_named("\tciaddr", msg->ciaddr); + printip_named(" yiaddr", msg->yiaddr); + printip_named(" siaddr", msg->siaddr); + printip_named(" giaddr", msg->giaddr); + printf(" chaddr %02x:%02x:%02x:%02x:%02x:%02x\n", msg->chaddr[0], msg->chaddr[1], msg->chaddr[2], msg->chaddr[3], msg->chaddr[4], msg->chaddr[5]); + printf("\toptions: "); #endif sz -= sizeof(dhcp_msg_t); opt = msg->options; @@ -229,35 +249,64 @@ static void dhcp_cb(void *data, size_t sz, uint32_t srcip, uint16_t srcport, voi } done: #if TRACE_DHCP + printf("\n\t"); + if (server) printip_named("server", server); + if (netmask) printip_named(" netmask", netmask); + if (gateway) printip_named(" gateway", gateway); + if (dns) printip_named(" dns", dns); printf("\n"); - if (server) printip("server", server); - if (netmask) printip("netmask", netmask); - if (gateway) printip("gateway", gateway); - if (dns) printip("dns", dns); #endif - if (cfgstate == 0) { + if (dhcp.state == DISCOVER_SENT) { if (op == OP_DHCPOFFER) { - printip("dhcp: offer:", msg->yiaddr); +#if TRACE_DHCP + printip_named("dhcp: offer:", msg->yiaddr); + printf("\n"); +#endif if (server) { - dhcp_request(0xaabbccdd, server, msg->yiaddr); - cfgstate = 1; + dhcp.state = RECV_OFFER; + dhcp_request(dhcp.xid, server, msg->yiaddr); } } - } else if (cfgstate == 1) { + } else if (dhcp.state == REQUEST_SENT) { if (op == OP_DHCPACK) { - printip("dhcp: ack:", msg->yiaddr); +#if TRACE_DHCP + printip_named("dhcp: ack:", msg->yiaddr); + printf("\n"); +#endif + printf("DHCP configured\n"); minip_set_ipaddr(msg->yiaddr); - configured = 1; + if (netmask) { + minip_set_netmask(netmask); + } + if (gateway) { + minip_set_gateway(gateway); + } + dhcp.state = CONFIGURED; + dhcp.configured = true; } } } static int dhcp_thread(void *arg) { - for (;;) { - if (configured) break; - thread_sleep(500); - if (configured) break; - dhcp_discover(0xaabbccdd); + // roll a random xid + dhcp.xid = rand(); + + while (!dhcp.configured) { + switch (dhcp.state) { + case INITIAL: + case DISCOVER_SENT: + // for these two states, start off by sending a discover packet + dhcp_discover(dhcp.xid); + break; + case REQUEST_SENT: + // if we're still in this state after some period of time, + // switch back to the INITIAL state and start over. + dhcp.state = INITIAL; + break; + default: + ; + } + thread_sleep(1000); } return 0; } @@ -268,7 +317,10 @@ void minip_init_dhcp(tx_func_t tx_func, void *tx_arg) { minip_init(tx_func, tx_arg, IPV4_NONE, IPV4_NONE, IPV4_NONE); int ret = udp_open(IPV4_BCAST, DHCP_CLIENT_PORT, DHCP_SERVER_PORT, &dhcp_udp_handle); - printf("dhcp opened udp: %d\n", ret); + if (ret != NO_ERROR) { + printf("DHCP: error opening udp socket\n"); + return; + } udp_listen(DHCP_CLIENT_PORT, dhcp_cb, NULL); diff --git a/lib/minip/include/lib/minip.h b/lib/minip/include/lib/minip.h index 3d3e7c27..5ae8b74f 100644 --- a/lib/minip/include/lib/minip.h +++ b/lib/minip/include/lib/minip.h @@ -49,7 +49,13 @@ void minip_set_ipaddr(const uint32_t addr); void minip_set_hostname(const char *name); const char *minip_get_hostname(void); -uint32_t minip_parse_ipaddr(const char *addr, size_t len); +uint32_t minip_get_broadcast(void); + +uint32_t minip_get_netmask(void); +void minip_set_netmask(const uint32_t netmask); + +uint32_t minip_get_gateway(void); +void minip_set_gateway(const uint32_t gateway); /* udp */ typedef struct udp_socket udp_socket_t; @@ -76,4 +82,8 @@ static inline status_t tcp_accept(tcp_socket_t *listen_socket, tcp_socket_t **ac /* utilities */ void gen_random_mac_address(uint8_t *mac_addr); +uint32_t minip_parse_ipaddr(const char *addr, size_t len); +void printip(uint32_t x); +void printip_named(const char *s, u32 x); + __END_CDECLS diff --git a/lib/minip/lk_console.c b/lib/minip/lk_console.c index d157a7d3..93349151 100644 --- a/lib/minip/lk_console.c +++ b/lib/minip/lk_console.c @@ -76,10 +76,11 @@ minip_usage: break; case 's': { - uint32_t ipaddr = minip_get_ipaddr(); - printf("hostname: %s\n", minip_get_hostname()); - printf("ip: %u.%u.%u.%u\n", IPV4_SPLIT(ipaddr)); + printf("ip: %u.%u.%u.%u\n", IPV4_SPLIT(minip_get_ipaddr())); + printf("netmask: %u.%u.%u.%u\n", IPV4_SPLIT(minip_get_netmask())); + printf("broadcast: %u.%u.%u.%u\n", IPV4_SPLIT(minip_get_broadcast())); + printf("gateway: %u.%u.%u.%u\n", IPV4_SPLIT(minip_get_gateway())); } break; case 't': { diff --git a/lib/minip/minip-internal.h b/lib/minip/minip-internal.h index afa1c4a8..64a78bf7 100644 --- a/lib/minip/minip-internal.h +++ b/lib/minip/minip-internal.h @@ -78,6 +78,7 @@ enum { enum { ETH_TYPE_IPV4 = 0x0800, ETH_TYPE_ARP = 0x0806, + ETH_TYPE_IPV6 = 0x88dd, }; enum { diff --git a/lib/minip/minip.c b/lib/minip/minip.c index e608dad4..0c74fb23 100644 --- a/lib/minip/minip.c +++ b/lib/minip/minip.c @@ -70,6 +70,27 @@ void minip_set_ipaddr(const uint32_t addr) { compute_broadcast_address(); } +uint32_t minip_get_broadcast(void) { + return minip_broadcast; +} + +uint32_t minip_get_netmask(void) { + return minip_netmask; +} + +void minip_set_netmask(const uint32_t netmask) { + minip_netmask = netmask; + compute_broadcast_address(); +} + +uint32_t minip_get_gateway(void) { + return minip_gateway; +} + +void minip_set_gateway(const uint32_t gateway) { + minip_gateway = gateway; +} + void gen_random_mac_address(uint8_t *mac_addr) { for (size_t i = 0; i < 6; i++) { mac_addr[i] = rand() & 0xff; @@ -202,6 +223,10 @@ status_t minip_ipv4_send(pktbuf_t *p, uint32_t dest_addr, uint8_t proto) { } ready: + if (LOCAL_TRACE) { + printf("sending ipv4\n"); + } + minip_build_mac_hdr(eth, dst_mac, ETH_TYPE_IPV4); minip_build_ipv4_hdr(ip, dest_addr, proto, data_len); @@ -397,11 +422,6 @@ __NO_INLINE static int handle_arp_pkt(pktbuf_t *p) { return 0; } -static void dump_mac_address(const uint8_t *mac) { - printf("%02x:%02x:%02x:%02x:%02x:%02x", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); -} - static void dump_eth_packet(const struct eth_hdr *eth) { printf("ETH src "); dump_mac_address(eth->src_mac); @@ -440,6 +460,11 @@ void minip_rx_driver_callback(pktbuf_t *p) { } } +void dump_mac_address(const uint8_t *mac) { + printf("%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + uint32_t minip_parse_ipaddr(const char *ipaddr_str, size_t len) { uint8_t ip[4] = { 0, 0, 0, 0 }; uint8_t pos = 0, i = 0; @@ -459,3 +484,18 @@ uint32_t minip_parse_ipaddr(const char *ipaddr_str, size_t len) { return IPV4_PACK(ip); } + +// printf the ip address passed in +void printip(uint32_t x) { + union { + u32 u; + u8 b[4]; + } ip; + ip.u = x; + printf("%d.%d.%d.%d", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); +} + +void printip_named(const char *s, uint32_t x) { + printf("%s ", s); + printip(x); +} diff --git a/lib/minip/udp.c b/lib/minip/udp.c index 737d9094..cd51f4a2 100644 --- a/lib/minip/udp.c +++ b/lib/minip/udp.c @@ -148,6 +148,8 @@ status_t udp_send_iovec(const iovec_t *iov, uint iov_count, udp_socket_t *handle udp->chksum = rfc768_chksum(ip, udp); #endif + LTRACEF("packet paylod len %zu\n", len); + minip_tx_handler(p); return ret; diff --git a/scripts/do-qemuarm b/scripts/do-qemuarm index 74d52561..febc1ba5 100755 --- a/scripts/do-qemuarm +++ b/scripts/do-qemuarm @@ -77,7 +77,7 @@ fi ARGS=" -cpu $CPU -m $MEMSIZE -smp $SMP -machine $MACHINE -kernel build-${PROJECT}/lk.elf" 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_NET_ARGS=" -net none" DISPLAY_ARGS=" -device virtio-gpu-device -serial stdio" NO_DISPLAY_ARGS=" -nographic" diff --git a/scripts/do-qemux86 b/scripts/do-qemux86 index a0091171..fdf17e85 100755 --- a/scripts/do-qemux86 +++ b/scripts/do-qemux86 @@ -11,6 +11,7 @@ function HELP { echo "-d : a virtio block device" echo "-n : user mode networking, defaults to virtio-net-pci device" echo "-N : specify network driver (virtio-net-pci, e1000e, etc)" + echo "-t : use tap interface qemu0 for networking" echo "-g : with graphics" echo "-k : use KVM" echo "-h for help" @@ -25,13 +26,14 @@ DO_KVM=0 DO_DISK=0 DISK_IMAGE="" DO_NET=0 +DO_NET_TAP=0 NETDEV=e1000e MEMSIZE=0 SMP=1 SUDO="" MAKE_VARS="" -while getopts 6d:gklm:nN:s:h FLAG; do +while getopts 6d:gklm:nN:s:th FLAG; do case $FLAG in 6) DO_64BIT=1;; d) DO_DISK=1; DISK_IMAGE=$OPTARG;; @@ -40,6 +42,7 @@ while getopts 6d:gklm:nN:s:h FLAG; do l) DO_LEGACY=1;; m) MEMSIZE=$OPTARG;; n) DO_NET=1;; + t) DO_NET_TAP=1;; N) NETDEV=$OPTARG;; s) SMP=$OPTARG;; h) HELP;; @@ -107,10 +110,19 @@ if (( $DO_DISK )); then fi if (( $DO_NET )); then if (( ! $DO_LEGACY )); then - ARGS+=" -netdev user,id=vmnic,hostname=qemu -device $NETDEV,netdev=vmnic" - #ARGS+=" -netdev tap,id=vmnic,ifname=qemu.tap0,script=no,downscript=no" - #ARGS+=" -device e1000e,netdev=vmnic" - #SUDO="sudo" + if (( ! $DO_NET_TAP )); then + ARGS+=" -netdev user,id=vmnic,hostname=qemu" + # quick note to enable tap interface + # IFNAME=qemu + # BRIDGE=br0 + # sudo tunctl -u $(whoami) -t ${IFNAME} + # sudo ifconfig ${IFNAME} up + # sudo ip link set ${IFNAME} master ${BRIDGE} + else + ARGS+=" -netdev tap,id=vmnic,ifname=qemu0,script=no,downscript=no" + #SUDO="sudo" + fi + ARGS+=" -device $NETDEV,netdev=vmnic" else echo "implement legacy + network config" exit 1