[minip][dhcp] spiff up the state machine to handle more edge cases

-Add TAP option to scripts/qemux86
This commit is contained in:
Travis Geiselbrecht
2021-02-06 14:28:46 -08:00
parent 0617d6c845
commit 35da2d1260
8 changed files with 175 additions and 57 deletions

View File

@@ -8,17 +8,17 @@
#include "minip-internal.h"
#include <lk/err.h>
#include <platform.h>
#include <stdio.h>
#include <endian.h>
#include <kernel/thread.h>
#include <lk/debug.h>
#include <lk/err.h>
#include <lk/trace.h>
#include <malloc.h>
#include <kernel/thread.h>
#include <sys/types.h>
#include <endian.h>
#include <platform.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#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);
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);

View File

@@ -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

View File

@@ -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': {

View File

@@ -78,6 +78,7 @@ enum {
enum {
ETH_TYPE_IPV4 = 0x0800,
ETH_TYPE_ARP = 0x0806,
ETH_TYPE_IPV6 = 0x88dd,
};
enum {

View File

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

View File

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

View File

@@ -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"

View File

@@ -11,6 +11,7 @@ function HELP {
echo "-d <disk image> : a virtio block device"
echo "-n : user mode networking, defaults to virtio-net-pci device"
echo "-N <network driver> : 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"
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