GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmd/config.c Lines: 0 229 0.0 %
Date: 2017-11-07 Branches: 0 150 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: config.c,v 1.36 2017/09/14 10:07:17 reyk 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", NULL };
44
45
46
int
47
config_init(struct vmd *env)
48
{
49
	struct privsep	*ps = &env->vmd_ps;
50
	unsigned int	 what;
51
52
	/* Global configuration */
53
	ps->ps_what[PROC_PARENT] = CONFIG_ALL;
54
	ps->ps_what[PROC_VMM] = CONFIG_VMS;
55
56
	if (host(VMD_DHCP_PREFIX, &env->vmd_cfg.cfg_localprefix) == -1)
57
		return (-1);
58
59
	/* Other configuration */
60
	what = ps->ps_what[privsep_process];
61
	if (what & CONFIG_VMS) {
62
		if ((env->vmd_vms = calloc(1, sizeof(*env->vmd_vms))) == NULL)
63
			return (-1);
64
		TAILQ_INIT(env->vmd_vms);
65
	}
66
	if (what & CONFIG_SWITCHES) {
67
		if ((env->vmd_switches = calloc(1,
68
		    sizeof(*env->vmd_switches))) == NULL)
69
			return (-1);
70
		TAILQ_INIT(env->vmd_switches);
71
	}
72
73
	return (0);
74
}
75
76
void
77
config_purge(struct vmd *env, unsigned int reset)
78
{
79
	struct privsep		*ps = &env->vmd_ps;
80
	struct vmd_vm		*vm;
81
	struct vmd_switch	*vsw;
82
	unsigned int		 what;
83
84
	log_debug("%s: purging vms and switches from running config",
85
	    __func__);
86
	/* Reset global configuration (prefix was verified before) */
87
	(void)host(VMD_DHCP_PREFIX, &env->vmd_cfg.cfg_localprefix);
88
89
	/* Reset other configuration */
90
	what = ps->ps_what[privsep_process] & reset;
91
	if (what & CONFIG_VMS && env->vmd_vms != NULL) {
92
		while ((vm = TAILQ_FIRST(env->vmd_vms)) != NULL) {
93
			log_debug("%s: calling vm_remove", __func__);
94
			vm_remove(vm);
95
		}
96
		env->vmd_nvm = 0;
97
	}
98
	if (what & CONFIG_SWITCHES && env->vmd_switches != NULL) {
99
		while ((vsw = TAILQ_FIRST(env->vmd_switches)) != NULL)
100
			switch_remove(vsw);
101
		env->vmd_nswitches = 0;
102
	}
103
}
104
105
int
106
config_setconfig(struct vmd *env)
107
{
108
	struct privsep	*ps = &env->vmd_ps;
109
	unsigned int	 id;
110
111
	log_debug("%s: setting config", __func__);
112
	for (id = 0; id < PROC_MAX; id++) {
113
		if (id == privsep_process)
114
			continue;
115
		proc_compose(ps, id, IMSG_VMDOP_CONFIG, &env->vmd_cfg,
116
		    sizeof(env->vmd_cfg));
117
	}
118
119
	return (0);
120
}
121
122
int
123
config_getconfig(struct vmd *env, struct imsg *imsg)
124
{
125
	log_debug("%s: retrieving config", __func__);
126
	IMSG_SIZE_CHECK(imsg, &env->vmd_cfg);
127
	memcpy(&env->vmd_cfg, imsg->data, sizeof(env->vmd_cfg));
128
129
	return (0);
130
}
131
132
int
133
config_setreset(struct vmd *env, unsigned int reset)
134
{
135
	struct privsep	*ps = &env->vmd_ps;
136
	unsigned int	 id;
137
138
	log_debug("%s: resetting state", __func__);
139
	for (id = 0; id < PROC_MAX; id++) {
140
		if ((reset & ps->ps_what[id]) == 0 ||
141
		    id == privsep_process)
142
			continue;
143
		proc_compose(ps, id, IMSG_CTL_RESET, &reset, sizeof(reset));
144
	}
145
146
	return (0);
147
}
148
149
int
150
config_getreset(struct vmd *env, struct imsg *imsg)
151
{
152
	unsigned int	 mode;
153
154
	IMSG_SIZE_CHECK(imsg, &mode);
155
	memcpy(&mode, imsg->data, sizeof(mode));
156
157
	log_debug("%s: resetting state", __func__);
158
	config_purge(env, mode);
159
160
	return (0);
161
}
162
163
int
164
config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
165
{
166
	struct vmd_if		*vif;
167
	struct vmop_create_params *vmc = &vm->vm_params;
168
	struct vm_create_params	*vcp = &vmc->vmc_params;
169
	struct stat		 stat_buf;
170
	unsigned int		 i;
171
	int			 fd = -1, vmboot = 0;
172
	int			 kernfd = -1, *diskfds = NULL, *tapfds = NULL;
173
	int			 saved_errno = 0;
174
	char			 ifname[IF_NAMESIZE], *s;
175
	char			 path[PATH_MAX];
176
	unsigned int		 unit;
177
178
	errno = 0;
179
180
	if (vm->vm_running) {
181
		log_warnx("%s: vm is already running", __func__);
182
		errno = EALREADY;
183
		goto fail;
184
	}
185
186
	diskfds = reallocarray(NULL, vcp->vcp_ndisks, sizeof(*diskfds));
187
	if (diskfds == NULL) {
188
		log_warn("%s: can't allocate disk fds", __func__);
189
		goto fail;
190
	}
191
	for (i = 0; i < vcp->vcp_ndisks; i++)
192
		diskfds[i] = -1;
193
194
	tapfds = reallocarray(NULL, vcp->vcp_nnics, sizeof(*tapfds));
195
	if (tapfds == NULL) {
196
		log_warn("%s: can't allocate tap fds", __func__);
197
		goto fail;
198
	}
199
	for (i = 0; i < vcp->vcp_nnics; i++)
200
		tapfds[i] = -1;
201
202
	vm->vm_peerid = peerid;
203
	vm->vm_uid = uid;
204
205
	if (!vm->vm_received) {
206
		if (strlen(vcp->vcp_kernel)) {
207
			/*
208
			 * Boot kernel from disk image if path matches the
209
			 * root disk.
210
			 */
211
			if (vcp->vcp_ndisks &&
212
			    strcmp(vcp->vcp_kernel, vcp->vcp_disks[0]) == 0)
213
				vmboot = 1;
214
			/* Open external kernel for child */
215
			else if ((kernfd =
216
			    open(vcp->vcp_kernel, O_RDONLY)) == -1) {
217
				log_warn("%s: can't open kernel or BIOS "
218
				    "boot image %s", __func__, vcp->vcp_kernel);
219
				goto fail;
220
			}
221
		}
222
223
		/*
224
		 * Try to open the default BIOS image if no kernel/BIOS has been
225
		 * specified.  The BIOS is an external firmware file that is
226
		 * typically distributed separately due to an incompatible
227
		 * license.
228
		 */
229
		if (kernfd == -1 && !vmboot &&
230
		    (kernfd = open(VM_DEFAULT_BIOS, O_RDONLY)) == -1) {
231
			log_warn("%s: can't open %s", __func__,
232
			    VM_DEFAULT_BIOS);
233
			errno = VMD_BIOS_MISSING;
234
			goto fail;
235
		}
236
	}
237
238
	/* Open disk images for child */
239
	for (i = 0 ; i < vcp->vcp_ndisks; i++) {
240
                /* Stat disk[i] to ensure it is a regular file */
241
		if (stat(vcp->vcp_disks[i], &stat_buf) == -1) {
242
			log_warn("%s: can't open disk %s", __func__,
243
			    vcp->vcp_disks[i]);
244
			errno = VMD_DISK_MISSING;
245
			goto fail;
246
		}
247
		if (S_ISREG(stat_buf.st_mode) == 0) {
248
			log_warn("%s: disk %s is not a regular file", __func__,
249
			    vcp->vcp_disks[i]);
250
			errno = VMD_DISK_INVALID;
251
			goto fail;
252
		}
253
		if ((diskfds[i] =
254
		    open(vcp->vcp_disks[i], O_RDWR)) == -1) {
255
			log_warn("%s: can't open disk %s", __func__,
256
			    vcp->vcp_disks[i]);
257
			errno = VMD_DISK_MISSING;
258
			goto fail;
259
		}
260
	}
261
262
	/* Open network interfaces */
263
	for (i = 0 ; i < vcp->vcp_nnics; i++) {
264
		vif = &vm->vm_ifs[i];
265
266
		/* Check if the user has requested a specific tap(4) */
267
		s = vmc->vmc_ifnames[i];
268
		if (*s != '\0' && strcmp("tap", s) != 0) {
269
			if (priv_getiftype(s, ifname, &unit) == -1 ||
270
			    strcmp(ifname, "tap") != 0) {
271
				log_warnx("%s: invalid tap name %s",
272
				    __func__, s);
273
				errno = EINVAL;
274
				goto fail;
275
			}
276
		} else
277
			s = NULL;
278
279
		/*
280
		 * Either open the requested tap(4) device or get
281
		 * the next available one.
282
		 */
283
		if (s != NULL) {
284
			snprintf(path, PATH_MAX, "/dev/%s", s);
285
			tapfds[i] = open(path, O_RDWR | O_NONBLOCK);
286
		} else {
287
			tapfds[i] = opentap(ifname);
288
			s = ifname;
289
		}
290
		if (tapfds[i] == -1) {
291
			log_warn("%s: can't open tap %s", __func__, s);
292
			goto fail;
293
		}
294
		if ((vif->vif_name = strdup(s)) == NULL) {
295
			log_warn("%s: can't save tap %s", __func__, s);
296
			goto fail;
297
		}
298
299
		/* Check if the the interface is attached to a switch */
300
		s = vmc->vmc_ifswitch[i];
301
		if (*s != '\0') {
302
			if ((vif->vif_switch = strdup(s)) == NULL) {
303
				log_warn("%s: can't save switch %s",
304
				    __func__, s);
305
				goto fail;
306
			}
307
		}
308
309
		/* Check if the the interface is assigned to a group */
310
		s = vmc->vmc_ifgroup[i];
311
		if (*s != '\0') {
312
			if ((vif->vif_group = strdup(s)) == NULL) {
313
				log_warn("%s: can't save group %s",
314
				    __func__, s);
315
				goto fail;
316
			}
317
		}
318
319
		/* non-default rdomain (requires VMIFF_RDOMAIN below) */
320
		vif->vif_rdomain = vmc->vmc_ifrdomain[i];
321
322
		/* Set the interface status */
323
		vif->vif_flags =
324
		    vmc->vmc_ifflags[i] & (VMIFF_UP|VMIFF_OPTMASK);
325
	}
326
327
	/* Open TTY */
328
	if (vm->vm_ttyname == NULL) {
329
		if (vm_opentty(vm) == -1) {
330
			log_warn("%s: can't open tty %s", __func__,
331
			    vm->vm_ttyname == NULL ? "" : vm->vm_ttyname);
332
			goto fail;
333
		}
334
	}
335
	if ((fd = dup(vm->vm_tty)) == -1) {
336
		log_warn("%s: can't re-open tty %s", __func__, vm->vm_ttyname);
337
		goto fail;
338
	}
339
340
	/* Send VM information */
341
	if (vm->vm_received)
342
		proc_compose_imsg(ps, PROC_VMM, -1,
343
		    IMSG_VMDOP_RECEIVE_VM_REQUEST, vm->vm_vmid, fd,  vmc,
344
		    sizeof(struct vmop_create_params));
345
	else
346
		proc_compose_imsg(ps, PROC_VMM, -1,
347
		    IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, kernfd,
348
		    vmc, sizeof(*vmc));
349
	for (i = 0; i < vcp->vcp_ndisks; i++) {
350
		proc_compose_imsg(ps, PROC_VMM, -1,
351
		    IMSG_VMDOP_START_VM_DISK, vm->vm_vmid, diskfds[i],
352
		    &i, sizeof(i));
353
	}
354
	for (i = 0; i < vcp->vcp_nnics; i++) {
355
		proc_compose_imsg(ps, PROC_VMM, -1,
356
		    IMSG_VMDOP_START_VM_IF, vm->vm_vmid, tapfds[i],
357
		    &i, sizeof(i));
358
	}
359
360
	if (!vm->vm_received)
361
		proc_compose_imsg(ps, PROC_VMM, -1,
362
		    IMSG_VMDOP_START_VM_END, vm->vm_vmid, fd,  NULL, 0);
363
364
	free(diskfds);
365
	free(tapfds);
366
367
	vm->vm_running = 1;
368
	return (0);
369
370
 fail:
371
	saved_errno = errno;
372
	log_warnx("%s: failed to start vm %s", __func__, vcp->vcp_name);
373
374
	if (kernfd != -1)
375
		close(kernfd);
376
	if (diskfds != NULL) {
377
		for (i = 0; i < vcp->vcp_ndisks; i++)
378
			close(diskfds[i]);
379
		free(diskfds);
380
	}
381
	if (tapfds != NULL) {
382
		for (i = 0; i < vcp->vcp_nnics; i++)
383
			close(tapfds[i]);
384
		free(tapfds);
385
	}
386
387
	log_debug("%s: calling vm_remove", __func__);
388
	vm_remove(vm);
389
	errno = saved_errno;
390
	if (errno == 0)
391
		errno = EINVAL;
392
	return (-1);
393
}
394
395
int
396
config_getvm(struct privsep *ps, struct imsg *imsg)
397
{
398
	struct vmop_create_params	 vmc;
399
	struct vmd_vm			*vm;
400
401
	IMSG_SIZE_CHECK(imsg, &vmc);
402
	memcpy(&vmc, imsg->data, sizeof(vmc));
403
404
	errno = 0;
405
	if (vm_register(ps, &vmc, &vm, imsg->hdr.peerid, 0) == -1)
406
		goto fail;
407
408
	/* If the fd is -1, the kernel will be searched on the disk */
409
	vm->vm_kernel = imsg->fd;
410
	vm->vm_running = 1;
411
412
	return (0);
413
414
 fail:
415
	if (imsg->fd != -1) {
416
		close(imsg->fd);
417
		imsg->fd = -1;
418
	}
419
420
	log_debug("%s: calling vm_remove", __func__);
421
	vm_remove(vm);
422
	if (errno == 0)
423
		errno = EINVAL;
424
425
	return (-1);
426
}
427
428
int
429
config_getdisk(struct privsep *ps, struct imsg *imsg)
430
{
431
	struct vmd_vm	*vm;
432
	unsigned int	 n;
433
434
	errno = 0;
435
	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
436
		errno = ENOENT;
437
		return (-1);
438
	}
439
440
	IMSG_SIZE_CHECK(imsg, &n);
441
	memcpy(&n, imsg->data, sizeof(n));
442
443
	if (n >= vm->vm_params.vmc_params.vcp_ndisks ||
444
	    vm->vm_disks[n] != -1 || imsg->fd == -1) {
445
		log_debug("invalid disk id");
446
		errno = EINVAL;
447
		return (-1);
448
	}
449
	vm->vm_disks[n] = imsg->fd;
450
451
	return (0);
452
}
453
454
int
455
config_getif(struct privsep *ps, struct imsg *imsg)
456
{
457
	struct vmd_vm	*vm;
458
	unsigned int	 n;
459
460
	errno = 0;
461
	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
462
		errno = ENOENT;
463
		return (-1);
464
	}
465
466
	IMSG_SIZE_CHECK(imsg, &n);
467
	memcpy(&n, imsg->data, sizeof(n));
468
	if (n >= vm->vm_params.vmc_params.vcp_nnics ||
469
	    vm->vm_ifs[n].vif_fd != -1 || imsg->fd == -1) {
470
		log_debug("invalid interface id");
471
		goto fail;
472
	}
473
	vm->vm_ifs[n].vif_fd = imsg->fd;
474
	return (0);
475
 fail:
476
	if (imsg->fd != -1)
477
		close(imsg->fd);
478
	errno = EINVAL;
479
	return (-1);
480
}