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