xref: /openbsd-src/usr.sbin/vmd/parse.y (revision 65bbee46cad7861cd5a570f338df9e976422e3ab)
1 /*	$OpenBSD: parse.y,v 1.71 2024/09/26 01:45:13 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
9  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
10  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
11  *
12  * Permission to use, copy, modify, and distribute this software for any
13  * purpose with or without fee is hereby granted, provided that the above
14  * copyright notice and this permission notice appear in all copies.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 %{
26 #include <sys/types.h>
27 #include <sys/queue.h>
28 #include <sys/socket.h>
29 
30 #include <dev/vmm/vmm.h>
31 
32 #include <arpa/inet.h>
33 #include <net/if.h>
34 #include <netinet/in.h>
35 #include <netinet/if_ether.h>
36 
37 #include <agentx.h>
38 #include <stdio.h>
39 #include <limits.h>
40 #include <stdarg.h>
41 #include <unistd.h>
42 #include <ctype.h>
43 #include <netdb.h>
44 #include <util.h>
45 #include <errno.h>
46 #include <err.h>
47 #include <fcntl.h>
48 #include <pwd.h>
49 #include <grp.h>
50 
51 #include "vmd.h"
52 
53 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
54 static struct file {
55 	TAILQ_ENTRY(file)	 entry;
56 	FILE			*stream;
57 	char			*name;
58 	size_t			 ungetpos;
59 	size_t			 ungetsize;
60 	u_char			*ungetbuf;
61 	int			 eof_reached;
62 	int			 lineno;
63 	int			 errors;
64 } *file, *topfile;
65 struct file	*pushfile(const char *, int);
66 int		 popfile(void);
67 int		 yyparse(void);
68 int		 yylex(void);
69 int		 yyerror(const char *, ...)
70     __attribute__((__format__ (printf, 1, 2)))
71     __attribute__((__nonnull__ (1)));
72 int		 kw_cmp(const void *, const void *);
73 int		 lookup(char *);
74 int		 igetc(void);
75 int		 lgetc(int);
76 void		 lungetc(int);
77 int		 findeol(void);
78 
79 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
80 struct sym {
81 	TAILQ_ENTRY(sym)	 entry;
82 	int			 used;
83 	int			 persist;
84 	char			*nam;
85 	char			*val;
86 };
87 int		 symset(const char *, const char *, int);
88 char		*symget(const char *);
89 
90 ssize_t		 parse_size(char *, int64_t);
91 int		 parse_disk(char *, int);
92 unsigned int	 parse_format(const char *);
93 
94 static struct vmop_create_params vmc;
95 static struct vm_create_params	*vcp;
96 static struct vmd_switch	*vsw;
97 static char			*kernel = NULL;
98 static char			 vsw_type[IF_NAMESIZE];
99 static int			 vmc_disable;
100 static size_t			 vmc_nnics;
101 static int			 errors;
102 extern struct vmd		*env;
103 extern const char		*vmd_descsw[];
104 
105 typedef struct {
106 	union {
107 		uint8_t		 lladdr[ETHER_ADDR_LEN];
108 		int64_t		 number;
109 		char		*string;
110 		struct {
111 			uid_t	 uid;
112 			int64_t	 gid;
113 		}		 owner;
114 	} v;
115 	int lineno;
116 } YYSTYPE;
117 
118 %}
119 
120 
121 %token	INCLUDE ERROR
122 %token	ADD AGENTX ALLOW BOOT CDROM CONTEXT DEVICE DISABLE DISK DOWN ENABLE
123 %token	FORMAT GROUP
124 %token	INET6 INSTANCE INTERFACE LLADDR LOCAL LOCKED MEMORY NET NIFS OWNER
125 %token	PATH PREFIX RDOMAIN SIZE SOCKET SWITCH UP VM VMID STAGGERED START
126 %token  PARALLEL DELAY SEV
127 %token	<v.number>	NUMBER
128 %token	<v.string>	STRING
129 %type	<v.lladdr>	lladdr
130 %type	<v.number>	bootdevice
131 %type	<v.number>	disable
132 %type	<v.number>	image_format
133 %type	<v.number>	local
134 %type	<v.number>	locked
135 %type	<v.number>	updown
136 %type	<v.owner>	owner_id
137 %type	<v.string>	optstring
138 %type	<v.string>	string
139 %type	<v.string>	vm_instance
140 %type	<v.number>	sev;
141 
142 %%
143 
144 grammar		: /* empty */
145 		| grammar include '\n'
146 		| grammar '\n'
147 		| grammar varset '\n'
148 		| grammar main '\n'
149 		| grammar switch '\n'
150 		| grammar vm '\n'
151 		| grammar error '\n'		{ file->errors++; }
152 		;
153 
154 include		: INCLUDE string		{
155 			struct file	*nfile;
156 
157 			if ((nfile = pushfile($2, 0)) == NULL) {
158 				yyerror("failed to include file %s", $2);
159 				free($2);
160 				YYERROR;
161 			}
162 			free($2);
163 
164 			file = nfile;
165 			lungetc('\n');
166 		}
167 		;
168 
169 varset		: STRING '=' STRING		{
170 			char *s = $1;
171 			while (*s++) {
172 				if (isspace((unsigned char)*s)) {
173 					yyerror("macro name cannot contain "
174 					    "whitespace");
175 					free($1);
176 					free($3);
177 					YYERROR;
178 				}
179 			}
180 			if (symset($1, $3, 0) == -1)
181 				fatalx("cannot store variable");
182 			free($1);
183 			free($3);
184 		}
185 		;
186 
187 main		: LOCAL INET6 {
188 			env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
189 		}
190 		| LOCAL INET6 PREFIX STRING {
191 			const char	*err;
192 
193 			if (parse_prefix6($4, &env->vmd_cfg.cfg_localprefix,
194 			    &err)) {
195 				yyerror("invalid local inet6 prefix: %s", err);
196 				YYERROR;
197 			} else {
198 				env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
199 				env->vmd_cfg.cfg_flags &= ~VMD_CFG_AUTOINET6;
200 			}
201 			free($4);
202 		}
203 		| LOCAL PREFIX STRING {
204 			const char	*err;
205 
206 			if (parse_prefix4($3, &env->vmd_cfg.cfg_localprefix,
207 			    &err)) {
208 				yyerror("invalid local prefix: %s", err);
209 				YYERROR;
210 			}
211 			free($3);
212 		}
213 		| SOCKET OWNER owner_id {
214 			env->vmd_ps.ps_csock.cs_uid = $3.uid;
215 			env->vmd_ps.ps_csock.cs_gid = $3.gid == -1 ? 0 : $3.gid;
216 		}
217 		| AGENTX {
218 			env->vmd_cfg.cfg_agentx.ax_enabled = 1;
219 		} agentxopts {
220 			if (env->vmd_cfg.cfg_agentx.ax_path[0] == '\0')
221 				if (strlcpy(env->vmd_cfg.cfg_agentx.ax_path,
222 				    AGENTX_MASTER_PATH,
223 				    sizeof(env->vmd_cfg.cfg_agentx.ax_path)) >=
224 				    sizeof(env->vmd_cfg.cfg_agentx.ax_path)) {
225 					yyerror("invalid agentx path");
226 					YYERROR;
227 				}
228 		}
229 		| STAGGERED START PARALLEL NUMBER DELAY NUMBER {
230 			env->vmd_cfg.cfg_flags |= VMD_CFG_STAGGERED_START;
231 			env->vmd_cfg.delay.tv_sec = $6;
232 			env->vmd_cfg.parallelism = $4;
233 		}
234 		;
235 
236 switch		: SWITCH string			{
237 			if ((vsw = calloc(1, sizeof(*vsw))) == NULL)
238 				fatal("could not allocate switch");
239 
240 			vsw->sw_id = env->vmd_nswitches + 1;
241 			vsw->sw_name = $2;
242 			vsw->sw_flags = VMIFF_UP;
243 
244 			vmc_disable = 0;
245 		} '{' optnl switch_opts_l '}'	{
246 			if (strnlen(vsw->sw_ifname,
247 			    sizeof(vsw->sw_ifname)) == 0) {
248 				yyerror("switch \"%s\" "
249 				    "is missing interface name",
250 				    vsw->sw_name);
251 				YYERROR;
252 			}
253 
254 			if (vmc_disable) {
255 				log_debug("%s:%d: switch \"%s\""
256 				    " skipped (disabled)",
257 				    file->name, yylval.lineno, vsw->sw_name);
258 			} else if (!env->vmd_noaction) {
259 				TAILQ_INSERT_TAIL(env->vmd_switches,
260 				    vsw, sw_entry);
261 				env->vmd_nswitches++;
262 				log_debug("%s:%d: switch \"%s\" registered",
263 				    file->name, yylval.lineno, vsw->sw_name);
264 			}
265 		}
266 		;
267 
268 switch_opts_l	: switch_opts_l switch_opts nl
269 		| switch_opts optnl
270 		;
271 
272 switch_opts	: disable			{
273 			vmc_disable = $1;
274 		}
275 		| GROUP string			{
276 			if (priv_validgroup($2) == -1) {
277 				yyerror("invalid group name: %s", $2);
278 				free($2);
279 				YYERROR;
280 			}
281 			vsw->sw_group = $2;
282 		}
283 		| INTERFACE string		{
284 			if (priv_getiftype($2, vsw_type, NULL) == -1 ||
285 			    priv_findname(vsw_type, vmd_descsw) == -1) {
286 				yyerror("invalid switch interface: %s", $2);
287 				free($2);
288 				YYERROR;
289 			}
290 
291 			if (strlcpy(vsw->sw_ifname, $2,
292 			    sizeof(vsw->sw_ifname)) >= sizeof(vsw->sw_ifname)) {
293 				yyerror("switch interface too long: %s", $2);
294 				free($2);
295 				YYERROR;
296 			}
297 			free($2);
298 		}
299 		| LOCKED LLADDR			{
300 			vsw->sw_flags |= VMIFF_LOCKED;
301 		}
302 		| RDOMAIN NUMBER		{
303 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
304 				yyerror("invalid rdomain: %lld", $2);
305 				YYERROR;
306 			}
307 			vsw->sw_flags |= VMIFF_RDOMAIN;
308 			vsw->sw_rdomain = $2;
309 		}
310 		| updown			{
311 			if ($1)
312 				vsw->sw_flags |= VMIFF_UP;
313 			else
314 				vsw->sw_flags &= ~VMIFF_UP;
315 		}
316 		;
317 
318 vm		: VM string vm_instance		{
319 			unsigned int	 i;
320 			char		*name;
321 
322 			memset(&vmc, 0, sizeof(vmc));
323 			vmc.vmc_kernel = -1;
324 
325 			vcp = &vmc.vmc_params;
326 			vmc_disable = 0;
327 			vmc_nnics = 0;
328 
329 			if ($3 != NULL) {
330 				/* This is an instance of a pre-configured VM */
331 				if (strlcpy(vmc.vmc_instance, $2,
332 				    sizeof(vmc.vmc_instance)) >=
333 				    sizeof(vmc.vmc_instance)) {
334 					yyerror("vm %s name too long", $2);
335 					free($2);
336 					free($3);
337 					YYERROR;
338 				}
339 
340 				free($2);
341 				name = $3;
342 				vmc.vmc_flags |= VMOP_CREATE_INSTANCE;
343 			} else
344 				name = $2;
345 
346 			for (i = 0; i < VM_MAX_NICS_PER_VM; i++) {
347 				/* Set the interface to UP by default */
348 				vmc.vmc_ifflags[i] |= IFF_UP;
349 			}
350 
351 			if (strlcpy(vcp->vcp_name, name,
352 			    sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name)) {
353 				yyerror("vm name too long");
354 				free($2);
355 				free($3);
356 				YYERROR;
357 			}
358 
359 			/* set default user/group permissions */
360 			vmc.vmc_owner.uid = 0;
361 			vmc.vmc_owner.gid = -1;
362 		} '{' optnl vm_opts_l '}'	{
363 			struct vmd_vm	*vm;
364 			int		 ret;
365 
366 			/* configured interfaces vs. number of interfaces */
367 			if (vmc_nnics > vmc.vmc_nnics)
368 				vmc.vmc_nnics = vmc_nnics;
369 
370 			if (!env->vmd_noaction) {
371 				ret = vm_register(&env->vmd_ps, &vmc,
372 				    &vm, 0, 0);
373 				if (ret == -1 && errno == EALREADY) {
374 					log_debug("%s:%d: vm \"%s\""
375 					    " skipped (%s)",
376 					    file->name, yylval.lineno,
377 					    vcp->vcp_name,
378 					    (vm->vm_state & VM_STATE_RUNNING) ?
379 					    "running" : "already exists");
380 				} else if (ret == -1) {
381 					yyerror("vm \"%s\" failed: %s",
382 					    vcp->vcp_name, strerror(errno));
383 					YYERROR;
384 				} else {
385 					if (vmc_disable)
386 						vm->vm_state |= VM_STATE_DISABLED;
387 					else
388 						vm->vm_state |= VM_STATE_WAITING;
389 					log_debug("%s:%d: vm \"%s\" "
390 					    "registered (%s)",
391 					    file->name, yylval.lineno,
392 					    vcp->vcp_name,
393 					    vmc_disable ?
394 					    "disabled" : "enabled");
395 				}
396 				vm->vm_kernel_path = kernel;
397 				vm->vm_kernel = -1;
398 				vm->vm_from_config = 1;
399 			}
400 			kernel = NULL;
401 		}
402 		;
403 
404 vm_instance	: /* empty */			{ $$ = NULL; }
405 		| INSTANCE string		{ $$ = $2; }
406 		;
407 
408 vm_opts_l	: vm_opts_l vm_opts nl
409 		| vm_opts optnl
410 		;
411 
412 vm_opts		: disable			{
413 			vmc_disable = $1;
414 		}
415 		| sev				{
416 			vcp->vcp_sev = 1;
417 		}
418 		| DISK string image_format	{
419 			if (parse_disk($2, $3) != 0) {
420 				yyerror("failed to parse disks: %s", $2);
421 				free($2);
422 				YYERROR;
423 			}
424 			free($2);
425 			vmc.vmc_flags |= VMOP_CREATE_DISK;
426 		}
427 		| local INTERFACE optstring iface_opts_o {
428 			unsigned int	i;
429 			char		type[IF_NAMESIZE];
430 
431 			i = vmc_nnics;
432 			if (++vmc_nnics > VM_MAX_NICS_PER_VM) {
433 				yyerror("too many interfaces: %zu", vmc_nnics);
434 				free($3);
435 				YYERROR;
436 			}
437 
438 			if ($1)
439 				vmc.vmc_ifflags[i] |= VMIFF_LOCAL;
440 			if ($3 != NULL) {
441 				if (strcmp($3, "tap") != 0 &&
442 				    (priv_getiftype($3, type, NULL) == -1 ||
443 				    strcmp(type, "tap") != 0)) {
444 					yyerror("invalid interface: %s", $3);
445 					free($3);
446 					YYERROR;
447 				}
448 
449 				if (strlcpy(vmc.vmc_ifnames[i], $3,
450 				    sizeof(vmc.vmc_ifnames[i])) >=
451 				    sizeof(vmc.vmc_ifnames[i])) {
452 					yyerror("interface name too long: %s",
453 					    $3);
454 					free($3);
455 					YYERROR;
456 				}
457 			}
458 			free($3);
459 			vmc.vmc_flags |= VMOP_CREATE_NETWORK;
460 		}
461 		| BOOT string			{
462 			char	 path[PATH_MAX];
463 
464 			if (kernel != NULL) {
465 				yyerror("kernel specified more than once");
466 				free($2);
467 				YYERROR;
468 
469 			}
470 			if (realpath($2, path) == NULL) {
471 				yyerror("kernel path not found: %s",
472 				    strerror(errno));
473 				free($2);
474 				YYERROR;
475 			}
476 			free($2);
477 			kernel = malloc(sizeof(path));
478 			if (kernel == NULL)
479 				yyerror("malloc");
480 			memcpy(kernel, &path, sizeof(path));
481 			vmc.vmc_flags |= VMOP_CREATE_KERNEL;
482 		}
483 		| BOOT DEVICE bootdevice	{
484 			vmc.vmc_bootdevice = $3;
485 		}
486 		| CDROM string			{
487 			if (vmc.vmc_cdrom[0] != '\0') {
488 				yyerror("cdrom specified more than once");
489 				free($2);
490 				YYERROR;
491 
492 			}
493 			if (strlcpy(vmc.vmc_cdrom, $2,
494 			    sizeof(vmc.vmc_cdrom)) >=
495 			    sizeof(vmc.vmc_cdrom)) {
496 				yyerror("cdrom name too long");
497 				free($2);
498 				YYERROR;
499 			}
500 			free($2);
501 			vmc.vmc_flags |= VMOP_CREATE_CDROM;
502 		}
503 		| NIFS NUMBER			{
504 			if (vmc.vmc_nnics != 0) {
505 				yyerror("interfaces specified more than once");
506 				YYERROR;
507 			}
508 			if ($2 < 0 || $2 > VM_MAX_NICS_PER_VM) {
509 				yyerror("too many interfaces: %lld", $2);
510 				YYERROR;
511 			}
512 			vmc.vmc_nnics = (size_t)$2;
513 			vmc.vmc_flags |= VMOP_CREATE_NETWORK;
514 		}
515 		| MEMORY NUMBER			{
516 			ssize_t	 res;
517 			if (vcp->vcp_memranges[0].vmr_size != 0) {
518 				yyerror("memory specified more than once");
519 				YYERROR;
520 			}
521 			if ((res = parse_size(NULL, $2)) == -1) {
522 				yyerror("failed to parse size: %lld", $2);
523 				YYERROR;
524 			}
525 			vcp->vcp_memranges[0].vmr_size = (size_t)res;
526 			vmc.vmc_flags |= VMOP_CREATE_MEMORY;
527 		}
528 		| MEMORY STRING			{
529 			ssize_t	 res;
530 			if (vcp->vcp_memranges[0].vmr_size != 0) {
531 				yyerror("argument specified more than once");
532 				free($2);
533 				YYERROR;
534 			}
535 			if ((res = parse_size($2, 0)) == -1) {
536 				yyerror("failed to parse size: %s", $2);
537 				free($2);
538 				YYERROR;
539 			}
540 			vcp->vcp_memranges[0].vmr_size = (size_t)res;
541 			vmc.vmc_flags |= VMOP_CREATE_MEMORY;
542 		}
543 		| OWNER owner_id		{
544 			vmc.vmc_owner.uid = $2.uid;
545 			vmc.vmc_owner.gid = $2.gid;
546 		}
547 		| instance
548 		;
549 
550 instance	: ALLOW INSTANCE '{' optnl instance_l '}'
551 		| ALLOW INSTANCE instance_flags
552 		;
553 
554 instance_l	: instance_flags optcommanl instance_l
555 		| instance_flags optnl
556 		;
557 
558 instance_flags	: BOOT		{ vmc.vmc_insflags |= VMOP_CREATE_KERNEL; }
559 		| MEMORY	{ vmc.vmc_insflags |= VMOP_CREATE_MEMORY; }
560 		| INTERFACE	{ vmc.vmc_insflags |= VMOP_CREATE_NETWORK; }
561 		| DISK		{ vmc.vmc_insflags |= VMOP_CREATE_DISK; }
562 		| CDROM		{ vmc.vmc_insflags |= VMOP_CREATE_CDROM; }
563 		| INSTANCE	{ vmc.vmc_insflags |= VMOP_CREATE_INSTANCE; }
564 		| OWNER owner_id {
565 			vmc.vmc_insowner.uid = $2.uid;
566 			vmc.vmc_insowner.gid = $2.gid;
567 		}
568 		;
569 
570 owner_id	: NUMBER		{
571 			$$.uid = $1;
572 			$$.gid = -1;
573 		}
574 		| STRING		{
575 			char		*user, *group;
576 			struct passwd	*pw;
577 			struct group	*gr;
578 
579 			$$.uid = 0;
580 			$$.gid = -1;
581 
582 			user = $1;
583 			if ((group = strchr(user, ':')) != NULL) {
584 				if (group == user)
585 					user = NULL;
586 				*group++ = '\0';
587 			}
588 
589 			if (user != NULL && *user) {
590 				if ((pw = getpwnam(user)) == NULL) {
591 					yyerror("failed to get user: %s",
592 					    user);
593 					free($1);
594 					YYERROR;
595 				}
596 				$$.uid = pw->pw_uid;
597 			}
598 
599 			if (group != NULL && *group) {
600 				if ((gr = getgrnam(group)) == NULL) {
601 					yyerror("failed to get group: %s",
602 					    group);
603 					free($1);
604 					YYERROR;
605 				}
606 				$$.gid = gr->gr_gid;
607 			}
608 
609 			free($1);
610 		}
611 		;
612 
613 agentxopt	: CONTEXT STRING {
614 			if (strlcpy(env->vmd_cfg.cfg_agentx.ax_context, $2,
615 			    sizeof(env->vmd_cfg.cfg_agentx.ax_context)) >=
616 			    sizeof(env->vmd_cfg.cfg_agentx.ax_context)) {
617 				yyerror("agentx context too large");
618 				free($2);
619 				YYERROR;
620 			}
621 			free($2);
622 		}
623 		| PATH STRING {
624 			if (strlcpy(env->vmd_cfg.cfg_agentx.ax_path, $2,
625 			    sizeof(env->vmd_cfg.cfg_agentx.ax_path)) >=
626 			    sizeof(env->vmd_cfg.cfg_agentx.ax_path)) {
627 				yyerror("agentx path too large");
628 				free($2);
629 				YYERROR;
630 			}
631 			free($2);
632 			if (env->vmd_cfg.cfg_agentx.ax_path[0] != '/') {
633 				yyerror("agentx path is not absolute");
634 				YYERROR;
635 			}
636 		}
637 
638 agentxopts	: /* none */
639 		| agentxopts agentxopt
640 		;
641 
642 image_format	: /* none 	*/	{
643 			$$ = 0;
644 		}
645 	     	| FORMAT string		{
646 			if (($$ = parse_format($2)) == 0) {
647 				yyerror("unrecognized disk format %s", $2);
648 				free($2);
649 				YYERROR;
650 			}
651 		}
652 		;
653 
654 iface_opts_o	: '{' optnl iface_opts_l '}'
655 		| iface_opts_c
656 		| /* empty */
657 		;
658 
659 iface_opts_l	: iface_opts_l iface_opts optnl
660 		| iface_opts optnl
661 		;
662 
663 iface_opts_c	: iface_opts_c iface_opts optcomma
664 		| iface_opts
665 		;
666 
667 iface_opts	: SWITCH string			{
668 			unsigned int	i = vmc_nnics;
669 
670 			/* No need to check if the switch exists */
671 			if (strlcpy(vmc.vmc_ifswitch[i], $2,
672 			    sizeof(vmc.vmc_ifswitch[i])) >=
673 			    sizeof(vmc.vmc_ifswitch[i])) {
674 				yyerror("switch name too long: %s", $2);
675 				free($2);
676 				YYERROR;
677 			}
678 			free($2);
679 		}
680 		| GROUP string			{
681 			unsigned int	i = vmc_nnics;
682 
683 			if (priv_validgroup($2) == -1) {
684 				yyerror("invalid group name: %s", $2);
685 				free($2);
686 				YYERROR;
687 			}
688 
689 			/* No need to check if the group exists */
690 			(void)strlcpy(vmc.vmc_ifgroup[i], $2,
691 			    sizeof(vmc.vmc_ifgroup[i]));
692 			free($2);
693 		}
694 		| locked LLADDR lladdr		{
695 			if ($1)
696 				vmc.vmc_ifflags[vmc_nnics] |= VMIFF_LOCKED;
697 			memcpy(vmc.vmc_macs[vmc_nnics], $3, ETHER_ADDR_LEN);
698 		}
699 		| RDOMAIN NUMBER		{
700 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
701 				yyerror("invalid rdomain: %lld", $2);
702 				YYERROR;
703 			}
704 			vmc.vmc_ifflags[vmc_nnics] |= VMIFF_RDOMAIN;
705 			vmc.vmc_ifrdomain[vmc_nnics] = $2;
706 		}
707 		| updown			{
708 			if ($1)
709 				vmc.vmc_ifflags[vmc_nnics] |= VMIFF_UP;
710 			else
711 				vmc.vmc_ifflags[vmc_nnics] &= ~VMIFF_UP;
712 		}
713 		;
714 
715 optstring	: STRING			{ $$ = $1; }
716 		| /* empty */			{ $$ = NULL; }
717 		;
718 
719 string		: STRING string			{
720 			if (asprintf(&$$, "%s%s", $1, $2) == -1)
721 				fatal("asprintf string");
722 			free($1);
723 			free($2);
724 		}
725 		| STRING
726 		;
727 
728 lladdr		: STRING			{
729 			struct ether_addr *ea;
730 
731 			if ((ea = ether_aton($1)) == NULL) {
732 				yyerror("invalid address: %s\n", $1);
733 				free($1);
734 				YYERROR;
735 			}
736 			free($1);
737 
738 			memcpy($$, ea, ETHER_ADDR_LEN);
739 		}
740 		| /* empty */ {
741 			memset($$, 0, ETHER_ADDR_LEN);
742 		}
743 		;
744 
745 local		: /* empty */			{ $$ = 0; }
746 		| LOCAL				{ $$ = 1; }
747 		;
748 
749 locked		: /* empty */			{ $$ = 0; }
750 		| LOCKED			{ $$ = 1; }
751 		;
752 
753 updown		: UP				{ $$ = 1; }
754 		| DOWN				{ $$ = 0; }
755 		;
756 
757 disable		: ENABLE			{ $$ = 0; }
758 		| DISABLE			{ $$ = 1; }
759 		;
760 
761 sev		: SEV				{ $$ = 1; }
762 		;
763 
764 bootdevice	: CDROM				{ $$ = VMBOOTDEV_CDROM; }
765 		| DISK				{ $$ = VMBOOTDEV_DISK; }
766 		| NET				{ $$ = VMBOOTDEV_NET; }
767 		;
768 
769 optcomma	: ','
770 		|
771 		;
772 
773 optnl		: '\n' optnl
774 		|
775 		;
776 
777 optcommanl	: ',' optnl
778 		| nl
779 		;
780 
781 nl		: '\n' optnl
782 		;
783 
784 %%
785 
786 struct keywords {
787 	const char	*k_name;
788 	int		 k_val;
789 };
790 
791 int
792 yyerror(const char *fmt, ...)
793 {
794 	va_list		 ap;
795 	char		*msg;
796 
797 	file->errors++;
798 	va_start(ap, fmt);
799 	if (vasprintf(&msg, fmt, ap) == -1)
800 		fatal("yyerror vasprintf");
801 	va_end(ap);
802 	log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
803 	free(msg);
804 	return (0);
805 }
806 
807 int
808 kw_cmp(const void *k, const void *e)
809 {
810 	return (strcmp(k, ((const struct keywords *)e)->k_name));
811 }
812 
813 int
814 lookup(char *s)
815 {
816 	/* this has to be sorted always */
817 	static const struct keywords keywords[] = {
818 		{ "add",		ADD },
819 		{ "agentx",		AGENTX },
820 		{ "allow",		ALLOW },
821 		{ "boot",		BOOT },
822 		{ "cdrom",		CDROM },
823 		{ "context",		CONTEXT},
824 		{ "delay",		DELAY },
825 		{ "device",		DEVICE },
826 		{ "disable",		DISABLE },
827 		{ "disk",		DISK },
828 		{ "down",		DOWN },
829 		{ "enable",		ENABLE },
830 		{ "format",		FORMAT },
831 		{ "group",		GROUP },
832 		{ "id",			VMID },
833 		{ "include",		INCLUDE },
834 		{ "inet6",		INET6 },
835 		{ "instance",		INSTANCE },
836 		{ "interface",		INTERFACE },
837 		{ "interfaces",		NIFS },
838 		{ "lladdr",		LLADDR },
839 		{ "local",		LOCAL },
840 		{ "locked",		LOCKED },
841 		{ "memory",		MEMORY },
842 		{ "net",		NET },
843 		{ "owner",		OWNER },
844 		{ "parallel",		PARALLEL },
845 		{ "path",		PATH },
846 		{ "prefix",		PREFIX },
847 		{ "rdomain",		RDOMAIN },
848 		{ "sev",		SEV },
849 		{ "size",		SIZE },
850 		{ "socket",		SOCKET },
851 		{ "staggered",		STAGGERED },
852 		{ "start",		START  },
853 		{ "switch",		SWITCH },
854 		{ "up",			UP },
855 		{ "vm",			VM }
856 	};
857 	const struct keywords	*p;
858 
859 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
860 	    sizeof(keywords[0]), kw_cmp);
861 
862 	if (p)
863 		return (p->k_val);
864 	else
865 		return (STRING);
866 }
867 
868 #define START_EXPAND	1
869 #define DONE_EXPAND	2
870 
871 static int	expanding;
872 
873 int
874 igetc(void)
875 {
876 	int	c;
877 
878 	while (1) {
879 		if (file->ungetpos > 0)
880 			c = file->ungetbuf[--file->ungetpos];
881 		else
882 			c = getc(file->stream);
883 
884 		if (c == START_EXPAND)
885 			expanding = 1;
886 		else if (c == DONE_EXPAND)
887 			expanding = 0;
888 		else
889 			break;
890 	}
891 	return (c);
892 }
893 
894 int
895 lgetc(int quotec)
896 {
897 	int		c, next;
898 
899 	if (quotec) {
900 		if ((c = igetc()) == EOF) {
901 			yyerror("reached end of file while parsing "
902 			    "quoted string");
903 			if (file == topfile || popfile() == EOF)
904 				return (EOF);
905 			return (quotec);
906 		}
907 		return (c);
908 	}
909 
910 	while ((c = igetc()) == '\\') {
911 		next = igetc();
912 		if (next != '\n') {
913 			c = next;
914 			break;
915 		}
916 		yylval.lineno = file->lineno;
917 		file->lineno++;
918 	}
919 	if (c == '\t' || c == ' ') {
920 		/* Compress blanks to a single space. */
921 		do {
922 			c = getc(file->stream);
923 		} while (c == '\t' || c == ' ');
924 		ungetc(c, file->stream);
925 		c = ' ';
926 	}
927 
928 	if (c == EOF) {
929 		/*
930 		 * Fake EOL when hit EOF for the first time. This gets line
931 		 * count right if last line in included file is syntactically
932 		 * invalid and has no newline.
933 		 */
934 		if (file->eof_reached == 0) {
935 			file->eof_reached = 1;
936 			return ('\n');
937 		}
938 		while (c == EOF) {
939 			if (file == topfile || popfile() == EOF)
940 				return (EOF);
941 			c = igetc();
942 		}
943 	}
944 	return (c);
945 }
946 
947 void
948 lungetc(int c)
949 {
950 	if (c == EOF)
951 		return;
952 
953 	if (file->ungetpos >= file->ungetsize) {
954 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
955 		if (p == NULL)
956 			err(1, "%s", __func__);
957 		file->ungetbuf = p;
958 		file->ungetsize *= 2;
959 	}
960 	file->ungetbuf[file->ungetpos++] = c;
961 }
962 
963 int
964 findeol(void)
965 {
966 	int	c;
967 
968 	/* skip to either EOF or the first real EOL */
969 	while (1) {
970 		c = lgetc(0);
971 		if (c == '\n') {
972 			file->lineno++;
973 			break;
974 		}
975 		if (c == EOF)
976 			break;
977 	}
978 	return (ERROR);
979 }
980 
981 int
982 yylex(void)
983 {
984 	char	 buf[8096];
985 	char	*p, *val;
986 	int	 quotec, next, c;
987 	int	 token;
988 
989 top:
990 	p = buf;
991 	while ((c = lgetc(0)) == ' ' || c == '\t')
992 		; /* nothing */
993 
994 	yylval.lineno = file->lineno;
995 	if (c == '#')
996 		while ((c = lgetc(0)) != '\n' && c != EOF)
997 			; /* nothing */
998 	if (c == '$' && !expanding) {
999 		while (1) {
1000 			if ((c = lgetc(0)) == EOF)
1001 				return (0);
1002 
1003 			if (p + 1 >= buf + sizeof(buf) - 1) {
1004 				yyerror("string too long");
1005 				return (findeol());
1006 			}
1007 			if (isalnum(c) || c == '_') {
1008 				*p++ = c;
1009 				continue;
1010 			}
1011 			*p = '\0';
1012 			lungetc(c);
1013 			break;
1014 		}
1015 		val = symget(buf);
1016 		if (val == NULL) {
1017 			yyerror("macro '%s' not defined", buf);
1018 			return (findeol());
1019 		}
1020 		p = val + strlen(val) - 1;
1021 		lungetc(DONE_EXPAND);
1022 		while (p >= val) {
1023 			lungetc((unsigned char)*p);
1024 			p--;
1025 		}
1026 		lungetc(START_EXPAND);
1027 		goto top;
1028 	}
1029 
1030 	switch (c) {
1031 	case '\'':
1032 	case '"':
1033 		quotec = c;
1034 		while (1) {
1035 			if ((c = lgetc(quotec)) == EOF)
1036 				return (0);
1037 			if (c == '\n') {
1038 				file->lineno++;
1039 				continue;
1040 			} else if (c == '\\') {
1041 				if ((next = lgetc(quotec)) == EOF)
1042 					return (0);
1043 				if (next == quotec || next == ' ' ||
1044 				    next == '\t')
1045 					c = next;
1046 				else if (next == '\n') {
1047 					file->lineno++;
1048 					continue;
1049 				} else
1050 					lungetc(next);
1051 			} else if (c == quotec) {
1052 				*p = '\0';
1053 				break;
1054 			} else if (c == '\0') {
1055 				yyerror("syntax error");
1056 				return (findeol());
1057 			}
1058 			if (p + 1 >= buf + sizeof(buf) - 1) {
1059 				yyerror("string too long");
1060 				return (findeol());
1061 			}
1062 			*p++ = c;
1063 		}
1064 		yylval.v.string = strdup(buf);
1065 		if (yylval.v.string == NULL)
1066 			fatal("yylex: strdup");
1067 		return (STRING);
1068 	}
1069 
1070 #define allowed_to_end_number(x) \
1071 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1072 
1073 	if (c == '-' || isdigit(c)) {
1074 		do {
1075 			*p++ = c;
1076 			if ((size_t)(p-buf) >= sizeof(buf)) {
1077 				yyerror("string too long");
1078 				return (findeol());
1079 			}
1080 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1081 		lungetc(c);
1082 		if (p == buf + 1 && buf[0] == '-')
1083 			goto nodigits;
1084 		if (c == EOF || allowed_to_end_number(c)) {
1085 			const char *errstr = NULL;
1086 
1087 			*p = '\0';
1088 			yylval.v.number = strtonum(buf, LLONG_MIN,
1089 			    LLONG_MAX, &errstr);
1090 			if (errstr) {
1091 				yyerror("\"%s\" invalid number: %s",
1092 				    buf, errstr);
1093 				return (findeol());
1094 			}
1095 			return (NUMBER);
1096 		} else {
1097 nodigits:
1098 			while (p > buf + 1)
1099 				lungetc((unsigned char)*--p);
1100 			c = (unsigned char)*--p;
1101 			if (c == '-')
1102 				return (c);
1103 		}
1104 	}
1105 
1106 #define allowed_in_string(x) \
1107 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1108 	x != '{' && x != '}' && \
1109 	x != '!' && x != '=' && x != '#' && \
1110 	x != ','))
1111 
1112 	if (isalnum(c) || c == ':' || c == '_' || c == '/') {
1113 		do {
1114 			*p++ = c;
1115 			if ((size_t)(p-buf) >= sizeof(buf)) {
1116 				yyerror("string too long");
1117 				return (findeol());
1118 			}
1119 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1120 		lungetc(c);
1121 		*p = '\0';
1122 		if ((token = lookup(buf)) == STRING)
1123 			if ((yylval.v.string = strdup(buf)) == NULL)
1124 				fatal("yylex: strdup");
1125 		return (token);
1126 	}
1127 	if (c == '\n') {
1128 		yylval.lineno = file->lineno;
1129 		file->lineno++;
1130 	}
1131 	if (c == EOF)
1132 		return (0);
1133 	return (c);
1134 }
1135 
1136 struct file *
1137 pushfile(const char *name, int secret)
1138 {
1139 	struct file	*nfile;
1140 
1141 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
1142 		log_warn("%s", __func__);
1143 		return (NULL);
1144 	}
1145 	if ((nfile->name = strdup(name)) == NULL) {
1146 		log_warn("%s", __func__);
1147 		free(nfile);
1148 		return (NULL);
1149 	}
1150 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1151 		free(nfile->name);
1152 		free(nfile);
1153 		return (NULL);
1154 	}
1155 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
1156 	nfile->ungetsize = 16;
1157 	nfile->ungetbuf = malloc(nfile->ungetsize);
1158 	if (nfile->ungetbuf == NULL) {
1159 		log_warn("%s", __func__);
1160 		fclose(nfile->stream);
1161 		free(nfile->name);
1162 		free(nfile);
1163 		return (NULL);
1164 	}
1165 	TAILQ_INSERT_TAIL(&files, nfile, entry);
1166 	return (nfile);
1167 }
1168 
1169 int
1170 popfile(void)
1171 {
1172 	struct file	*prev;
1173 
1174 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1175 		prev->errors += file->errors;
1176 
1177 	TAILQ_REMOVE(&files, file, entry);
1178 	fclose(file->stream);
1179 	free(file->name);
1180 	free(file->ungetbuf);
1181 	free(file);
1182 	file = prev;
1183 	return (file ? 0 : EOF);
1184 }
1185 
1186 int
1187 parse_config(const char *filename)
1188 {
1189 	extern const char	 default_conffile[];
1190 	struct sym		*sym, *next;
1191 
1192 	if ((file = pushfile(filename, 0)) == NULL) {
1193 		/* no default config file is fine */
1194 		if (errno == ENOENT && filename == default_conffile) {
1195 			log_debug("%s: missing", filename);
1196 			return (0);
1197 		}
1198 		log_warn("failed to open %s", filename);
1199 		if (errno == ENOENT)
1200 			return (0);
1201 		return (-1);
1202 	}
1203 	topfile = file;
1204 	setservent(1);
1205 
1206 	/* Set the default switch type */
1207 	(void)strlcpy(vsw_type, VMD_SWITCH_TYPE, sizeof(vsw_type));
1208 
1209 	env->vmd_cfg.cfg_agentx.ax_enabled = 0;
1210 	env->vmd_cfg.cfg_agentx.ax_context[0] = '\0';
1211 	env->vmd_cfg.cfg_agentx.ax_path[0] = '\0';
1212 
1213 	yyparse();
1214 	errors = file->errors;
1215 	popfile();
1216 
1217 	endservent();
1218 
1219 	/* Free macros and check which have not been used. */
1220 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1221 		if (!sym->used)
1222 			fprintf(stderr, "warning: macro '%s' not "
1223 			    "used\n", sym->nam);
1224 		if (!sym->persist) {
1225 			free(sym->nam);
1226 			free(sym->val);
1227 			TAILQ_REMOVE(&symhead, sym, entry);
1228 			free(sym);
1229 		}
1230 	}
1231 
1232 	if (errors)
1233 		return (-1);
1234 
1235 	return (0);
1236 }
1237 
1238 int
1239 symset(const char *nam, const char *val, int persist)
1240 {
1241 	struct sym	*sym;
1242 
1243 	TAILQ_FOREACH(sym, &symhead, entry) {
1244 		if (strcmp(nam, sym->nam) == 0)
1245 			break;
1246 	}
1247 
1248 	if (sym != NULL) {
1249 		if (sym->persist == 1)
1250 			return (0);
1251 		else {
1252 			free(sym->nam);
1253 			free(sym->val);
1254 			TAILQ_REMOVE(&symhead, sym, entry);
1255 			free(sym);
1256 		}
1257 	}
1258 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1259 		return (-1);
1260 
1261 	sym->nam = strdup(nam);
1262 	if (sym->nam == NULL) {
1263 		free(sym);
1264 		return (-1);
1265 	}
1266 	sym->val = strdup(val);
1267 	if (sym->val == NULL) {
1268 		free(sym->nam);
1269 		free(sym);
1270 		return (-1);
1271 	}
1272 	sym->used = 0;
1273 	sym->persist = persist;
1274 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1275 	return (0);
1276 }
1277 
1278 int
1279 cmdline_symset(char *s)
1280 {
1281 	char	*sym, *val;
1282 	int	ret;
1283 
1284 	if ((val = strrchr(s, '=')) == NULL)
1285 		return (-1);
1286 	sym = strndup(s, val - s);
1287 	if (sym == NULL)
1288 		fatal("%s: strndup", __func__);
1289 	ret = symset(sym, val + 1, 1);
1290 	free(sym);
1291 
1292 	return (ret);
1293 }
1294 
1295 char *
1296 symget(const char *nam)
1297 {
1298 	struct sym	*sym;
1299 
1300 	TAILQ_FOREACH(sym, &symhead, entry) {
1301 		if (strcmp(nam, sym->nam) == 0) {
1302 			sym->used = 1;
1303 			return (sym->val);
1304 		}
1305 	}
1306 	return (NULL);
1307 }
1308 
1309 ssize_t
1310 parse_size(char *word, int64_t val)
1311 {
1312 	char		 result[FMT_SCALED_STRSIZE];
1313 	ssize_t		 size;
1314 	long long	 res;
1315 
1316 	if (word != NULL) {
1317 		if (scan_scaled(word, &res) != 0) {
1318 			log_warn("invalid memory size: %s", word);
1319 			return (-1);
1320 		}
1321 		val = (int64_t)res;
1322 	}
1323 
1324 	if (val < (1024 * 1024)) {
1325 		log_warnx("memory size must be at least 1MB");
1326 		return (-1);
1327 	}
1328 
1329 	if (val > VMM_MAX_VM_MEM_SIZE) {
1330 		if (fmt_scaled(VMM_MAX_VM_MEM_SIZE, result) == 0)
1331 			log_warnx("memory size too large (limit is %s)",
1332 			    result);
1333 		else
1334 			log_warnx("memory size too large");
1335 		return (-1);
1336 	}
1337 
1338 	/* Round down to the megabyte. */
1339 	size = (val / (1024 * 1024)) * (1024 * 1024);
1340 
1341 	if (size != val) {
1342 		if (fmt_scaled(size, result) == 0)
1343 			log_debug("memory size rounded to %s", result);
1344 		else
1345 			log_debug("memory size rounded to %zd bytes", size);
1346 	}
1347 
1348 	return ((ssize_t)size);
1349 }
1350 
1351 int
1352 parse_disk(char *word, int type)
1353 {
1354 	char	 buf[BUFSIZ], path[PATH_MAX];
1355 	int	 fd;
1356 	ssize_t	 len;
1357 
1358 	if (vmc.vmc_ndisks >= VM_MAX_DISKS_PER_VM) {
1359 		log_warnx("too many disks");
1360 		return (-1);
1361 	}
1362 
1363 	if (realpath(word, path) == NULL) {
1364 		log_warn("disk %s", word);
1365 		return (-1);
1366 	}
1367 
1368 	if (!type) {
1369 		/* Use raw as the default format */
1370 		type = VMDF_RAW;
1371 
1372 		/* Try to derive the format from the file signature */
1373 		if ((fd = open(path, O_RDONLY)) != -1) {
1374 			len = read(fd, buf, sizeof(buf));
1375 			close(fd);
1376 			if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) &&
1377 			    strncmp(buf, VM_MAGIC_QCOW,
1378 			    strlen(VM_MAGIC_QCOW)) == 0) {
1379 				/* The qcow version will be checked later */
1380 				type = VMDF_QCOW2;
1381 			}
1382 		}
1383 	}
1384 
1385 	if (strlcpy(vmc.vmc_disks[vmc.vmc_ndisks], path,
1386 	    sizeof(vmc.vmc_disks[vmc.vmc_ndisks])) >=
1387 	    sizeof(vmc.vmc_disks[vmc.vmc_ndisks])) {
1388 		log_warnx("disk path too long");
1389 		return (-1);
1390 	}
1391 	vmc.vmc_disktypes[vmc.vmc_ndisks] = type;
1392 
1393 	vmc.vmc_ndisks++;
1394 
1395 	return (0);
1396 }
1397 
1398 unsigned int
1399 parse_format(const char *word)
1400 {
1401 	if (strcasecmp(word, "raw") == 0)
1402 		return (VMDF_RAW);
1403 	else if (strcasecmp(word, "qcow2") == 0)
1404 		return (VMDF_QCOW2);
1405 	return (0);
1406 }
1407 
1408 /*
1409  * Parse an ipv4 address and prefix for local interfaces and validate
1410  * constraints for vmd networking.
1411  */
1412 int
1413 parse_prefix4(const char *str, struct local_prefix *out, const char **errstr)
1414 {
1415 	struct addrinfo		 hints, *res = NULL;
1416 	struct sockaddr_storage	 ss;
1417 	struct in_addr		 addr;
1418 	int			 mask = 16;
1419 	char			*p, *ps;
1420 
1421 	if ((ps = strdup(str)) == NULL)
1422 		fatal("%s: strdup", __func__);
1423 
1424 	if ((p = strrchr(ps, '/')) != NULL) {
1425 		mask = strtonum(p + 1, 1, 16, errstr);
1426 		if (errstr != NULL && *errstr) {
1427 			free(ps);
1428 			return (1);
1429 		}
1430 		p[0] = '\0';
1431 	}
1432 
1433 	/* Attempt to construct an address from the user input. */
1434 	memset(&hints, 0, sizeof(hints));
1435 	hints.ai_family = AF_INET;
1436 	hints.ai_socktype = SOCK_DGRAM;
1437 	hints.ai_flags = AI_NUMERICHOST;
1438 
1439 	if (getaddrinfo(ps, NULL, &hints, &res) == 0) {
1440 		memset(&ss, 0, sizeof(ss));
1441 		memcpy(&ss, res->ai_addr, res->ai_addrlen);
1442 		addr.s_addr = ss2sin(&ss)->sin_addr.s_addr;
1443 		freeaddrinfo(res);
1444 	} else { /* try 10/8 parsing */
1445 		memset(&addr, 0, sizeof(addr));
1446 		if (inet_net_pton(AF_INET, ps, &addr, sizeof(addr)) == -1) {
1447 			if (errstr)
1448 				*errstr = "invalid format";
1449 			free(ps);
1450 			return (1);
1451 		}
1452 	}
1453 	free(ps);
1454 
1455 	/*
1456 	 * Validate the prefix by comparing it with the mask. Since we
1457 	 * constrain the mask length to 16 above, this also validates
1458 	 * we reserve the last 16 bits for use by vmd to assign vm id
1459 	 * and interface id.
1460 	 */
1461 	if ((addr.s_addr & prefixlen2mask(mask)) != addr.s_addr) {
1462 		if (errstr)
1463 			*errstr = "bad mask";
1464 		return (1);
1465 	}
1466 
1467 	/* Copy out the local prefix. */
1468 	out->lp_in.s_addr = addr.s_addr;
1469 	out->lp_mask.s_addr = prefixlen2mask(mask);
1470 	return (0);
1471 }
1472 
1473 /*
1474  * Parse an ipv6 address and prefix for local interfaces and validate
1475  * constraints for vmd networking.
1476  */
1477 int
1478 parse_prefix6(const char *str, struct local_prefix *out, const char **errstr)
1479 {
1480 	struct addrinfo		 hints, *res = NULL;
1481 	struct sockaddr_storage	 ss;
1482 	struct in6_addr		 addr6, mask6;
1483 	size_t			 i;
1484 	int			 mask = 64, err;
1485 	char			*p, *ps;
1486 
1487 	if ((ps = strdup(str)) == NULL)
1488 		fatal("%s: strdup", __func__);
1489 
1490 	if ((p = strrchr(ps, '/')) != NULL) {
1491 		mask = strtonum(p + 1, 0, 64, errstr);
1492 		if (errstr != NULL && *errstr) {
1493 			free(ps);
1494 			return (1);
1495 		}
1496 		p[0] = '\0';
1497 	}
1498 
1499 	/* Attempt to construct an address from the user input. */
1500 	memset(&hints, 0, sizeof(hints));
1501 	hints.ai_family = AF_INET6;
1502 	hints.ai_socktype = SOCK_DGRAM;
1503 	hints.ai_flags = AI_NUMERICHOST;
1504 
1505 	if ((err = getaddrinfo(ps, NULL, &hints, &res)) != 0) {
1506 		if (errstr)
1507 			*errstr = gai_strerror(err);
1508 		free(ps);
1509 		return (1);
1510 	}
1511 	free(ps);
1512 
1513 	memset(&ss, 0, sizeof(ss));
1514 	memcpy(&ss, res->ai_addr, res->ai_addrlen);
1515 	freeaddrinfo(res);
1516 
1517 	memcpy(&addr6, (void*)&ss2sin6(&ss)->sin6_addr, sizeof(addr6));
1518 	prefixlen2mask6(mask, &mask6);
1519 
1520 	/*
1521 	 * Validate the prefix by comparing it with the mask. Since we
1522 	 * constrain the mask length to 64 above, this also validates
1523 	 * that we're reserving bits for the encoding of the ipv4
1524 	 * address, the vm id, and interface id. */
1525 	for (i = 0; i < 16; i++) {
1526 		if ((addr6.s6_addr[i] & mask6.s6_addr[i]) != addr6.s6_addr[i]) {
1527 			if (errstr)
1528 				*errstr = "bad mask";
1529 			return (1);
1530 		}
1531 	}
1532 
1533 	/* Copy out the local prefix. */
1534 	memcpy(&out->lp_in6, &addr6, sizeof(out->lp_in6));
1535 	memcpy(&out->lp_mask6, &mask6, sizeof(out->lp_mask6));
1536 	return (0);
1537 }
1538