diff options
-rw-r--r-- | usr.sbin/vmctl/vmctl.8 | 6 | ||||
-rw-r--r-- | usr.sbin/vmd/dhcp.c | 101 | ||||
-rw-r--r-- | usr.sbin/vmd/vm.conf.5 | 6 |
3 files changed, 91 insertions, 22 deletions
diff --git a/usr.sbin/vmctl/vmctl.8 b/usr.sbin/vmctl/vmctl.8 index b66eddeae84..2efcf035531 100644 --- a/usr.sbin/vmctl/vmctl.8 +++ b/usr.sbin/vmctl/vmctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: vmctl.8,v 1.34 2017/09/05 22:06:49 edd Exp $ +.\" $OpenBSD: vmctl.8,v 1.35 2017/11/05 20:01:09 reyk Exp $ .\" .\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 5 2017 $ +.Dd $Mdocdate: November 5 2017 $ .Dt VMCTL 8 .Os .Sh NAME @@ -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 diff --git a/usr.sbin/vmd/dhcp.c b/usr.sbin/vmd/dhcp.c index 3fd0f727b07..5b1df2e6bc2 100644 --- a/usr.sbin/vmd/dhcp.c +++ b/usr.sbin/vmd/dhcp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dhcp.c,v 1.3 2017/04/24 07:14:27 reyk Exp $ */ +/* $OpenBSD: dhcp.c,v 1.4 2017/11/05 20:01:09 reyk Exp $ */ /* * Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org> @@ -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, char *buf, size_t buflen, char **obuf) 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, char *buf, size_t buflen, char **obuf) 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, char *buf, size_t buflen, char **obuf) 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, char *buf, size_t buflen, char **obuf) free(respbuf); return (0); } - diff --git a/usr.sbin/vmd/vm.conf.5 b/usr.sbin/vmd/vm.conf.5 index 6ce21896657..89d25b3faf5 100644 --- a/usr.sbin/vmd/vm.conf.5 +++ b/usr.sbin/vmd/vm.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: vm.conf.5,v 1.23 2017/10/30 03:37:33 mlarkin Exp $ +.\" $OpenBSD: vm.conf.5,v 1.24 2017/11/05 20:01:09 reyk Exp $ .\" .\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> .\" Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: October 30 2017 $ +.Dd $Mdocdate: November 5 2017 $ .Dt VM.CONF 5 .Os .Sh NAME @@ -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. |