diff options
-rw-r--r-- | usr.sbin/vmctl/main.c | 10 | ||||
-rw-r--r-- | usr.sbin/vmctl/vmctl.8 | 12 | ||||
-rw-r--r-- | usr.sbin/vmctl/vmctl.c | 12 | ||||
-rw-r--r-- | usr.sbin/vmd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/vmd/dhcp.c | 163 | ||||
-rw-r--r-- | usr.sbin/vmd/dhcp.h | 181 | ||||
-rw-r--r-- | usr.sbin/vmd/packet.c | 332 | ||||
-rw-r--r-- | usr.sbin/vmd/parse.y | 34 | ||||
-rw-r--r-- | usr.sbin/vmd/priv.c | 90 | ||||
-rw-r--r-- | usr.sbin/vmd/virtio.c | 37 | ||||
-rw-r--r-- | usr.sbin/vmd/virtio.h | 12 | ||||
-rw-r--r-- | usr.sbin/vmd/vm.c | 4 | ||||
-rw-r--r-- | usr.sbin/vmd/vm.conf.5 | 14 | ||||
-rw-r--r-- | usr.sbin/vmd/vmd.c | 14 | ||||
-rw-r--r-- | usr.sbin/vmd/vmd.h | 47 |
15 files changed, 923 insertions, 43 deletions
diff --git a/usr.sbin/vmctl/main.c b/usr.sbin/vmctl/main.c index da27fd6c913..20ced9884e7 100644 --- a/usr.sbin/vmctl/main.c +++ b/usr.sbin/vmctl/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.25 2017/04/06 18:07:13 reyk Exp $ */ +/* $OpenBSD: main.c,v 1.26 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org> @@ -65,7 +65,7 @@ struct ctl_command ctl_commands[] = { { "reload", CMD_RELOAD, ctl_reload, "" }, { "reset", CMD_RESET, ctl_reset, "[all|vms|switches]" }, { "start", CMD_START, ctl_start, "\"name\"" - " [-c] [-b image] [-m size]\n" + " [-Lc] [-b image] [-m size]\n" "\t\t[-n switch] [-i count] [-d disk]*" }, { "status", CMD_STATUS, ctl_status, "[id]" }, { "stop", CMD_STOP, ctl_stop, "id" }, @@ -539,7 +539,7 @@ ctl_start(struct parse_result *res, int argc, char *argv[]) argc--; argv++; - while ((ch = getopt(argc, argv, "b:cm:n:d:i:")) != -1) { + while ((ch = getopt(argc, argv, "b:cLm:n:d:i:")) != -1) { switch (ch) { case 'b': if (res->path) @@ -552,6 +552,10 @@ ctl_start(struct parse_result *res, int argc, char *argv[]) case 'c': tty_autoconnect = 1; break; + case 'L': + if (parse_network(res, ".") != 0) + errx(1, "invalid network: %s", optarg); + break; case 'm': if (res->size) errx(1, "memory specified multiple times"); diff --git a/usr.sbin/vmctl/vmctl.8 b/usr.sbin/vmctl/vmctl.8 index aed2816f15f..71a0aa4e7f6 100644 --- a/usr.sbin/vmctl/vmctl.8 +++ b/usr.sbin/vmctl/vmctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: vmctl.8,v 1.28 2017/04/14 00:53:28 mlarkin Exp $ +.\" $OpenBSD: vmctl.8,v 1.29 2017/04/19 15:38:32 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: April 14 2017 $ +.Dd $Mdocdate: April 19 2017 $ .Dt VMCTL 8 .Os .Sh NAME @@ -72,8 +72,8 @@ Reset the configured switches. .It Cm reset vms Reset and terminate all VMs. .It Xo Cm start Ar name +.Op Fl Lc .Op Fl b Ar path -.Op Fl c .Op Fl d Ar path .Op Fl i Ar count .Op Fl m Ar size @@ -91,6 +91,12 @@ Automatically connect to the VM console. Disk image file (may be specified multiple times to add multiple disk images). .It Fl i Ar count Number of network interfaces to add to the VM. +.It Fl L +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. .It Fl m Ar size Memory .Ar size diff --git a/usr.sbin/vmctl/vmctl.c b/usr.sbin/vmctl/vmctl.c index 39d1615d8f2..ee9c6db0e9d 100644 --- a/usr.sbin/vmctl/vmctl.c +++ b/usr.sbin/vmctl/vmctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmctl.c,v 1.29 2017/04/06 18:07:13 reyk Exp $ */ +/* $OpenBSD: vmctl.c,v 1.30 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org> @@ -123,8 +123,16 @@ vm_start(uint32_t start_id, const char *name, int memsize, int nnics, for (i = 0 ; i < ndisks; i++) strlcpy(vcp->vcp_disks[i], disks[i], VMM_MAX_PATH_DISK); for (i = 0 ; i < nnics; i++) { - strlcpy(vmc->vmc_ifswitch[i], nics[i], IF_NAMESIZE); vmc->vmc_ifflags[i] = VMIFF_UP; + + if (strcmp(".", nics[i]) == 0) { + /* Add a "local" interface */ + strlcpy(vmc->vmc_ifswitch[i], "", IF_NAMESIZE); + vmc->vmc_ifflags[i] |= VMIFF_LOCAL; + } else { + /* Add a interface to a switch */ + strlcpy(vmc->vmc_ifswitch[i], nics[i], IF_NAMESIZE); + } } if (name != NULL) strlcpy(vcp->vcp_name, name, VMM_MAX_NAME_LEN); diff --git a/usr.sbin/vmd/Makefile b/usr.sbin/vmd/Makefile index 09a9c263dee..9144d92cf76 100644 --- a/usr.sbin/vmd/Makefile +++ b/usr.sbin/vmd/Makefile @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.13 2017/03/01 18:00:50 reyk Exp $ +# $OpenBSD: Makefile,v 1.14 2017/04/19 15:38:32 reyk Exp $ .if ${MACHINE} == "amd64" || ${MACHINE} == "i386" PROG= vmd SRCS= vmd.c control.c log.c priv.c proc.c config.c vmm.c SRCS+= vm.c loadfile_elf.c pci.c virtio.c i8259.c mc146818.c -SRCS+= ns8250.c i8253.c vmboot.c ufs.c disklabel.c +SRCS+= ns8250.c i8253.c vmboot.c ufs.c disklabel.c dhcp.c packet.c SRCS+= parse.y CFLAGS+= -Wall -I${.CURDIR} diff --git a/usr.sbin/vmd/dhcp.c b/usr.sbin/vmd/dhcp.c new file mode 100644 index 00000000000..b8b05000e3a --- /dev/null +++ b/usr.sbin/vmd/dhcp.c @@ -0,0 +1,163 @@ +/* $OpenBSD: dhcp.c,v 1.1 2017/04/19 15:38:32 reyk Exp $ */ + +/* + * Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <stdlib.h> +#include <string.h> +#include <stddef.h> + +#include "proc.h" +#include "vmd.h" +#include "dhcp.h" +#include "virtio.h" + +static const uint8_t broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +ssize_t +dhcp_request(struct vionet_dev *dev, char *buf, size_t buflen, char **obuf) +{ + unsigned char *respbuf = NULL; + ssize_t offset, respbuflen = 0; + struct packet_ctx pc; + struct dhcp_packet req, resp; + struct in_addr in, mask; + size_t resplen, o; + + if (buflen < (ssize_t)(BOOTP_MIN_LEN + sizeof(struct ether_header))) + return (-1); + + memset(&pc, 0, sizeof(pc)); + if ((offset = decode_hw_header(buf, buflen, 0, &pc, HTYPE_ETHER)) < 0) + return (-1); + + if (memcmp(pc.pc_smac, dev->mac, ETHER_ADDR_LEN) != 0 || + memcmp(pc.pc_dmac, broadcast, ETHER_ADDR_LEN) != 0) + return (-1); + + if ((offset = decode_udp_ip_header(buf, buflen, offset, &pc)) < 0) + return (-1); + + if (ntohs(ss2sin(&pc.pc_src)->sin_port) != CLIENT_PORT || + ntohs(ss2sin(&pc.pc_dst)->sin_port) != SERVER_PORT) + return (-1); + + memset(&req, 0, sizeof(req)); + memcpy(&req, buf + offset, buflen - offset); + + if (req.op != BOOTREQUEST || + req.htype != pc.pc_htype || + req.hlen != ETHER_ADDR_LEN || + memcmp(dev->mac, req.chaddr, req.hlen) != 0) + return (-1); + + /* Ignore unsupported requests for now */ + if (req.ciaddr.s_addr != 0 || req.file[0] != '\0' || req.hops != 0) + return (-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(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)); + ss2sin(&pc.pc_dst)->sin_port = htons(CLIENT_PORT); + + if ((in.s_addr = vm_priv_addr(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)); + ss2sin(&pc.pc_src)->sin_port = htons(SERVER_PORT); + + /* Packet is already allocated */ + if (*obuf != NULL) + goto fail; + + buflen = 0; + respbuflen = DHCP_MTU_MAX; + if ((respbuf = calloc(1, respbuflen)) == NULL) + goto fail; + + memcpy(&pc.pc_dmac, dev->mac, sizeof(pc.pc_dmac)); + memcpy(&resp.chaddr, dev->mac, resp.hlen); + memcpy(&pc.pc_smac, dev->mac, sizeof(pc.pc_smac)); + pc.pc_smac[5]++; + if ((offset = assemble_hw_header(respbuf, respbuflen, 0, + &pc, HTYPE_ETHER)) < 0) { + log_debug("%s: assemble_hw_header failed", __func__); + goto fail; + } + + /* BOOTP uses a 64byte vendor field instead of the DHCP options */ + resplen = BOOTP_MIN_LEN; + + /* Add BOOTP Vendor Extensions (DHCP options) */ + o = 0; + memcpy(&resp.options, + DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN); + o+= DHCP_OPTIONS_COOKIE_LEN; + + resp.options[o++] = DHO_SUBNET_MASK; + resp.options[o++] = sizeof(mask); + mask.s_addr = htonl(0xfffffffe); + memcpy(&resp.options[o], &mask, sizeof(mask)); + 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++] = DHO_DOMAIN_NAME_SERVERS; + resp.options[o++] = sizeof(in); + memcpy(&resp.options[o], &in, sizeof(in)); + o += sizeof(in); + + resp.options[o++] = DHO_END; + + resplen = offsetof(struct dhcp_packet, options) + o; + + /* Minimum packet size */ + if (resplen < BOOTP_MIN_LEN) + resplen = BOOTP_MIN_LEN; + + if ((offset = assemble_udp_ip_header(respbuf, respbuflen, offset, &pc, + (unsigned char *)&resp, resplen)) < 0) { + log_debug("%s: assemble_udp_ip_header failed", __func__); + goto fail; + } + + memcpy(respbuf + offset, &resp, sizeof(resp)); + respbuflen = offset + resplen; + + *obuf = respbuf; + return (respbuflen); + fail: + free(respbuf); + return (0); +} + diff --git a/usr.sbin/vmd/dhcp.h b/usr.sbin/vmd/dhcp.h new file mode 100644 index 00000000000..2dccd45a3a6 --- /dev/null +++ b/usr.sbin/vmd/dhcp.h @@ -0,0 +1,181 @@ +/* $OpenBSD: dhcp.h,v 1.1 2017/04/19 15:38:32 reyk Exp $ */ + +/* Protocol structures... */ + +/* + * Copyright (c) 1995, 1996 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#define DHCP_UDP_OVERHEAD (20 + /* IP header */ \ + 8) /* UDP header */ +#define DHCP_SNAME_LEN 64 +#define DHCP_FILE_LEN 128 +#define DHCP_FIXED_NON_UDP 236 +#define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD) + /* Everything but options. */ +#define DHCP_MTU_MAX 1500 +#define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN) +/* The option/sub-option maximum length. */ +#define DHCP_OPTION_MAXLEN 255 +/* The option/sub-option header length. */ +#define DHCP_OPTION_HDR_LEN 2 + +#define BOOTP_MIN_LEN 300 + +#define SERVER_PORT 67 +#define CLIENT_PORT 68 + +struct dhcp_packet { + u_int8_t op; /* Message opcode/type */ + u_int8_t htype; /* Hardware addr type (see net/if_types.h) */ + u_int8_t hlen; /* Hardware addr length */ + u_int8_t hops; /* Number of relay agent hops from client */ + u_int32_t xid; /* Transaction ID */ + u_int16_t secs; /* Seconds since client started looking */ + u_int16_t flags; /* Flag bits */ + struct in_addr ciaddr; /* Client IP address (if already in use) */ + struct in_addr yiaddr; /* Client IP address */ + struct in_addr siaddr; /* IP address of next server to talk to */ + struct in_addr giaddr; /* DHCP relay agent IP address */ + unsigned char chaddr[16]; /* Client hardware address */ + char sname[DHCP_SNAME_LEN]; /* Server name */ + char file[DHCP_FILE_LEN]; /* Boot filename */ + unsigned char options[DHCP_OPTION_LEN]; + /* Optional parameters + (actual length dependent on MTU). */ +}; + +/* BOOTP (rfc951) message types */ +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +/* Possible values for flags field... */ +#define BOOTP_BROADCAST 32768L + +/* Possible values for hardware type (htype) field... */ +#define HTYPE_ETHER 1 /* Ethernet */ +#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */ +#define HTYPE_FDDI 8 /* FDDI... */ +#define HTYPE_IPSEC_TUNNEL 31 /* IPsec Tunnel (RFC3456) */ + +/* Magic cookie validating dhcp options field (and bootp vendor + extensions field). */ +#define DHCP_OPTIONS_COOKIE "\143\202\123\143" +#define DHCP_OPTIONS_COOKIE_LEN 4 + +/* DHCP Option codes: */ + +#define DHO_PAD 0 +#define DHO_SUBNET_MASK 1 +#define DHO_TIME_OFFSET 2 +#define DHO_ROUTERS 3 +#define DHO_TIME_SERVERS 4 +#define DHO_NAME_SERVERS 5 +#define DHO_DOMAIN_NAME_SERVERS 6 +#define DHO_LOG_SERVERS 7 +#define DHO_COOKIE_SERVERS 8 +#define DHO_LPR_SERVERS 9 +#define DHO_IMPRESS_SERVERS 10 +#define DHO_RESOURCE_LOCATION_SERVERS 11 +#define DHO_HOST_NAME 12 +#define DHO_BOOT_SIZE 13 +#define DHO_MERIT_DUMP 14 +#define DHO_DOMAIN_NAME 15 +#define DHO_SWAP_SERVER 16 +#define DHO_ROOT_PATH 17 +#define DHO_EXTENSIONS_PATH 18 +#define DHO_IP_FORWARDING 19 +#define DHO_NON_LOCAL_SOURCE_ROUTING 20 +#define DHO_POLICY_FILTER 21 +#define DHO_MAX_DGRAM_REASSEMBLY 22 +#define DHO_DEFAULT_IP_TTL 23 +#define DHO_PATH_MTU_AGING_TIMEOUT 24 +#define DHO_PATH_MTU_PLATEAU_TABLE 25 +#define DHO_INTERFACE_MTU 26 +#define DHO_ALL_SUBNETS_LOCAL 27 +#define DHO_BROADCAST_ADDRESS 28 +#define DHO_PERFORM_MASK_DISCOVERY 29 +#define DHO_MASK_SUPPLIER 30 +#define DHO_ROUTER_DISCOVERY 31 +#define DHO_ROUTER_SOLICITATION_ADDRESS 32 +#define DHO_STATIC_ROUTES 33 +#define DHO_TRAILER_ENCAPSULATION 34 +#define DHO_ARP_CACHE_TIMEOUT 35 +#define DHO_IEEE802_3_ENCAPSULATION 36 +#define DHO_DEFAULT_TCP_TTL 37 +#define DHO_TCP_KEEPALIVE_INTERVAL 38 +#define DHO_TCP_KEEPALIVE_GARBAGE 39 +#define DHO_NIS_DOMAIN 40 +#define DHO_NIS_SERVERS 41 +#define DHO_NTP_SERVERS 42 +#define DHO_VENDOR_ENCAPSULATED_OPTIONS 43 +#define DHO_NETBIOS_NAME_SERVERS 44 +#define DHO_NETBIOS_DD_SERVER 45 +#define DHO_NETBIOS_NODE_TYPE 46 +#define DHO_NETBIOS_SCOPE 47 +#define DHO_FONT_SERVERS 48 +#define DHO_X_DISPLAY_MANAGER 49 +#define DHO_DHCP_REQUESTED_ADDRESS 50 +#define DHO_DHCP_LEASE_TIME 51 +#define DHO_DHCP_OPTION_OVERLOAD 52 +#define DHO_DHCP_MESSAGE_TYPE 53 +#define DHO_DHCP_SERVER_IDENTIFIER 54 +#define DHO_DHCP_PARAMETER_REQUEST_LIST 55 +#define DHO_DHCP_MESSAGE 56 +#define DHO_DHCP_MAX_MESSAGE_SIZE 57 +#define DHO_DHCP_RENEWAL_TIME 58 +#define DHO_DHCP_REBINDING_TIME 59 +#define DHO_DHCP_CLASS_IDENTIFIER 60 +#define DHO_DHCP_CLIENT_IDENTIFIER 61 +#define DHO_DHCP_USER_CLASS_ID 77 +#define DHO_RELAY_AGENT_INFORMATION 82 +#define DHO_END 255 + +/* DHCP message types. */ +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +/* Relay Agent Information sub-options */ +#define RAI_CIRCUIT_ID 1 +#define RAI_REMOTE_ID 2 +#define RAI_AGENT_ID 3 diff --git a/usr.sbin/vmd/packet.c b/usr.sbin/vmd/packet.c new file mode 100644 index 00000000000..7e2b635335c --- /dev/null +++ b/usr.sbin/vmd/packet.c @@ -0,0 +1,332 @@ +/* $OpenBSD: packet.c,v 1.1 2017/04/19 15:38:32 reyk Exp $ */ + +/* Packet assembly code, originally contributed by Archie Cobbs. */ + +/* + * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <arpa/inet.h> + +#include <net/if.h> +#include <net/if_enc.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/if_ether.h> + +#include <string.h> + +#include "dhcp.h" +#include "vmd.h" +#include "proc.h" + +u_int32_t checksum(unsigned char *, u_int32_t, u_int32_t); +u_int32_t wrapsum(u_int32_t); + +u_int32_t +checksum(unsigned char *buf, u_int32_t nbytes, u_int32_t sum) +{ + u_int32_t i; + + /* Checksum all the pairs of bytes first... */ + for (i = 0; i < (nbytes & ~1U); i += 2) { + sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i))); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + + /* + * If there's a single byte left over, checksum it, too. + * Network byte order is big-endian, so the remaining byte is + * the high byte. + */ + if (i < nbytes) { + sum += buf[i] << 8; + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + + return (sum); +} + +u_int32_t +wrapsum(u_int32_t sum) +{ + sum = ~sum & 0xFFFF; + return (htons(sum)); +} + +ssize_t +assemble_hw_header(unsigned char *buf, size_t buflen, + size_t offset, struct packet_ctx *pc, unsigned int intfhtype) +{ + struct ether_header eh; + + switch (intfhtype) { + case HTYPE_ETHER: + if (buflen < offset + ETHER_HDR_LEN) + return (-1); + + /* Use the supplied address or let the kernel fill it. */ + memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN); + memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN); + + eh.ether_type = htons(ETHERTYPE_IP); + + memcpy(&buf[offset], &eh, ETHER_HDR_LEN); + offset += ETHER_HDR_LEN; + break; + default: + return (-1); + } + + return (offset); +} + +ssize_t +assemble_udp_ip_header(unsigned char *buf, size_t buflen, size_t offset, + struct packet_ctx *pc, unsigned char *data, size_t datalen) +{ + struct ip ip; + struct udphdr udp; + + if (buflen < offset + sizeof(ip) + sizeof(udp)) + return (-1); + + ip.ip_v = 4; + ip.ip_hl = 5; + ip.ip_tos = IPTOS_LOWDELAY; + ip.ip_len = htons(sizeof(ip) + sizeof(udp) + datalen); + ip.ip_id = 0; + ip.ip_off = 0; + ip.ip_ttl = 16; + ip.ip_p = IPPROTO_UDP; + ip.ip_sum = 0; + ip.ip_src.s_addr = ss2sin(&pc->pc_src)->sin_addr.s_addr; + ip.ip_dst.s_addr = ss2sin(&pc->pc_dst)->sin_addr.s_addr; + + ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); + memcpy(&buf[offset], &ip, sizeof(ip)); + offset += sizeof(ip); + + udp.uh_sport = ss2sin(&pc->pc_src)->sin_port; + udp.uh_dport = ss2sin(&pc->pc_dst)->sin_port; + udp.uh_ulen = htons(sizeof(udp) + datalen); + memset(&udp.uh_sum, 0, sizeof(udp.uh_sum)); + + udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), + checksum(data, datalen, checksum((unsigned char *)&ip.ip_src, + 2 * sizeof(ip.ip_src), + IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen))))); + + memcpy(&buf[offset], &udp, sizeof(udp)); + offset += sizeof(udp); + + return (offset); +} + +ssize_t +decode_hw_header(unsigned char *buf, size_t buflen, + size_t offset, struct packet_ctx *pc, unsigned int intfhtype) +{ + u_int32_t ip_len; + struct ip *ip; + + switch (intfhtype) { + case HTYPE_IPSEC_TUNNEL: + if (buflen < offset + ENC_HDRLEN + sizeof(*ip)) + return (-1); + offset += ENC_HDRLEN; + ip_len = (buf[offset] & 0xf) << 2; + if (buflen < offset + ip_len) + return (-1); + + ip = (struct ip *)(buf + offset); + + /* Encapsulated IP */ + if (ip->ip_p != IPPROTO_IPIP) + return (-1); + + memset(pc->pc_dmac, 0xff, ETHER_ADDR_LEN); + offset += ip_len; + + pc->pc_htype = ARPHRD_ETHER; + pc->pc_hlen = ETHER_ADDR_LEN; + break; + case HTYPE_ETHER: + if (buflen < offset + ETHER_HDR_LEN) + return (-1); + + memcpy(pc->pc_dmac, buf + offset, ETHER_ADDR_LEN); + memcpy(pc->pc_smac, buf + offset + ETHER_ADDR_LEN, + ETHER_ADDR_LEN); + offset += ETHER_HDR_LEN; + + pc->pc_htype = ARPHRD_ETHER; + pc->pc_hlen = ETHER_ADDR_LEN; + break; + default: + return (-1); + } + + return (offset); +} + +ssize_t +decode_udp_ip_header(unsigned char *buf, size_t buflen, + size_t offset, struct packet_ctx *pc) +{ + struct ip *ip; + struct udphdr *udp; + unsigned char *data; + u_int32_t ip_len; + u_int32_t sum, usum; + static unsigned int ip_packets_seen; + static unsigned int ip_packets_bad_checksum; + static unsigned int udp_packets_seen; + static unsigned int udp_packets_bad_checksum; + static unsigned int udp_packets_length_checked; + static unsigned int udp_packets_length_overflow; + int len; + + /* Assure that an entire IP header is within the buffer. */ + if (buflen < offset + sizeof(*ip)) + return (-1); + ip_len = (buf[offset] & 0xf) << 2; + if (buflen < offset + ip_len) + return (-1); + + ip = (struct ip *)(buf + offset); + ip_packets_seen++; + + /* Check the IP header checksum - it should be zero. */ + if (wrapsum(checksum(buf + offset, ip_len, 0)) != 0) { + ip_packets_bad_checksum++; + if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 && + (ip_packets_seen / ip_packets_bad_checksum) < 2) { + log_info("%u bad IP checksums seen in %u packets", + ip_packets_bad_checksum, ip_packets_seen); + ip_packets_seen = ip_packets_bad_checksum = 0; + } + return (-1); + } + + pc->pc_src.ss_len = sizeof(struct sockaddr_in); + pc->pc_src.ss_family = AF_INET; + memcpy(&ss2sin(&pc->pc_src)->sin_addr, &ip->ip_src, + sizeof(ss2sin(&pc->pc_src)->sin_addr)); + + pc->pc_dst.ss_len = sizeof(struct sockaddr_in); + pc->pc_dst.ss_family = AF_INET; + memcpy(&ss2sin(&pc->pc_dst)->sin_addr, &ip->ip_dst, + sizeof(ss2sin(&pc->pc_dst)->sin_addr)); + +#ifdef DEBUG + if (buflen != offset + ntohs(ip->ip_len)) + log_debug("ip length %d disagrees with bytes received %zd.", + ntohs(ip->ip_len), buflen - offset); +#endif + + /* Assure that the entire IP packet is within the buffer. */ + if (buflen < offset + ntohs(ip->ip_len)) + return (-1); + + /* Assure that the UDP header is within the buffer. */ + if (buflen < offset + ip_len + sizeof(*udp)) + return (-1); + udp = (struct udphdr *)(buf + offset + ip_len); + udp_packets_seen++; + + /* Assure that the entire UDP packet is within the buffer. */ + if (buflen < offset + ip_len + ntohs(udp->uh_ulen)) + return (-1); + data = buf + offset + ip_len + sizeof(*udp); + + /* + * Compute UDP checksums, including the ``pseudo-header'', the + * UDP header and the data. If the UDP checksum field is zero, + * we're not supposed to do a checksum. + */ + udp_packets_length_checked++; + len = ntohs(udp->uh_ulen) - sizeof(*udp); + if ((len < 0) || (len + data > buf + buflen)) { + udp_packets_length_overflow++; + if (udp_packets_length_checked > 4 && + udp_packets_length_overflow != 0 && + (udp_packets_length_checked / + udp_packets_length_overflow) < 2) { + log_info("%u udp packets in %u too long - dropped", + udp_packets_length_overflow, + udp_packets_length_checked); + udp_packets_length_overflow = + udp_packets_length_checked = 0; + } + return (-1); + } + if (len + data != buf + buflen) + log_debug("accepting packet with data after udp payload."); + + usum = udp->uh_sum; + udp->uh_sum = 0; + + sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp), + checksum(data, len, checksum((unsigned char *)&ip->ip_src, + 2 * sizeof(ip->ip_src), + IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen))))); + + udp_packets_seen++; + if (usum && usum != sum) { + udp_packets_bad_checksum++; + if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 && + (udp_packets_seen / udp_packets_bad_checksum) < 2) { + log_info("%u bad udp checksums in %u packets", + udp_packets_bad_checksum, udp_packets_seen); + udp_packets_seen = udp_packets_bad_checksum = 0; + } + return (-1); + } + + ss2sin(&pc->pc_src)->sin_port = udp->uh_sport; + ss2sin(&pc->pc_dst)->sin_port = udp->uh_dport; + + return (offset + ip_len + sizeof(*udp)); +} diff --git a/usr.sbin/vmd/parse.y b/usr.sbin/vmd/parse.y index 935e13b4106..cf181701ada 100644 --- a/usr.sbin/vmd/parse.y +++ b/usr.sbin/vmd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.24 2017/04/06 21:35:22 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.25 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org> @@ -116,10 +116,11 @@ typedef struct { %token INCLUDE ERROR %token ADD DISK DOWN GROUP INTERFACE NIFS PATH SIZE SWITCH UP VMID -%token ENABLE DISABLE VM BOOT LLADDR MEMORY OWNER LOCKED +%token ENABLE DISABLE VM BOOT LLADDR MEMORY OWNER LOCKED LOCAL %token <v.string> STRING %token <v.number> NUMBER %type <v.number> disable +%type <v.number> local %type <v.number> locked %type <v.number> updown %type <v.lladdr> lladdr @@ -325,36 +326,38 @@ vm_opts : disable { free($2); vmc.vmc_flags |= VMOP_CREATE_DISK; } - | INTERFACE optstring iface_opts_o { + | local INTERFACE optstring iface_opts_o { unsigned int i; char type[IF_NAMESIZE]; i = vcp_nnics; if (++vcp_nnics > VMM_MAX_NICS_PER_VM) { yyerror("too many interfaces: %zu", vcp_nnics); - free($2); + free($3); YYERROR; } - if ($2 != NULL) { - if (strcmp($2, "tap") != 0 && - (priv_getiftype($2, type, NULL) == -1 || + if ($1) + vmc.vmc_ifflags[i] |= VMIFF_LOCAL; + if ($3 != NULL) { + if (strcmp($3, "tap") != 0 && + (priv_getiftype($3, type, NULL) == -1 || strcmp(type, "tap") != 0)) { - yyerror("invalid interface: %s", $2); - free($2); + yyerror("invalid interface: %s", $3); + free($3); YYERROR; } - if (strlcpy(vmc.vmc_ifnames[i], $2, + if (strlcpy(vmc.vmc_ifnames[i], $3, sizeof(vmc.vmc_ifnames[i])) >= sizeof(vmc.vmc_ifnames[i])) { yyerror("interface name too long: %s", - $2); - free($2); + $3); + free($3); YYERROR; } } - free($2); + free($3); vmc.vmc_flags |= VMOP_CREATE_NETWORK; } | BOOT string { @@ -547,6 +550,10 @@ lladdr : STRING { } ; +local : /* empty */ { $$ = 0; } + | LOCAL { $$ = 1; } + ; + locked : /* empty */ { $$ = 0; } | LOCKED { $$ = 1; } ; @@ -616,6 +623,7 @@ lookup(char *s) { "interface", INTERFACE }, { "interfaces", NIFS }, { "lladdr", LLADDR }, + { "local", LOCAL }, { "locked", LOCKED }, { "memory", MEMORY }, { "owner", OWNER }, diff --git a/usr.sbin/vmd/priv.c b/usr.sbin/vmd/priv.c index 2b99a246259..c0f1c36fd25 100644 --- a/usr.sbin/vmd/priv.c +++ b/usr.sbin/vmd/priv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: priv.c,v 1.6 2017/03/02 07:33:37 reyk Exp $ */ +/* $OpenBSD: priv.c,v 1.7 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org> @@ -29,6 +29,8 @@ #include <netinet/if_ether.h> #include <net/if_bridge.h> +#include <arpa/inet.h> + #include <errno.h> #include <event.h> #include <fcntl.h> @@ -80,6 +82,7 @@ priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) struct ifreq ifr; struct ifbreq ifbr; struct ifgroupreq ifgr; + struct ifaliasreq ifra; char type[IF_NAMESIZE]; switch (imsg->hdr.type) { @@ -89,6 +92,7 @@ priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_VMDOP_PRIV_IFUP: case IMSG_VMDOP_PRIV_IFDOWN: case IMSG_VMDOP_PRIV_IFGROUP: + case IMSG_VMDOP_PRIV_IFADDR: IMSG_SIZE_CHECK(imsg, &vfr); memcpy(&vfr, imsg->data, sizeof(vfr)); @@ -160,6 +164,25 @@ priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) errno != EEXIST) log_warn("SIOCAIFGROUP"); break; + case IMSG_VMDOP_PRIV_IFADDR: + memset(&ifra, 0, sizeof(ifra)); + + /* Set the interface address */ + strlcpy(ifra.ifra_name, vfr.vfr_name, sizeof(ifra.ifra_name)); + + memcpy(&ifra.ifra_addr, &vfr.vfr_ifra.ifra_addr, + sizeof(ifra.ifra_addr)); + ifra.ifra_addr.sa_family = AF_INET; + ifra.ifra_addr.sa_len = sizeof(struct sockaddr_in); + + memcpy(&ifra.ifra_mask, &vfr.vfr_ifra.ifra_mask, + sizeof(ifra.ifra_mask)); + ifra.ifra_mask.sa_family = AF_INET; + ifra.ifra_mask.sa_len = sizeof(struct sockaddr_in); + + if (ioctl(env->vmd_fd, SIOCAIFADDR, &ifra) < 0) + log_warn("SIOCAIFADDR"); + break; default: return (-1); } @@ -227,6 +250,7 @@ vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm) struct vmd_switch *vsw; unsigned int i; struct vmop_ifreq vfr, vfbr; + struct sockaddr_in *sin4; for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { vif = &vm->vm_ifs[i]; @@ -298,6 +322,27 @@ vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm) proc_compose(ps, PROC_PRIV, (vif->vif_flags & VMIFF_UP) ? IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, &vfr, sizeof(vfr)); + + if (vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) { + sin4 = (struct sockaddr_in *)&vfr.vfr_ifra.ifra_mask; + sin4->sin_family = AF_INET; + sin4->sin_len = sizeof(*sin4); + sin4->sin_addr.s_addr = htonl(0xfffffffe); + + sin4 = (struct sockaddr_in *)&vfr.vfr_ifra.ifra_addr; + sin4->sin_family = AF_INET; + sin4->sin_len = sizeof(*sin4); + if ((sin4->sin_addr.s_addr = + vm_priv_addr(vm->vm_vmid, i, 0)) == 0) + return (-1); + + log_debug("%s: interface %s address %s/31", + __func__, vfr.vfr_name, + inet_ntoa(sin4->sin_addr)); + + proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR, + &vfr, sizeof(vfr)); + } } return (0); @@ -346,3 +391,46 @@ vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw) vsw->sw_running = 1; return (0); } + +uint32_t +vm_priv_addr(uint32_t vmid, int idx, int isvm) +{ + in_addr_t prefix, mask, addr; + + /* + * 1. Set the address prefix and mask, 100.64.0.0/10 by default. + * XXX make the global prefix configurable. + */ + prefix = inet_addr(VMD_DHCP_PREFIX); + mask = prefixlen2mask(VMD_DHCP_PREFIXLEN); + + /* 2. Encode the VM ID as a per-VM subnet range N, 10.64.N.0/24. */ + addr = vmid << 8; + + /* + * 3. Assign a /31 subnet M per VM interface, 10.64.N.M/31. + * Each subnet contains exactly two IP addresses; skip the + * first subnet to avoid a gateway address ending with .0. + */ + addr |= (idx + 1) * 2; + + /* 4. Use the first address for the gateway, the second for the VM. */ + if (isvm) + addr++; + + /* 5. Convert to network byte order and add the prefix. */ + addr = htonl(addr) | prefix; + + /* + * Validate the results: + * - the address should not exceed the prefix (eg. VM ID to high). + * - up to 126 interfaces can be encoded per VM. + */ + if (prefix != (addr & mask) || idx >= 0x7f) { + log_warnx("%s: dhcp address range exceeded," + " vm id %u interface %d", __func__, vmid, idx); + return (0); + } + + return (addr); +} diff --git a/usr.sbin/vmd/virtio.c b/usr.sbin/vmd/virtio.c index 4dd35923d00..5bdb999092d 100644 --- a/usr.sbin/vmd/virtio.c +++ b/usr.sbin/vmd/virtio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: virtio.c,v 1.41 2017/04/08 19:08:18 mlarkin Exp $ */ +/* $OpenBSD: virtio.c,v 1.42 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -1251,15 +1251,17 @@ vionet_notifyq(struct vionet_dev *dev) uint32_t vr_sz; uint16_t idx, pkt_desc_idx, hdr_desc_idx, dxx; size_t pktsz; - int ret, num_enq, ofs; - char *vr, *pkt; + ssize_t dhcpsz; + int ret, num_enq, ofs, spc; + char *vr, *pkt, *dhcppkt; struct vring_desc *desc, *pkt_desc, *hdr_desc; struct vring_avail *avail; struct vring_used *used; struct ether_header *eh; - vr = pkt = NULL; - ret = 0; + vr = pkt = dhcppkt = NULL; + ret = spc = 0; + dhcpsz = 0; /* Invalid queue? */ if (dev->cfg.queue_notify != 1) { @@ -1373,8 +1375,13 @@ vionet_notifyq(struct vionet_dev *dev) log_debug("vionet: wrong source address %s for vm %d", ether_ntoa((struct ether_addr *) eh->ether_shost), dev->vm_id); + else if (dev->local && dhcpsz == 0 && + (dhcpsz = dhcp_request(dev, pkt, pktsz, &dhcppkt)) != -1) { + log_debug("vionet: dhcp request," + " local response size %zd", dhcpsz); + /* XXX signed vs unsigned here, funky cast */ - else if (write(dev->fd, pkt, pktsz) != (int)pktsz) { + } else if (write(dev->fd, pkt, pktsz) != (int)pktsz) { log_warnx("vionet: tx failed writing to tap: " "%d", errno); goto out; @@ -1398,9 +1405,15 @@ vionet_notifyq(struct vionet_dev *dev) log_warnx("vionet: tx error writing vio ring"); } + if (dhcpsz > 0) { + if (vionet_enq_rx(dev, dhcppkt, dhcpsz, &spc)) + ret = 1; + } + out: free(vr); free(pkt); + free(dhcppkt); return (ret); } @@ -1582,8 +1595,9 @@ vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr, } void -virtio_init(struct vmop_create_params *vmc, int *child_disks, int *child_taps) +virtio_init(struct vmd_vm *vm, int *child_disks, int *child_taps) { + struct vmop_create_params *vmc = &vm->vm_params; struct vm_create_params *vcp = &vmc->vmc_params; static const uint8_t zero_mac[6]; uint8_t id; @@ -1713,6 +1727,7 @@ virtio_init(struct vmop_create_params *vmc, int *child_disks, int *child_taps) vionet[i].fd = child_taps[i]; vionet[i].rx_pending = 0; vionet[i].vm_id = vcp->vcp_id; + vionet[i].vm_vmid = vm->vm_vmid; vionet[i].irq = pci_get_dev_irq(id); event_set(&vionet[i].event, vionet[i].fd, @@ -1747,11 +1762,15 @@ virtio_init(struct vmop_create_params *vmc, int *child_disks, int *child_taps) } vionet[i].lockedmac = vmc->vmc_ifflags[i] & VMIFF_LOCKED ? 1 : 0; + vionet[i].local = + vmc->vmc_ifflags[i] & VMIFF_LOCAL ? 1 : 0; + vionet[i].idx = i; - log_debug("%s: vm \"%s\" vio%u lladdr %s%s", + log_debug("%s: vm \"%s\" vio%u lladdr %s%s%s", __func__, vcp->vcp_name, i, ether_ntoa((void *)vionet[i].mac), - vionet[i].lockedmac ? " (locked)" : ""); + vionet[i].lockedmac ? ", locked" : "", + vionet[i].local ? ", local" : ""); } } diff --git a/usr.sbin/vmd/virtio.h b/usr.sbin/vmd/virtio.h index 04b81ea472d..a830d1738d0 100644 --- a/usr.sbin/vmd/virtio.h +++ b/usr.sbin/vmd/virtio.h @@ -1,4 +1,4 @@ -/* $OpenBSD: virtio.h,v 1.14 2017/03/27 00:28:04 deraadt Exp $ */ +/* $OpenBSD: virtio.h,v 1.15 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -118,9 +118,13 @@ struct vionet_dev { int fd, rx_added; int rx_pending; uint32_t vm_id; + uint32_t vm_vmid; int irq; uint8_t mac[6]; + + int idx; int lockedmac; + int local; }; struct virtio_net_hdr { @@ -154,7 +158,8 @@ struct vmmci_dev { int irq; }; -void virtio_init(struct vmop_create_params *, int *, int *); +/* virtio.c */ +void virtio_init(struct vmd_vm *, int *, int *); uint32_t vring_size(uint32_t); int virtio_rnd_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t); @@ -181,3 +186,6 @@ void vmmci_ack(unsigned int); void vmmci_timeout(int, short, void *); const char *vioblk_cmd_name(uint32_t); + +/* dhcp.c */ +ssize_t dhcp_request(struct vionet_dev *, char *, size_t, char **); diff --git a/usr.sbin/vmd/vm.c b/usr.sbin/vmd/vm.c index b833d8da897..6ce5f519f52 100644 --- a/usr.sbin/vmd/vm.c +++ b/usr.sbin/vmd/vm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm.c,v 1.11 2017/03/27 00:28:04 deraadt Exp $ */ +/* $OpenBSD: vm.c,v 1.12 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -656,7 +656,7 @@ init_emulated_hw(struct vmop_create_params *vmc, int *child_disks, pci_init(); /* Initialize virtio devices */ - virtio_init(vmc, child_disks, child_taps); + virtio_init(current_vm, child_disks, child_taps); } /* diff --git a/usr.sbin/vmd/vm.conf.5 b/usr.sbin/vmd/vm.conf.5 index 6093c9d7cf4..ad610b30b8a 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.17 2017/03/25 16:28:25 reyk Exp $ +.\" $OpenBSD: vm.conf.5,v 1.18 2017/04/19 15:38:32 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: March 25 2017 $ +.Dd $Mdocdate: April 19 2017 $ .Dt VM.CONF 5 .Os .Sh NAME @@ -112,7 +112,7 @@ is specified. Do not start this VM. .It Cm disk Ar path Disk image file (may be specified multiple times to add multiple disk images). -.It Cm interface Oo name Oc Op Brq ... +.It Oo Cm local Oc Cm interface Oo name Oc Op Brq ... Network interface to add to the VM. The optional .Ar name @@ -123,6 +123,7 @@ to select the next available interface on the VM host side (the default) or .Ar tapN to select a specific one. +.Pp Valid options are: .Bl -tag -width Ds .It Cm group Ar group-name @@ -158,6 +159,13 @@ This is the default. .It Cm down Stop the interface from forwarding packets. .El +.Pp +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. +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. If the diff --git a/usr.sbin/vmd/vmd.c b/usr.sbin/vmd/vmd.c index 3bcfed30965..fcd2c3696b9 100644 --- a/usr.sbin/vmd/vmd.c +++ b/usr.sbin/vmd/vmd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmd.c,v 1.56 2017/04/06 18:07:13 reyk Exp $ */ +/* $OpenBSD: vmd.c,v 1.57 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org> @@ -1045,3 +1045,15 @@ get_string(uint8_t *ptr, size_t len) return strndup(ptr, i); } + +uint32_t +prefixlen2mask(uint8_t prefixlen) +{ + if (prefixlen == 0) + return (0); + + if (prefixlen > 32) + prefixlen = 32; + + return (htonl(0xffffffff << (32 - prefixlen))); +} diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h index 3e51d964702..5dc0bb4d81d 100644 --- a/usr.sbin/vmd/vmd.h +++ b/usr.sbin/vmd/vmd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vmd.h,v 1.50 2017/04/06 18:07:13 reyk Exp $ */ +/* $OpenBSD: vmd.h,v 1.51 2017/04/19 15:38:32 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -23,6 +23,8 @@ #include <machine/vmmvar.h> #include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> #include <limits.h> #include <stdio.h> @@ -48,6 +50,10 @@ #define VMD_SWITCH_TYPE "bridge" #define VM_DEFAULT_MEMORY 512 +/* 100.64.0.0/10 from rfc6598 (IPv4 Prefix for Shared Address Space) */ +#define VMD_DHCP_PREFIX "100.64.0.0" +#define VMD_DHCP_PREFIXLEN 10 + #ifdef VMD_DEBUG #define dprintf(x...) do { log_debug(x); } while(0) #else @@ -74,6 +80,7 @@ enum imsg_type { IMSG_VMDOP_PRIV_IFUP, IMSG_VMDOP_PRIV_IFDOWN, IMSG_VMDOP_PRIV_IFGROUP, + IMSG_VMDOP_PRIV_IFADDR, IMSG_VMDOP_VM_SHUTDOWN, IMSG_VMDOP_VM_REBOOT }; @@ -102,6 +109,7 @@ struct vmop_ifreq { uint32_t vfr_id; char vfr_name[IF_NAMESIZE]; char vfr_value[VM_NAME_MAX]; + struct ifaliasreq vfr_ifra; }; struct vmop_create_params { @@ -116,7 +124,8 @@ struct vmop_create_params { unsigned int vmc_ifflags[VMM_MAX_NICS_PER_VM]; #define VMIFF_UP 0x01 #define VMIFF_LOCKED 0x02 -#define VMIFF_OPTMASK VMIFF_LOCKED +#define VMIFF_LOCAL 0x04 +#define VMIFF_OPTMASK (VMIFF_LOCKED|VMIFF_LOCAL) char vmc_ifnames[VMM_MAX_NICS_PER_VM][IF_NAMESIZE]; char vmc_ifswitch[VMM_MAX_NICS_PER_VM][VM_NAME_MAX]; char vmc_ifgroup[VMM_MAX_NICS_PER_VM][IF_NAMESIZE]; @@ -198,6 +207,38 @@ struct vmd { int vmd_ptmfd; }; +static inline struct sockaddr_in * +ss2sin(struct sockaddr_storage *ss) +{ + return ((struct sockaddr_in *)ss); +} + +static inline struct sockaddr_in6 * +ss2sin6(struct sockaddr_storage *ss) +{ + return ((struct sockaddr_in6 *)ss); +} + +struct packet_ctx { + uint8_t pc_htype; + uint8_t pc_hlen; + uint8_t pc_smac[ETHER_ADDR_LEN]; + uint8_t pc_dmac[ETHER_ADDR_LEN]; + + struct sockaddr_storage pc_src; + struct sockaddr_storage pc_dst; +}; + +/* packet.c */ +ssize_t assemble_hw_header(unsigned char *, size_t, size_t, + struct packet_ctx *, unsigned int); +ssize_t assemble_udp_ip_header(unsigned char *, size_t, size_t, + struct packet_ctx *pc, unsigned char *, size_t); +ssize_t decode_hw_header(unsigned char *, size_t, size_t, struct packet_ctx *, + unsigned int); +ssize_t decode_udp_ip_header(unsigned char *, size_t, size_t, + struct packet_ctx *); + /* vmd.c */ void vmd_reload(unsigned int, const char *); struct vmd_vm *vm_getbyid(uint32_t); @@ -216,6 +257,7 @@ void vm_closetty(struct vmd_vm *); void switch_remove(struct vmd_switch *); struct vmd_switch *switch_getbyname(const char *); char *get_string(uint8_t *, size_t); +uint32_t prefixlen2mask(uint8_t); /* priv.c */ void priv(struct privsep *, struct privsep_proc *); @@ -224,6 +266,7 @@ int priv_findname(const char *, const char **); int priv_validgroup(const char *); int vm_priv_ifconfig(struct privsep *, struct vmd_vm *); int vm_priv_brconfig(struct privsep *, struct vmd_switch *); +uint32_t vm_priv_addr(uint32_t, int, int); /* vmm.c */ void vmm(struct privsep *, struct privsep_proc *); |