[minip][dhcp] spiff up the state machine to handle more edge cases
-Add TAP option to scripts/qemux86
This commit is contained in:
136
lib/minip/dhcp.c
136
lib/minip/dhcp.c
@@ -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);
|
||||
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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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': {
|
||||
|
||||
@@ -78,6 +78,7 @@ enum {
|
||||
enum {
|
||||
ETH_TYPE_IPV4 = 0x0800,
|
||||
ETH_TYPE_ARP = 0x0806,
|
||||
ETH_TYPE_IPV6 = 0x88dd,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
#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
|
||||
|
||||
Reference in New Issue
Block a user