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