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

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