xref: /openbsd/usr.sbin/vmd/config.c (revision 2d545756)
1 /*	$OpenBSD: config.c,v 1.63 2021/07/18 11:55:45 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[] = { "switch", "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 	if (what & CONFIG_USERS) {
102 		if ((env->vmd_users = calloc(1,
103 		    sizeof(*env->vmd_users))) == NULL)
104 			return (-1);
105 		TAILQ_INIT(env->vmd_users);
106 	}
107 
108 	return (0);
109 }
110 
111 void
112 config_purge(struct vmd *env, unsigned int reset)
113 {
114 	struct privsep		*ps = &env->vmd_ps;
115 	struct name2id		*n2i;
116 	struct vmd_vm		*vm;
117 	struct vmd_switch	*vsw;
118 	unsigned int		 what;
119 
120 	DPRINTF("%s: %s purging vms and switches",
121 	    __func__, ps->ps_title[privsep_process]);
122 
123 	/* Reset global configuration (prefix was verified before) */
124 	config_init_localprefix(&env->vmd_cfg);
125 
126 	/* Reset other configuration */
127 	what = ps->ps_what[privsep_process] & reset;
128 	if (what & CONFIG_VMS && env->vmd_vms != NULL) {
129 		while ((vm = TAILQ_FIRST(env->vmd_vms)) != NULL) {
130 			vm_remove(vm, __func__);
131 		}
132 		while ((n2i = TAILQ_FIRST(env->vmd_known)) != NULL) {
133 			TAILQ_REMOVE(env->vmd_known, n2i, entry);
134 			free(n2i);
135 		}
136 		env->vmd_nvm = 0;
137 	}
138 	if (what & CONFIG_SWITCHES && env->vmd_switches != NULL) {
139 		while ((vsw = TAILQ_FIRST(env->vmd_switches)) != NULL)
140 			switch_remove(vsw);
141 		env->vmd_nswitches = 0;
142 	}
143 }
144 
145 int
146 config_setconfig(struct vmd *env)
147 {
148 	struct privsep	*ps = &env->vmd_ps;
149 	unsigned int	 id;
150 
151 	DPRINTF("%s: setting config", __func__);
152 
153 	for (id = 0; id < PROC_MAX; id++) {
154 		if (id == privsep_process)
155 			continue;
156 		proc_compose(ps, id, IMSG_VMDOP_CONFIG, &env->vmd_cfg,
157 		    sizeof(env->vmd_cfg));
158 	}
159 
160 	return (0);
161 }
162 
163 int
164 config_getconfig(struct vmd *env, struct imsg *imsg)
165 {
166 	struct privsep	*ps = &env->vmd_ps;
167 
168 	log_debug("%s: %s retrieving config",
169 	    __func__, ps->ps_title[privsep_process]);
170 
171 	IMSG_SIZE_CHECK(imsg, &env->vmd_cfg);
172 	memcpy(&env->vmd_cfg, imsg->data, sizeof(env->vmd_cfg));
173 
174 	return (0);
175 }
176 
177 int
178 config_setreset(struct vmd *env, unsigned int reset)
179 {
180 	struct privsep	*ps = &env->vmd_ps;
181 	unsigned int	 id;
182 
183 	DPRINTF("%s: resetting state", __func__);
184 
185 	for (id = 0; id < PROC_MAX; id++) {
186 		if ((reset & ps->ps_what[id]) == 0 ||
187 		    id == privsep_process)
188 			continue;
189 		proc_compose(ps, id, IMSG_CTL_RESET, &reset, sizeof(reset));
190 	}
191 
192 	return (0);
193 }
194 
195 int
196 config_getreset(struct vmd *env, struct imsg *imsg)
197 {
198 	unsigned int	 mode;
199 
200 	IMSG_SIZE_CHECK(imsg, &mode);
201 	memcpy(&mode, imsg->data, sizeof(mode));
202 
203 	log_debug("%s: %s resetting state",
204 	    __func__, env->vmd_ps.ps_title[privsep_process]);
205 
206 	config_purge(env, mode);
207 
208 	return (0);
209 }
210 
211 /*
212  * config_setvm
213  *
214  * Configure a vm, opening any required file descriptors.
215  *
216  * Returns 0 on success, error code on failure.
217  */
218 int
219 config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
220 {
221 	int diskfds[VMM_MAX_DISKS_PER_VM][VM_MAX_BASE_PER_DISK];
222 	struct vmd_if		*vif;
223 	struct vmop_create_params *vmc = &vm->vm_params;
224 	struct vm_create_params	*vcp = &vmc->vmc_params;
225 	unsigned int		 i, j;
226 	int			 fd = -1, cdromfd = -1, kernfd = -1;
227 	int			*tapfds = NULL;
228 	int			 n = 0, aflags, oflags, ret = -1;
229 	char			 ifname[IF_NAMESIZE], *s;
230 	char			 path[PATH_MAX], base[PATH_MAX];
231 	unsigned int		 unit;
232 	struct timeval		 tv, rate, since_last;
233 	struct vmop_addr_req	 var;
234 
235 	if (vm->vm_state & VM_STATE_RUNNING) {
236 		log_warnx("%s: vm is already running", __func__);
237 		return (EALREADY);
238 	}
239 
240 	/* increase the user reference counter and check user limits */
241 	if (vm->vm_user != NULL && user_get(vm->vm_user->usr_id.uid) != NULL) {
242 		user_inc(vcp, vm->vm_user, 1);
243 		if (user_checklimit(vm->vm_user, vcp) == -1)
244 			return (EPERM);
245 	}
246 
247 	/*
248 	 * Rate-limit the VM so that it cannot restart in a loop:
249 	 * if the VM restarts after less than VM_START_RATE_SEC seconds,
250 	 * we increment the limit counter.  After VM_START_RATE_LIMIT
251 	 * of suchs fast reboots the VM is stopped.
252 	 */
253 	getmonotime(&tv);
254 	if (vm->vm_start_tv.tv_sec) {
255 		timersub(&tv, &vm->vm_start_tv, &since_last);
256 
257 		rate.tv_sec = VM_START_RATE_SEC;
258 		rate.tv_usec = 0;
259 		if (timercmp(&since_last, &rate, <))
260 			vm->vm_start_limit++;
261 		else {
262 			/* Reset counter */
263 			vm->vm_start_limit = 0;
264 		}
265 
266 		log_debug("%s: vm %u restarted after %lld.%ld seconds,"
267 		    " limit %d/%d", __func__, vcp->vcp_id, since_last.tv_sec,
268 		    since_last.tv_usec, vm->vm_start_limit,
269 		    VM_START_RATE_LIMIT);
270 
271 		if (vm->vm_start_limit >= VM_START_RATE_LIMIT) {
272 			log_warnx("%s: vm %u restarted too quickly",
273 			    __func__, vcp->vcp_id);
274 			return (EPERM);
275 		}
276 	}
277 	vm->vm_start_tv = tv;
278 
279 	for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++)
280 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++)
281 			diskfds[i][j] = -1;
282 
283 	tapfds = reallocarray(NULL, vcp->vcp_nnics, sizeof(*tapfds));
284 	if (tapfds == NULL) {
285 		ret = errno;
286 		log_warn("%s: can't allocate tap fds", __func__);
287 		return (ret);
288 	}
289 	for (i = 0; i < vcp->vcp_nnics; i++)
290 		tapfds[i] = -1;
291 
292 	vm->vm_peerid = peerid;
293 	vm->vm_uid = uid;
294 
295 	/*
296 	 * From here onward, all failures need cleanup and use goto fail
297 	 */
298 
299 	if (!(vm->vm_state & VM_STATE_RECEIVED)) {
300 		if (strlen(vcp->vcp_kernel)) {
301 			/* Open external kernel for child */
302 			if ((kernfd = open(vcp->vcp_kernel, O_RDONLY)) == -1) {
303 				ret = errno;
304 				log_warn("%s: can't open kernel or BIOS "
305 				    "boot image %s", __func__, vcp->vcp_kernel);
306 				goto fail;
307 			}
308 		}
309 
310 		/*
311 		 * Try to open the default BIOS image if no kernel/BIOS has been
312 		 * specified.  The BIOS is an external firmware file that is
313 		 * typically distributed separately due to an incompatible
314 		 * license.
315 		 */
316 		if (kernfd == -1 &&
317 		    (kernfd = open(VM_DEFAULT_BIOS, O_RDONLY)) == -1) {
318 			log_warn("can't open %s", VM_DEFAULT_BIOS);
319 			ret = VMD_BIOS_MISSING;
320 			goto fail;
321 		}
322 
323 		if (vm_checkaccess(kernfd,
324 		    vmc->vmc_checkaccess & VMOP_CREATE_KERNEL,
325 		    uid, R_OK) == -1) {
326 			log_warnx("vm \"%s\" no read access to kernel %s",
327 			    vcp->vcp_name, vcp->vcp_kernel);
328 			ret = EPERM;
329 			goto fail;
330 		}
331 	}
332 
333 	/* Open CDROM image for child */
334 	if (strlen(vcp->vcp_cdrom)) {
335 		/* Stat cdrom to ensure it is a regular file */
336 		if ((cdromfd =
337 		    open(vcp->vcp_cdrom, O_RDONLY)) == -1) {
338 			log_warn("can't open cdrom %s", vcp->vcp_cdrom);
339 			ret = VMD_CDROM_MISSING;
340 			goto fail;
341 		}
342 
343 		if (vm_checkaccess(cdromfd,
344 		    vmc->vmc_checkaccess & VMOP_CREATE_CDROM,
345 		    uid, R_OK) == -1) {
346 			log_warnx("vm \"%s\" no read access to cdrom %s",
347 			    vcp->vcp_name, vcp->vcp_cdrom);
348 			ret = EPERM;
349 			goto fail;
350 		}
351 	}
352 
353 	/* Open disk images for child */
354 	for (i = 0 ; i < vcp->vcp_ndisks; i++) {
355 		if (strlcpy(path, vcp->vcp_disks[i], sizeof(path))
356 		   >= sizeof(path))
357 			log_warnx("disk path %s too long", vcp->vcp_disks[i]);
358 		memset(vmc->vmc_diskbases, 0, sizeof(vmc->vmc_diskbases));
359 		oflags = O_RDWR|O_EXLOCK|O_NONBLOCK;
360 		aflags = R_OK|W_OK;
361 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++) {
362 			/* Stat disk[i] to ensure it is a regular file */
363 			if ((diskfds[i][j] = open(path, oflags)) == -1) {
364 				log_warn("can't open disk %s",
365 				    vcp->vcp_disks[i]);
366 				ret = VMD_DISK_MISSING;
367 				goto fail;
368 			}
369 
370 			if (vm_checkaccess(diskfds[i][j],
371 			    vmc->vmc_checkaccess & VMOP_CREATE_DISK,
372 			    uid, aflags) == -1) {
373 				log_warnx("vm \"%s\" unable to access "
374 				    "disk %s", vcp->vcp_name, path);
375 				errno = EPERM;
376 				goto fail;
377 			}
378 
379 			/*
380 			 * Clear the write and exclusive flags for base images.
381 			 * All writes should go to the top image, allowing them
382 			 * to be shared.
383 			 */
384 			oflags = O_RDONLY|O_NONBLOCK;
385 			aflags = R_OK;
386 			n = virtio_get_base(diskfds[i][j], base, sizeof(base),
387 			    vmc->vmc_disktypes[i], path);
388 			if (n == 0)
389 				break;
390 			if (n == -1) {
391 				log_warnx("vm \"%s\" unable to read "
392 				    "base for disk %s", vcp->vcp_name,
393 				    vcp->vcp_disks[i]);
394 				goto fail;
395 			}
396 			(void)strlcpy(path, base, sizeof(path));
397 		}
398 	}
399 
400 	/* Open network interfaces */
401 	for (i = 0 ; i < vcp->vcp_nnics; i++) {
402 		vif = &vm->vm_ifs[i];
403 
404 		/* Check if the user has requested a specific tap(4) */
405 		s = vmc->vmc_ifnames[i];
406 		if (*s != '\0' && strcmp("tap", s) != 0) {
407 			if (priv_getiftype(s, ifname, &unit) == -1 ||
408 			    strcmp(ifname, "tap") != 0) {
409 				log_warnx("%s: invalid tap name %s",
410 				    __func__, s);
411 				ret = EINVAL;
412 				goto fail;
413 			}
414 		} else
415 			s = NULL;
416 
417 		/*
418 		 * Either open the requested tap(4) device or get
419 		 * the next available one.
420 		 */
421 		if (s != NULL) {
422 			snprintf(path, PATH_MAX, "/dev/%s", s);
423 			tapfds[i] = open(path, O_RDWR | O_NONBLOCK);
424 		} else {
425 			tapfds[i] = opentap(ifname);
426 			s = ifname;
427 		}
428 		if (tapfds[i] == -1) {
429 			log_warnx("%s: can't open tap %s", __func__, s);
430 			goto fail;
431 		}
432 		if ((vif->vif_name = strdup(s)) == NULL) {
433 			log_warn("%s: can't save tap %s", __func__, s);
434 			goto fail;
435 		}
436 
437 		/* Check if the the interface is attached to a switch */
438 		s = vmc->vmc_ifswitch[i];
439 		if (*s != '\0') {
440 			if ((vif->vif_switch = strdup(s)) == NULL) {
441 				log_warn("%s: can't save switch %s",
442 				    __func__, s);
443 				goto fail;
444 			}
445 		}
446 
447 		/* Check if the the interface is assigned to a group */
448 		s = vmc->vmc_ifgroup[i];
449 		if (*s != '\0') {
450 			if ((vif->vif_group = strdup(s)) == NULL) {
451 				log_warn("%s: can't save group %s",
452 				    __func__, s);
453 				goto fail;
454 			}
455 		}
456 
457 		/* non-default rdomain (requires VMIFF_RDOMAIN below) */
458 		vif->vif_rdomain = vmc->vmc_ifrdomain[i];
459 
460 		/* Set the interface status */
461 		vif->vif_flags =
462 		    vmc->vmc_ifflags[i] & (VMIFF_UP|VMIFF_OPTMASK);
463 	}
464 
465 	/* Open TTY */
466 	if (vm->vm_ttyname == NULL) {
467 		if (vm_opentty(vm) == -1) {
468 			log_warn("%s: can't open tty %s", __func__,
469 			    vm->vm_ttyname == NULL ? "" : vm->vm_ttyname);
470 			goto fail;
471 		}
472 	}
473 	if ((fd = dup(vm->vm_tty)) == -1) {
474 		log_warn("%s: can't re-open tty %s", __func__, vm->vm_ttyname);
475 		goto fail;
476 	}
477 
478 	/* Send VM information */
479 	/* XXX check proc_compose_imsg return values */
480 	if (vm->vm_state & VM_STATE_RECEIVED)
481 		proc_compose_imsg(ps, PROC_VMM, -1,
482 		    IMSG_VMDOP_RECEIVE_VM_REQUEST, vm->vm_vmid, fd,  vmc,
483 		    sizeof(struct vmop_create_params));
484 	else
485 		proc_compose_imsg(ps, PROC_VMM, -1,
486 		    IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, kernfd,
487 		    vmc, sizeof(*vmc));
488 
489 	if (strlen(vcp->vcp_cdrom))
490 		proc_compose_imsg(ps, PROC_VMM, -1,
491 		    IMSG_VMDOP_START_VM_CDROM, vm->vm_vmid, cdromfd,
492 		    NULL, 0);
493 
494 	for (i = 0; i < vcp->vcp_ndisks; i++) {
495 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++) {
496 			if (diskfds[i][j] == -1)
497 				break;
498 			proc_compose_imsg(ps, PROC_VMM, -1,
499 			    IMSG_VMDOP_START_VM_DISK, vm->vm_vmid,
500 			    diskfds[i][j], &i, sizeof(i));
501 		}
502 	}
503 	for (i = 0; i < vcp->vcp_nnics; i++) {
504 		proc_compose_imsg(ps, PROC_VMM, -1,
505 		    IMSG_VMDOP_START_VM_IF, vm->vm_vmid, tapfds[i],
506 		    &i, sizeof(i));
507 
508 		memset(&var, 0, sizeof(var));
509 		var.var_vmid = vm->vm_vmid;
510 		var.var_nic_idx = i;
511 		proc_compose_imsg(ps, PROC_PRIV, -1, IMSG_VMDOP_PRIV_GET_ADDR,
512 		    vm->vm_vmid, dup(tapfds[i]), &var, sizeof(var));
513 	}
514 
515 	if (!(vm->vm_state & VM_STATE_RECEIVED))
516 		proc_compose_imsg(ps, PROC_VMM, -1,
517 		    IMSG_VMDOP_START_VM_END, vm->vm_vmid, fd,  NULL, 0);
518 
519 	free(tapfds);
520 
521 	vm->vm_state |= VM_STATE_RUNNING;
522 	return (0);
523 
524  fail:
525 	log_warnx("failed to start vm %s", vcp->vcp_name);
526 
527 	if (kernfd != -1)
528 		close(kernfd);
529 	if (cdromfd != -1)
530 		close(cdromfd);
531 	for (i = 0; i < vcp->vcp_ndisks; i++)
532 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++)
533 			if (diskfds[i][j] != -1)
534 				close(diskfds[i][j]);
535 	if (tapfds != NULL) {
536 		for (i = 0; i < vcp->vcp_nnics; i++)
537 			close(tapfds[i]);
538 		free(tapfds);
539 	}
540 
541 	if (vm->vm_from_config) {
542 		vm_stop(vm, 0, __func__);
543 	} else {
544 		vm_remove(vm, __func__);
545 	}
546 
547 	return (ret);
548 }
549 
550 int
551 config_getvm(struct privsep *ps, struct imsg *imsg)
552 {
553 	struct vmop_create_params	 vmc;
554 	struct vmd_vm			*vm;
555 
556 	IMSG_SIZE_CHECK(imsg, &vmc);
557 	memcpy(&vmc, imsg->data, sizeof(vmc));
558 
559 	errno = 0;
560 	if (vm_register(ps, &vmc, &vm, imsg->hdr.peerid, 0) == -1)
561 		goto fail;
562 
563 	/* If the fd is -1, the kernel will be searched on the disk */
564 	vm->vm_kernel = imsg->fd;
565 	vm->vm_state |= VM_STATE_RUNNING;
566 	vm->vm_peerid = (uint32_t)-1;
567 
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_params.vcp_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_params.vcp_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