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