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