/* $OpenBSD: config.c,v 1.13 2016/10/04 17:17:30 reyk Exp $ */ /* * Copyright (c) 2015 Reyk Floeter * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "proc.h" #include "vmd.h" int config_init(struct vmd *env) { struct privsep *ps = &env->vmd_ps; unsigned int what; /* Global configuration */ ps->ps_what[PROC_PARENT] = CONFIG_ALL; ps->ps_what[PROC_VMM] = CONFIG_VMS; ps->ps_what[PROC_PRIV] = 0; /* Other configuration */ what = ps->ps_what[privsep_process]; if (what & CONFIG_VMS) { if ((env->vmd_vms = calloc(1, sizeof(*env->vmd_vms))) == NULL) return (-1); TAILQ_INIT(env->vmd_vms); } return (0); } void config_purge(struct vmd *env, unsigned int reset) { struct privsep *ps = &env->vmd_ps; struct vmd_vm *vm; unsigned int what; what = ps->ps_what[privsep_process] & reset; if (what & CONFIG_VMS && env->vmd_vms != NULL) { while ((vm = TAILQ_FIRST(env->vmd_vms)) != NULL) vm_remove(vm); env->vmd_vmcount = 0; } } int config_setreset(struct vmd *env, unsigned int reset) { struct privsep *ps = &env->vmd_ps; unsigned int id; for (id = 0; id < PROC_MAX; id++) { if ((reset & ps->ps_what[id]) == 0 || id == privsep_process) continue; proc_compose(ps, id, IMSG_CTL_RESET, &reset, sizeof(reset)); } return (0); } int config_getreset(struct vmd *env, struct imsg *imsg) { unsigned int mode; IMSG_SIZE_CHECK(imsg, &mode); memcpy(&mode, imsg->data, sizeof(mode)); config_purge(env, mode); return (0); } int config_getvm(struct privsep *ps, struct vm_create_params *vcp, int kernel_fd, uint32_t peerid) { struct vmd *env = ps->ps_env; struct vmd_vm *vm = NULL; unsigned int i; int fd, ttys_fd; int kernfd = -1, *diskfds = NULL, *tapfds = NULL; int saved_errno = 0; char ptyname[VM_TTYNAME_MAX]; char ifname[IF_NAMESIZE]; errno = 0; if (vm_getbyname(vcp->vcp_name) != NULL) { errno = EALREADY; goto fail; } if (vcp->vcp_ncpus == 0) vcp->vcp_ncpus = 1; if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM) { log_debug("invalid number of CPUs"); goto fail; } else if (vcp->vcp_ndisks > VMM_MAX_DISKS_PER_VM) { log_debug("invalid number of disks"); goto fail; } else if (vcp->vcp_nnics > VMM_MAX_NICS_PER_VM) { log_debug("invalid number of interfaces"); goto fail; } if ((vm = calloc(1, sizeof(*vm))) == NULL) goto fail; memcpy(&vm->vm_params, vcp, sizeof(vm->vm_params)); vm->vm_pid = -1; for (i = 0; i < vcp->vcp_ndisks; i++) vm->vm_disks[i] = -1; for (i = 0; i < vcp->vcp_nnics; i++) vm->vm_ifs[i] = -1; vm->vm_kernel = -1; vm->vm_vmid = env->vmd_nvm + 1; if (vm_getbyvmid(vm->vm_vmid) != NULL) fatalx("too many vms"); TAILQ_INSERT_TAIL(env->vmd_vms, vm, vm_entry); switch (privsep_process) { case PROC_VMM: if (kernel_fd == -1) { log_debug("invalid kernel fd"); goto fail; } vm->vm_kernel = kernel_fd; break; case PROC_PARENT: diskfds = reallocarray(NULL, vcp->vcp_ndisks, sizeof(*diskfds)); if (diskfds == NULL) { saved_errno = errno; log_warn("%s: cannot allocate disk fds", __func__); goto fail; } for (i = 0; i < vcp->vcp_ndisks; i++) diskfds[i] = -1; tapfds = reallocarray(NULL, vcp->vcp_nnics, sizeof(*tapfds)); if (tapfds == NULL) { saved_errno = errno; log_warn("%s: cannot allocate tap fds", __func__); goto fail; } for (i = 0; i < vcp->vcp_nnics; i++) tapfds[i] = -1; vm->vm_peerid = peerid; /* Open kernel for child */ if ((kernfd = open(vcp->vcp_kernel, O_RDONLY)) == -1) { saved_errno = errno; log_warn("%s: can't open kernel %s", __func__, vcp->vcp_kernel); goto fail; } /* Open disk images for child */ for (i = 0 ; i < vcp->vcp_ndisks; i++) { if ((diskfds[i] = open(vcp->vcp_disks[i], O_RDWR)) == -1) { saved_errno = errno; log_warn("%s: can't open %s", __func__, vcp->vcp_disks[i]); goto fail; } } /* Open disk network interfaces */ for (i = 0 ; i < vcp->vcp_nnics; i++) { if ((tapfds[i] = opentap(ifname)) == -1) { saved_errno = errno; log_warn("%s: can't open tap", __func__); goto fail; } if ((vm->vm_ifnames[i] = strdup(ifname)) == NULL) { saved_errno = errno; log_warn("%s: can't save ifname", __func__); goto fail; } } /* Open TTY */ if (openpty(&fd, &ttys_fd, ptyname, NULL, NULL) == -1 || (vm->vm_ttyname = strdup(ptyname)) == NULL) { saved_errno = errno; log_warn("%s: can't open tty", __func__); goto fail; } close(ttys_fd); /* Send VM information */ proc_compose_imsg(ps, PROC_VMM, -1, IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, kernfd, vcp, sizeof(*vcp)); for (i = 0; i < vcp->vcp_ndisks; i++) { proc_compose_imsg(ps, PROC_VMM, -1, IMSG_VMDOP_START_VM_DISK, vm->vm_vmid, diskfds[i], &i, sizeof(i)); } for (i = 0; i < vcp->vcp_nnics; i++) { proc_compose_imsg(ps, PROC_VMM, -1, IMSG_VMDOP_START_VM_IF, vm->vm_vmid, tapfds[i], &i, sizeof(i)); } proc_compose_imsg(ps, PROC_VMM, -1, IMSG_VMDOP_START_VM_END, vm->vm_vmid, fd, NULL, 0); break; default: fatalx("vm received by invalid process"); } free(diskfds); free(tapfds); env->vmd_nvm++; return (0); fail: if (kernfd != -1) close(kernfd); if (diskfds != NULL) { for (i = 0; i < vcp->vcp_ndisks; i++) close(diskfds[i]); free(diskfds); } if (tapfds != NULL) { for (i = 0; i < vcp->vcp_nnics; i++) close(tapfds[i]); free(tapfds); } vm_remove(vm); errno = saved_errno; if (errno == 0) errno = EINVAL; return (-1); } int config_getdisk(struct privsep *ps, struct imsg *imsg) { struct vmd_vm *vm; unsigned int n; errno = 0; if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) { errno = ENOENT; return (-1); } IMSG_SIZE_CHECK(imsg, &n); memcpy(&n, imsg->data, sizeof(n)); if (n >= vm->vm_params.vcp_ndisks || vm->vm_disks[n] != -1 || imsg->fd == -1) { log_debug("invalid disk id"); errno = EINVAL; return (-1); } vm->vm_disks[n] = imsg->fd; return (0); } int config_getif(struct privsep *ps, struct imsg *imsg) { struct vmd_vm *vm; unsigned int n; errno = 0; if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) { errno = ENOENT; return (-1); } IMSG_SIZE_CHECK(imsg, &n); memcpy(&n, imsg->data, sizeof(n)); if (n >= vm->vm_params.vcp_nnics || vm->vm_ifs[n] != -1 || imsg->fd == -1) { log_debug("invalid interface id"); goto fail; } vm->vm_ifs[n] = imsg->fd; return (0); fail: if (imsg->fd != -1) close(imsg->fd); errno = EINVAL; return (-1); }