xref: /openbsd-src/usr.sbin/vmd/config.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: config.c,v 1.71 2023/04/28 19:46:42 dv Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/time.h>
22 #include <sys/uio.h>
23 #include <sys/stat.h>
24 #include <sys/socket.h>
25 
26 #include <net/if.h>
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <termios.h>
31 #include <unistd.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <util.h>
36 #include <errno.h>
37 #include <imsg.h>
38 
39 #include "proc.h"
40 #include "vmd.h"
41 
42 /* Supported bridge types */
43 const char *vmd_descsw[] = { "bridge", "veb", NULL };
44 
45 static int	 config_init_localprefix(struct vmd_config *);
46 
47 static int
48 config_init_localprefix(struct vmd_config *cfg)
49 {
50 	struct sockaddr_in6	*sin6;
51 
52 	if (host(VMD_DHCP_PREFIX, &cfg->cfg_localprefix) == -1)
53 		return (-1);
54 
55 	/* IPv6 is disabled by default */
56 	cfg->cfg_flags &= ~VMD_CFG_INET6;
57 
58 	/* Generate random IPv6 prefix only once */
59 	if (cfg->cfg_flags & VMD_CFG_AUTOINET6)
60 		return (0);
61 	if (host(VMD_ULA_PREFIX, &cfg->cfg_localprefix6) == -1)
62 		return (-1);
63 	/* Randomize the 56 bits "Global ID" and "Subnet ID" */
64 	sin6 = ss2sin6(&cfg->cfg_localprefix6.ss);
65 	arc4random_buf(&sin6->sin6_addr.s6_addr[1], 7);
66 	cfg->cfg_flags |= VMD_CFG_AUTOINET6;
67 
68 	return (0);
69 }
70 
71 int
72 config_init(struct vmd *env)
73 {
74 	struct privsep		*ps = &env->vmd_ps;
75 	unsigned int		 what;
76 
77 	/* Global configuration */
78 	ps->ps_what[PROC_PARENT] = CONFIG_ALL;
79 	ps->ps_what[PROC_VMM] = CONFIG_VMS;
80 
81 	/* Local prefix */
82 	if (config_init_localprefix(&env->vmd_cfg) == -1)
83 		return (-1);
84 
85 	/* Other configuration */
86 	what = ps->ps_what[privsep_process];
87 	if (what & CONFIG_VMS) {
88 		if ((env->vmd_vms = calloc(1, sizeof(*env->vmd_vms))) == NULL)
89 			return (-1);
90 		if ((env->vmd_known = calloc(1, sizeof(*env->vmd_known))) == NULL)
91 			return (-1);
92 		TAILQ_INIT(env->vmd_vms);
93 		TAILQ_INIT(env->vmd_known);
94 	}
95 	if (what & CONFIG_SWITCHES) {
96 		if ((env->vmd_switches = calloc(1,
97 		    sizeof(*env->vmd_switches))) == NULL)
98 			return (-1);
99 		TAILQ_INIT(env->vmd_switches);
100 	}
101 
102 	return (0);
103 }
104 
105 void
106 config_purge(struct vmd *env, unsigned int reset)
107 {
108 	struct privsep		*ps = &env->vmd_ps;
109 	struct name2id		*n2i;
110 	struct vmd_vm		*vm;
111 	struct vmd_switch	*vsw;
112 	unsigned int		 what;
113 
114 	DPRINTF("%s: %s purging vms and switches",
115 	    __func__, ps->ps_title[privsep_process]);
116 
117 	/* Reset global configuration (prefix was verified before) */
118 	config_init_localprefix(&env->vmd_cfg);
119 
120 	/* Reset other configuration */
121 	what = ps->ps_what[privsep_process] & reset;
122 	if (what & CONFIG_VMS && env->vmd_vms != NULL) {
123 		while ((vm = TAILQ_FIRST(env->vmd_vms)) != NULL) {
124 			vm_remove(vm, __func__);
125 		}
126 		while ((n2i = TAILQ_FIRST(env->vmd_known)) != NULL) {
127 			TAILQ_REMOVE(env->vmd_known, n2i, entry);
128 			free(n2i);
129 		}
130 		env->vmd_nvm = 0;
131 	}
132 	if (what & CONFIG_SWITCHES && env->vmd_switches != NULL) {
133 		while ((vsw = TAILQ_FIRST(env->vmd_switches)) != NULL)
134 			switch_remove(vsw);
135 		env->vmd_nswitches = 0;
136 	}
137 }
138 
139 int
140 config_setconfig(struct vmd *env)
141 {
142 	struct privsep	*ps = &env->vmd_ps;
143 	unsigned int	 id;
144 
145 	DPRINTF("%s: setting config", __func__);
146 
147 	for (id = 0; id < PROC_MAX; id++) {
148 		if (id == privsep_process)
149 			continue;
150 		proc_compose(ps, id, IMSG_VMDOP_CONFIG, &env->vmd_cfg,
151 		    sizeof(env->vmd_cfg));
152 	}
153 
154 	return (0);
155 }
156 
157 int
158 config_getconfig(struct vmd *env, struct imsg *imsg)
159 {
160 	struct privsep	*ps = &env->vmd_ps;
161 
162 	log_debug("%s: %s retrieving config",
163 	    __func__, ps->ps_title[privsep_process]);
164 
165 	IMSG_SIZE_CHECK(imsg, &env->vmd_cfg);
166 	memcpy(&env->vmd_cfg, imsg->data, sizeof(env->vmd_cfg));
167 
168 	return (0);
169 }
170 
171 int
172 config_setreset(struct vmd *env, unsigned int reset)
173 {
174 	struct privsep	*ps = &env->vmd_ps;
175 	unsigned int	 id;
176 
177 	DPRINTF("%s: resetting state", __func__);
178 
179 	for (id = 0; id < PROC_MAX; id++) {
180 		if ((reset & ps->ps_what[id]) == 0 ||
181 		    id == privsep_process)
182 			continue;
183 		proc_compose(ps, id, IMSG_CTL_RESET, &reset, sizeof(reset));
184 	}
185 
186 	return (0);
187 }
188 
189 int
190 config_getreset(struct vmd *env, struct imsg *imsg)
191 {
192 	unsigned int	 mode;
193 
194 	IMSG_SIZE_CHECK(imsg, &mode);
195 	memcpy(&mode, imsg->data, sizeof(mode));
196 
197 	log_debug("%s: %s resetting state",
198 	    __func__, env->vmd_ps.ps_title[privsep_process]);
199 
200 	config_purge(env, mode);
201 
202 	return (0);
203 }
204 
205 /*
206  * config_setvm
207  *
208  * Configure a vm, opening any required file descriptors.
209  *
210  * Returns 0 on success, error code on failure.
211  */
212 int
213 config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
214 {
215 	int diskfds[VM_MAX_DISKS_PER_VM][VM_MAX_BASE_PER_DISK];
216 	struct vmd_if		*vif;
217 	struct vmop_create_params *vmc = &vm->vm_params;
218 	struct vm_create_params	*vcp = &vmc->vmc_params;
219 	unsigned int		 i, j;
220 	int			 fd = -1, cdromfd = -1, kernfd = -1;
221 	int			*tapfds = NULL;
222 	int			 n = 0, aflags, oflags, ret = -1;
223 	char			 ifname[IF_NAMESIZE], *s;
224 	char			 path[PATH_MAX], base[PATH_MAX];
225 	unsigned int		 unit;
226 	struct timeval		 tv, rate, since_last;
227 	struct vmop_addr_req	 var;
228 	size_t			 bytes = 0;
229 
230 	if (vm->vm_state & VM_STATE_RUNNING) {
231 		log_warnx("%s: vm is already running", __func__);
232 		return (EALREADY);
233 	}
234 
235 	/*
236 	 * Rate-limit the VM so that it cannot restart in a loop:
237 	 * if the VM restarts after less than VM_START_RATE_SEC seconds,
238 	 * we increment the limit counter.  After VM_START_RATE_LIMIT
239 	 * of suchs fast reboots the VM is stopped.
240 	 */
241 	getmonotime(&tv);
242 	if (vm->vm_start_tv.tv_sec) {
243 		timersub(&tv, &vm->vm_start_tv, &since_last);
244 
245 		rate.tv_sec = VM_START_RATE_SEC;
246 		rate.tv_usec = 0;
247 		if (timercmp(&since_last, &rate, <))
248 			vm->vm_start_limit++;
249 		else {
250 			/* Reset counter */
251 			vm->vm_start_limit = 0;
252 		}
253 
254 		log_debug("%s: vm %u restarted after %lld.%ld seconds,"
255 		    " limit %d/%d", __func__, vcp->vcp_id, since_last.tv_sec,
256 		    since_last.tv_usec, vm->vm_start_limit,
257 		    VM_START_RATE_LIMIT);
258 
259 		if (vm->vm_start_limit >= VM_START_RATE_LIMIT) {
260 			log_warnx("%s: vm %u restarted too quickly",
261 			    __func__, vcp->vcp_id);
262 			return (EPERM);
263 		}
264 	}
265 	vm->vm_start_tv = tv;
266 
267 	for (i = 0; i < VM_MAX_DISKS_PER_VM; i++)
268 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++)
269 			diskfds[i][j] = -1;
270 
271 	tapfds = reallocarray(NULL, vmc->vmc_nnics, sizeof(*tapfds));
272 	if (tapfds == NULL) {
273 		ret = errno;
274 		log_warn("%s: can't allocate tap fds", __func__);
275 		return (ret);
276 	}
277 	for (i = 0; i < vmc->vmc_nnics; i++)
278 		tapfds[i] = -1;
279 
280 	vm->vm_peerid = peerid;
281 	vm->vm_uid = uid;
282 
283 	/*
284 	 * From here onward, all failures need cleanup and use goto fail
285 	 */
286 	if (!(vm->vm_state & VM_STATE_RECEIVED) && vm->vm_kernel == -1) {
287 		if (vm->vm_kernel_path != NULL) {
288 			/* Open external kernel for child */
289 			kernfd = open(vm->vm_kernel_path, O_RDONLY);
290 			if (kernfd == -1) {
291 				ret = errno;
292 				log_warn("%s: can't open kernel or BIOS "
293 				    "boot image %s", __func__,
294 				    vm->vm_kernel_path);
295 				goto fail;
296 			}
297 		}
298 
299 		/*
300 		 * Try to open the default BIOS image if no kernel/BIOS has been
301 		 * specified.  The BIOS is an external firmware file that is
302 		 * typically distributed separately due to an incompatible
303 		 * license.
304 		 */
305 		if (kernfd == -1) {
306 			if ((kernfd = open(VM_DEFAULT_BIOS, O_RDONLY)) == -1) {
307 				log_warn("can't open %s", VM_DEFAULT_BIOS);
308 				ret = VMD_BIOS_MISSING;
309 				goto fail;
310 			}
311 		}
312 
313 		if (vm_checkaccess(kernfd,
314 		    vmc->vmc_checkaccess & VMOP_CREATE_KERNEL,
315 		    uid, R_OK) == -1) {
316 			log_warnx("vm \"%s\" no read access to kernel "
317 			    "%s", vcp->vcp_name, vm->vm_kernel_path);
318 			ret = EPERM;
319 			goto fail;
320 		}
321 
322 		vm->vm_kernel = kernfd;
323 		vmc->vmc_kernel = kernfd;
324 	}
325 
326 	/* Open CDROM image for child */
327 	if (strlen(vmc->vmc_cdrom)) {
328 		/* Stat cdrom to ensure it is a regular file */
329 		if ((cdromfd =
330 		    open(vmc->vmc_cdrom, O_RDONLY)) == -1) {
331 			log_warn("can't open cdrom %s", vmc->vmc_cdrom);
332 			ret = VMD_CDROM_MISSING;
333 			goto fail;
334 		}
335 
336 		if (vm_checkaccess(cdromfd,
337 		    vmc->vmc_checkaccess & VMOP_CREATE_CDROM,
338 		    uid, R_OK) == -1) {
339 			log_warnx("vm \"%s\" no read access to cdrom %s",
340 			    vcp->vcp_name, vmc->vmc_cdrom);
341 			ret = EPERM;
342 			goto fail;
343 		}
344 	}
345 
346 	/* Open disk images for child */
347 	for (i = 0 ; i < vmc->vmc_ndisks; i++) {
348 		if (strlcpy(path, vmc->vmc_disks[i], sizeof(path))
349 		   >= sizeof(path))
350 			log_warnx("disk path %s too long", vmc->vmc_disks[i]);
351 		memset(vmc->vmc_diskbases, 0, sizeof(vmc->vmc_diskbases));
352 		oflags = O_RDWR|O_EXLOCK|O_NONBLOCK;
353 		aflags = R_OK|W_OK;
354 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++) {
355 			/* Stat disk[i] to ensure it is a regular file */
356 			if ((diskfds[i][j] = open(path, oflags)) == -1) {
357 				log_warn("can't open disk %s",
358 				    vmc->vmc_disks[i]);
359 				ret = VMD_DISK_MISSING;
360 				goto fail;
361 			}
362 
363 			if (vm_checkaccess(diskfds[i][j],
364 			    vmc->vmc_checkaccess & VMOP_CREATE_DISK,
365 			    uid, aflags) == -1) {
366 				log_warnx("vm \"%s\" unable to access "
367 				    "disk %s", vcp->vcp_name, path);
368 				errno = EPERM;
369 				goto fail;
370 			}
371 
372 			/*
373 			 * Clear the write and exclusive flags for base images.
374 			 * All writes should go to the top image, allowing them
375 			 * to be shared.
376 			 */
377 			oflags = O_RDONLY|O_NONBLOCK;
378 			aflags = R_OK;
379 			n = virtio_get_base(diskfds[i][j], base, sizeof(base),
380 			    vmc->vmc_disktypes[i], path);
381 			if (n == 0)
382 				break;
383 			if (n == -1) {
384 				log_warnx("vm \"%s\" unable to read "
385 				    "base for disk %s", vcp->vcp_name,
386 				    vmc->vmc_disks[i]);
387 				goto fail;
388 			}
389 			(void)strlcpy(path, base, sizeof(path));
390 		}
391 	}
392 
393 	/* Open network interfaces */
394 	for (i = 0 ; i < vmc->vmc_nnics; i++) {
395 		vif = &vm->vm_ifs[i];
396 
397 		/* Check if the user has requested a specific tap(4) */
398 		s = vmc->vmc_ifnames[i];
399 		if (*s != '\0' && strcmp("tap", s) != 0) {
400 			if (priv_getiftype(s, ifname, &unit) == -1 ||
401 			    strcmp(ifname, "tap") != 0) {
402 				log_warnx("%s: invalid tap name %s",
403 				    __func__, s);
404 				ret = EINVAL;
405 				goto fail;
406 			}
407 		} else
408 			s = NULL;
409 
410 		/*
411 		 * Either open the requested tap(4) device or get
412 		 * the next available one.
413 		 */
414 		if (s != NULL) {
415 			snprintf(path, PATH_MAX, "/dev/%s", s);
416 			tapfds[i] = open(path, O_RDWR | O_NONBLOCK);
417 		} else {
418 			tapfds[i] = opentap(ifname);
419 			s = ifname;
420 		}
421 		if (tapfds[i] == -1) {
422 			log_warnx("%s: can't open tap %s", __func__, s);
423 			goto fail;
424 		}
425 		if ((vif->vif_name = strdup(s)) == NULL) {
426 			log_warn("%s: can't save tap %s", __func__, s);
427 			goto fail;
428 		}
429 
430 		/* Check if the the interface is attached to a switch */
431 		s = vmc->vmc_ifswitch[i];
432 		if (*s != '\0') {
433 			if ((vif->vif_switch = strdup(s)) == NULL) {
434 				log_warn("%s: can't save switch %s",
435 				    __func__, s);
436 				goto fail;
437 			}
438 		}
439 
440 		/* Check if the the interface is assigned to a group */
441 		s = vmc->vmc_ifgroup[i];
442 		if (*s != '\0') {
443 			if ((vif->vif_group = strdup(s)) == NULL) {
444 				log_warn("%s: can't save group %s",
445 				    __func__, s);
446 				goto fail;
447 			}
448 		}
449 
450 		/* non-default rdomain (requires VMIFF_RDOMAIN below) */
451 		vif->vif_rdomain = vmc->vmc_ifrdomain[i];
452 
453 		/* Set the interface status */
454 		vif->vif_flags =
455 		    vmc->vmc_ifflags[i] & (VMIFF_UP|VMIFF_OPTMASK);
456 	}
457 
458 	/* Open TTY */
459 	if (vm->vm_ttyname[0] == '\0') {
460 		if (vm_opentty(vm) == -1) {
461 			log_warn("%s: can't open tty %s", __func__,
462 			    vm->vm_ttyname[0] == '\0' ? "" : vm->vm_ttyname);
463 			goto fail;
464 		}
465 	}
466 	if ((fd = dup(vm->vm_tty)) == -1) {
467 		log_warn("%s: can't re-open tty %s", __func__, vm->vm_ttyname);
468 		goto fail;
469 	}
470 
471 	/* Send VM information */
472 	/* XXX check proc_compose_imsg return values */
473 	if (vm->vm_state & VM_STATE_RECEIVED)
474 		proc_compose_imsg(ps, PROC_VMM, -1,
475 		    IMSG_VMDOP_RECEIVE_VM_REQUEST, vm->vm_vmid, fd, vmc,
476 		    sizeof(struct vmop_create_params));
477 	else
478 		proc_compose_imsg(ps, PROC_VMM, -1,
479 		    IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, vm->vm_kernel,
480 		    vmc, sizeof(*vmc));
481 
482 	if (strlen(vmc->vmc_cdrom))
483 		proc_compose_imsg(ps, PROC_VMM, -1,
484 		    IMSG_VMDOP_START_VM_CDROM, vm->vm_vmid, cdromfd,
485 		    NULL, 0);
486 
487 	for (i = 0; i < vmc->vmc_ndisks; i++) {
488 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++) {
489 			if (diskfds[i][j] == -1)
490 				break;
491 			proc_compose_imsg(ps, PROC_VMM, -1,
492 			    IMSG_VMDOP_START_VM_DISK, vm->vm_vmid,
493 			    diskfds[i][j], &i, sizeof(i));
494 		}
495 	}
496 	for (i = 0; i < vmc->vmc_nnics; i++) {
497 		proc_compose_imsg(ps, PROC_VMM, -1,
498 		    IMSG_VMDOP_START_VM_IF, vm->vm_vmid, tapfds[i],
499 		    &i, sizeof(i));
500 
501 		memset(&var, 0, sizeof(var));
502 		var.var_vmid = vm->vm_vmid;
503 		var.var_nic_idx = i;
504 		proc_compose_imsg(ps, PROC_PRIV, -1, IMSG_VMDOP_PRIV_GET_ADDR,
505 		    vm->vm_vmid, dup(tapfds[i]), &var, sizeof(var));
506 	}
507 
508 	if (!(vm->vm_state & VM_STATE_RECEIVED))
509 		proc_compose_imsg(ps, PROC_VMM, -1,
510 		    IMSG_VMDOP_START_VM_END, vm->vm_vmid, fd, NULL, 0);
511 
512 	free(tapfds);
513 
514 	/* Collapse any memranges after the vm was sent to PROC_VMM */
515 	if (vcp->vcp_nmemranges > 0) {
516 		for (i = 0; i < vcp->vcp_nmemranges; i++)
517 			bytes += vcp->vcp_memranges[i].vmr_size;
518 		memset(&vcp->vcp_memranges, 0, sizeof(vcp->vcp_memranges));
519 		vcp->vcp_nmemranges = 0;
520 		vcp->vcp_memranges[0].vmr_size = bytes;
521 	}
522 	vm->vm_state |= VM_STATE_RUNNING;
523 	return (0);
524 
525  fail:
526 	log_warnx("failed to start vm %s", vcp->vcp_name);
527 
528 	if (vm->vm_kernel != -1)
529 		close(kernfd);
530 	if (cdromfd != -1)
531 		close(cdromfd);
532 	for (i = 0; i < vmc->vmc_ndisks; i++)
533 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++)
534 			if (diskfds[i][j] != -1)
535 				close(diskfds[i][j]);
536 	if (tapfds != NULL) {
537 		for (i = 0; i < vmc->vmc_nnics; i++)
538 			close(tapfds[i]);
539 		free(tapfds);
540 	}
541 
542 	if (vm->vm_from_config) {
543 		vm_stop(vm, 0, __func__);
544 	} else {
545 		vm_remove(vm, __func__);
546 	}
547 
548 	return (ret);
549 }
550 
551 int
552 config_getvm(struct privsep *ps, struct imsg *imsg)
553 {
554 	struct vmop_create_params	 vmc;
555 	struct vmd_vm			*vm = NULL;
556 
557 	IMSG_SIZE_CHECK(imsg, &vmc);
558 	memcpy(&vmc, imsg->data, sizeof(vmc));
559 	vmc.vmc_kernel = imsg->fd;
560 
561 	errno = 0;
562 	if (vm_register(ps, &vmc, &vm, imsg->hdr.peerid, 0) == -1)
563 		goto fail;
564 
565 	vm->vm_state |= VM_STATE_RUNNING;
566 	vm->vm_peerid = (uint32_t)-1;
567 	vm->vm_kernel = imsg->fd;
568 	return (0);
569 
570  fail:
571 	if (imsg->fd != -1) {
572 		close(imsg->fd);
573 		imsg->fd = -1;
574 	}
575 
576 	vm_remove(vm, __func__);
577 	if (errno == 0)
578 		errno = EINVAL;
579 
580 	return (-1);
581 }
582 
583 int
584 config_getdisk(struct privsep *ps, struct imsg *imsg)
585 {
586 	struct vmd_vm	*vm;
587 	unsigned int	 n, idx;
588 
589 	errno = 0;
590 	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
591 		errno = ENOENT;
592 		return (-1);
593 	}
594 
595 	IMSG_SIZE_CHECK(imsg, &n);
596 	memcpy(&n, imsg->data, sizeof(n));
597 
598 	if (n >= vm->vm_params.vmc_ndisks || imsg->fd == -1) {
599 		log_warnx("invalid disk id");
600 		errno = EINVAL;
601 		return (-1);
602 	}
603 	idx = vm->vm_params.vmc_diskbases[n]++;
604 	if (idx >= VM_MAX_BASE_PER_DISK) {
605 		log_warnx("too many bases for disk");
606 		errno = EINVAL;
607 		return (-1);
608 	}
609 	vm->vm_disks[n][idx] = imsg->fd;
610 	return (0);
611 }
612 
613 int
614 config_getif(struct privsep *ps, struct imsg *imsg)
615 {
616 	struct vmd_vm	*vm;
617 	unsigned int	 n;
618 
619 	errno = 0;
620 	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
621 		errno = ENOENT;
622 		return (-1);
623 	}
624 
625 	IMSG_SIZE_CHECK(imsg, &n);
626 	memcpy(&n, imsg->data, sizeof(n));
627 	if (n >= vm->vm_params.vmc_nnics ||
628 	    vm->vm_ifs[n].vif_fd != -1 || imsg->fd == -1) {
629 		log_warnx("invalid interface id");
630 		goto fail;
631 	}
632 	vm->vm_ifs[n].vif_fd = imsg->fd;
633 	return (0);
634  fail:
635 	if (imsg->fd != -1)
636 		close(imsg->fd);
637 	errno = EINVAL;
638 	return (-1);
639 }
640 
641 int
642 config_getcdrom(struct privsep *ps, struct imsg *imsg)
643 {
644 	struct vmd_vm	*vm;
645 
646 	errno = 0;
647 	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
648 		errno = ENOENT;
649 		return (-1);
650 	}
651 
652 	if (imsg->fd == -1) {
653 		log_warnx("invalid cdrom id");
654 		goto fail;
655 	}
656 
657 	vm->vm_cdrom = imsg->fd;
658 	return (0);
659  fail:
660 	if (imsg->fd != -1)
661 		close(imsg->fd);
662 	errno = EINVAL;
663 	return (-1);
664 }
665