Hi,
the problem got reported a few times and a similar diff was floating
around: vmd's "local interface" implements a simple auto-magic BOOTP
server, but udhcpc doesn't support BOOTP, which is the predecessor and
a valid subset of DHCP. udhcpc is used by busybox and many embedded
Linux distributions, maybe even by Android.
The following diff adds minimal DHCP support which works with udhcpc.
OK?
Reyk
Index: usr.sbin/vmd/dhcp.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/dhcp.c,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 dhcp.c
--- usr.sbin/vmd/dhcp.c 24 Apr 2017 07:14:27 -0000 1.3
+++ usr.sbin/vmd/dhcp.c 2 Nov 2017 21:15:03 -0000
@@ -22,6 +22,7 @@
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
+#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
@@ -38,12 +39,13 @@ extern struct vmd *env;
ssize_t
dhcp_request(struct vionet_dev *dev, char *buf, size_t buflen, char **obuf)
{
- unsigned char *respbuf = NULL;
+ unsigned char *respbuf = NULL, *op, *oe, dhcptype = 0;
ssize_t offset, respbuflen = 0;
struct packet_ctx pc;
struct dhcp_packet req, resp;
- struct in_addr in, mask;
+ struct in_addr server_addr, mask, client_addr, requested_addr;
size_t resplen, o;
+ uint32_t ltime;
if (buflen < (ssize_t)(BOOTP_MIN_LEN + sizeof(struct ether_header)))
return (-1);
@@ -76,24 +78,54 @@ dhcp_request(struct vionet_dev *dev, cha
if (req.ciaddr.s_addr != 0 || req.file[0] != '\0' || req.hops != 0)
return (-1);
+ /* Get a few DHCP options (best effort as we fall back to BOOTP) */
+ if (memcmp(&req.options,
+ DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN) == 0) {
+ memset(&requested_addr, 0, sizeof(requested_addr));
+ op = req.options + DHCP_OPTIONS_COOKIE_LEN;
+ oe = req.options + sizeof(req.options);
+ while (*op != DHO_END && op < oe) {
+ if (op[0] == DHO_PAD) {
+ op++;
+ continue;
+ }
+ if (op + 1 + op[1] >= oe)
+ break;
+ if (op[0] == DHO_DHCP_MESSAGE_TYPE &&
+ op[1] == 1)
+ dhcptype = op[2];
+ else if (op[0] == DHO_DHCP_REQUESTED_ADDRESS &&
+ op[1] == sizeof(requested_addr))
+ memcpy(&requested_addr, &op[2],
+ sizeof(requested_addr));
+ op += 2 + op[1];
+ }
+ }
+
memset(&resp, 0, sizeof(resp));
resp.op = BOOTREPLY;
resp.htype = req.htype;
resp.hlen = req.hlen;
resp.xid = req.xid;
- if ((in.s_addr = vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
+ if ((client_addr.s_addr =
+ vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
dev->vm_vmid, dev->idx, 1)) == 0)
return (-1);
- memcpy(&resp.yiaddr, &in, sizeof(in));
- memcpy(&ss2sin(&pc.pc_dst)->sin_addr, &in, sizeof(in));
+ memcpy(&resp.yiaddr, &client_addr,
+ sizeof(client_addr));
+ memcpy(&ss2sin(&pc.pc_dst)->sin_addr, &client_addr,
+ sizeof(client_addr));
ss2sin(&pc.pc_dst)->sin_port = htons(CLIENT_PORT);
- if ((in.s_addr = vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
+ if ((server_addr.s_addr =
+ vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
dev->vm_vmid, dev->idx, 0)) == 0)
return (-1);
- memcpy(&resp.siaddr, &in, sizeof(in));
- memcpy(&ss2sin(&pc.pc_src)->sin_addr, &in, sizeof(in));
+ memcpy(&resp.siaddr, &server_addr,
+ sizeof(server_addr));
+ memcpy(&ss2sin(&pc.pc_src)->sin_addr, &server_addr,
+ sizeof(server_addr));
ss2sin(&pc.pc_src)->sin_port = htons(SERVER_PORT);
/* Packet is already allocated */
@@ -124,6 +156,44 @@ dhcp_request(struct vionet_dev *dev, cha
DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN);
o+= DHCP_OPTIONS_COOKIE_LEN;
+ /* Did we receive a DHCP request or was it just BOOTP? */
+ if (dhcptype) {
+ /*
+ * There is no need for a real state machine as we always
+ * answer with the same client IP and options for the VM.
+ */
+ if (dhcptype == DHCPDISCOVER)
+ dhcptype = DHCPOFFER;
+ else if (dhcptype == DHCPREQUEST &&
+ (requested_addr.s_addr == 0 ||
+ client_addr.s_addr == requested_addr.s_addr))
+ dhcptype = DHCPACK;
+ else
+ dhcptype = DHCPNAK;
+
+ resp.options[o++] = DHO_DHCP_MESSAGE_TYPE;
+ resp.options[o++] = sizeof(dhcptype);
+ memcpy(&resp.options[o], &dhcptype, sizeof(dhcptype));
+ o += sizeof(dhcptype);
+
+ /* Our lease never changes, use the maximum lease time */
+ resp.options[o++] = DHO_DHCP_LEASE_TIME;
+ resp.options[o++] = sizeof(ltime);
+ ltime = ntohl(0xffffffff);
+ memcpy(&resp.options[o], <ime, sizeof(ltime));
+ o += sizeof(ltime);
+
+ resp.options[o++] = DHO_DHCP_SERVER_IDENTIFIER;
+ resp.options[o++] = sizeof(server_addr);
+ memcpy(&resp.options[o], &server_addr, sizeof(server_addr));
+ o += sizeof(server_addr);
+ }
+
+ resp.options[o++] = DHO_DOMAIN_NAME_SERVERS;
+ resp.options[o++] = sizeof(server_addr);
+ memcpy(&resp.options[o], &server_addr, sizeof(server_addr));
+ o += sizeof(server_addr);
+
resp.options[o++] = DHO_SUBNET_MASK;
resp.options[o++] = sizeof(mask);
mask.s_addr = htonl(0xfffffffe);
@@ -131,14 +201,14 @@ dhcp_request(struct vionet_dev *dev, cha
o += sizeof(mask);
resp.options[o++] = DHO_ROUTERS;
- resp.options[o++] = sizeof(in);
- memcpy(&resp.options[o], &in, sizeof(in));
- o += sizeof(in);
+ resp.options[o++] = sizeof(server_addr);
+ memcpy(&resp.options[o], &server_addr, sizeof(server_addr));
+ o += sizeof(server_addr);
resp.options[o++] = DHO_DOMAIN_NAME_SERVERS;
- resp.options[o++] = sizeof(in);
- memcpy(&resp.options[o], &in, sizeof(in));
- o += sizeof(in);
+ resp.options[o++] = sizeof(server_addr);
+ memcpy(&resp.options[o], &server_addr, sizeof(server_addr));
+ o += sizeof(server_addr);
resp.options[o++] = DHO_END;
@@ -163,4 +233,3 @@ dhcp_request(struct vionet_dev *dev, cha
free(respbuf);
return (0);
}
-
Index: usr.sbin/vmd/vm.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vm.conf.5,v
retrieving revision 1.23
diff -u -p -u -p -r1.23 vm.conf.5
--- usr.sbin/vmd/vm.conf.5 30 Oct 2017 03:37:33 -0000 1.23
+++ usr.sbin/vmd/vm.conf.5 2 Nov 2017 21:15:03 -0000
@@ -185,7 +185,7 @@ A
.Cm local
interface will auto-generate an IPv4 subnet for the interface,
configure a gateway address on the VM host side,
-and run a simple DHCP (BOOTP) server for the VM.
+and run a simple DHCP/BOOTP server for the VM.
This option can be used for layer 3 mode without configuring a switch.
.It Cm interfaces Ar count
Optional minimum number of network interfaces to add to the VM.
Index: usr.sbin/vmctl/vmctl.8
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/vmctl.8,v
retrieving revision 1.34
diff -u -p -u -p -r1.34 vmctl.8
--- usr.sbin/vmctl/vmctl.8 5 Sep 2017 22:06:49 -0000 1.34
+++ usr.sbin/vmctl/vmctl.8 2 Nov 2017 21:15:03 -0000
@@ -106,7 +106,7 @@ Add a local network interface.
.Xr vmd 8
will auto-generate an IPv4 subnet for the interface,
configure a gateway address on the VM host side,
-and run a simple DHCP (BOOTP) server for the VM.
+and run a simple DHCP/BOOTP server for the VM.
See
.Sx LOCAL INTERFACES
below for more information on how addresses are calculated and assigned when