1*65bbee46Sjsg /* $OpenBSD: parse.y,v 1.71 2024/09/26 01:45:13 jsg Exp $ */ 2f01317bcSreyk 3f01317bcSreyk /* 4789e0822Sreyk * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org> 5f01317bcSreyk * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 6f01317bcSreyk * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 7f01317bcSreyk * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 8f01317bcSreyk * Copyright (c) 2001 Markus Friedl. All rights reserved. 9f01317bcSreyk * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 10f01317bcSreyk * Copyright (c) 2001 Theo de Raadt. All rights reserved. 11f01317bcSreyk * 12f01317bcSreyk * Permission to use, copy, modify, and distribute this software for any 13f01317bcSreyk * purpose with or without fee is hereby granted, provided that the above 14f01317bcSreyk * copyright notice and this permission notice appear in all copies. 15f01317bcSreyk * 16f01317bcSreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 17f01317bcSreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 18f01317bcSreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 19f01317bcSreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 20f01317bcSreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 21f01317bcSreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 22f01317bcSreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23f01317bcSreyk */ 24f01317bcSreyk 25f01317bcSreyk %{ 26f01317bcSreyk #include <sys/types.h> 27f01317bcSreyk #include <sys/queue.h> 28789e0822Sreyk #include <sys/socket.h> 29f01317bcSreyk 30ba66f564Sdv #include <dev/vmm/vmm.h> 31f01317bcSreyk 322272e586Sdv #include <arpa/inet.h> 33789e0822Sreyk #include <net/if.h> 34789e0822Sreyk #include <netinet/in.h> 35789e0822Sreyk #include <netinet/if_ether.h> 36789e0822Sreyk 37f94ca20eSmartijn #include <agentx.h> 38f01317bcSreyk #include <stdio.h> 39f01317bcSreyk #include <limits.h> 40f01317bcSreyk #include <stdarg.h> 41e2ceadc1Sreyk #include <unistd.h> 42f01317bcSreyk #include <ctype.h> 43f01317bcSreyk #include <netdb.h> 44f01317bcSreyk #include <util.h> 456d5856cfSreyk #include <errno.h> 46f01317bcSreyk #include <err.h> 47e2ceadc1Sreyk #include <fcntl.h> 48e5d5b350Sreyk #include <pwd.h> 49e5d5b350Sreyk #include <grp.h> 50f01317bcSreyk 51f01317bcSreyk #include "vmd.h" 52f01317bcSreyk 53f01317bcSreyk TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 54f01317bcSreyk static struct file { 55f01317bcSreyk TAILQ_ENTRY(file) entry; 56f01317bcSreyk FILE *stream; 57f01317bcSreyk char *name; 58f61f6eccSdenis size_t ungetpos; 59f61f6eccSdenis size_t ungetsize; 60f61f6eccSdenis u_char *ungetbuf; 61f61f6eccSdenis int eof_reached; 62f01317bcSreyk int lineno; 63f01317bcSreyk int errors; 64f01317bcSreyk } *file, *topfile; 65f01317bcSreyk struct file *pushfile(const char *, int); 66f01317bcSreyk int popfile(void); 67f01317bcSreyk int yyparse(void); 68f01317bcSreyk int yylex(void); 69f01317bcSreyk int yyerror(const char *, ...) 70f01317bcSreyk __attribute__((__format__ (printf, 1, 2))) 71f01317bcSreyk __attribute__((__nonnull__ (1))); 72f01317bcSreyk int kw_cmp(const void *, const void *); 73f01317bcSreyk int lookup(char *); 74f61f6eccSdenis int igetc(void); 75f01317bcSreyk int lgetc(int); 76f61f6eccSdenis void lungetc(int); 77f01317bcSreyk int findeol(void); 78f01317bcSreyk 79f01317bcSreyk TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 80f01317bcSreyk struct sym { 81f01317bcSreyk TAILQ_ENTRY(sym) entry; 82f01317bcSreyk int used; 83f01317bcSreyk int persist; 84f01317bcSreyk char *nam; 85f01317bcSreyk char *val; 86f01317bcSreyk }; 87f01317bcSreyk int symset(const char *, const char *, int); 88f01317bcSreyk char *symget(const char *); 89f01317bcSreyk 90f01317bcSreyk ssize_t parse_size(char *, int64_t); 91f224f92aSccardenas int parse_disk(char *, int); 92e2ceadc1Sreyk unsigned int parse_format(const char *); 93f01317bcSreyk 94789e0822Sreyk static struct vmop_create_params vmc; 95789e0822Sreyk static struct vm_create_params *vcp; 96789e0822Sreyk static struct vmd_switch *vsw; 97b848b186Sdv static char *kernel = NULL; 98789e0822Sreyk static char vsw_type[IF_NAMESIZE]; 9973a98491Sdv static int vmc_disable; 10073a98491Sdv static size_t vmc_nnics; 101789e0822Sreyk static int errors; 102f01317bcSreyk extern struct vmd *env; 103789e0822Sreyk extern const char *vmd_descsw[]; 104f01317bcSreyk 105f01317bcSreyk typedef struct { 106f01317bcSreyk union { 107e14446e4Sreyk uint8_t lladdr[ETHER_ADDR_LEN]; 108f01317bcSreyk int64_t number; 109f01317bcSreyk char *string; 110e5d5b350Sreyk struct { 111e5d5b350Sreyk uid_t uid; 112e5d5b350Sreyk int64_t gid; 113e5d5b350Sreyk } owner; 114f01317bcSreyk } v; 115f01317bcSreyk int lineno; 116f01317bcSreyk } YYSTYPE; 117f01317bcSreyk 118f01317bcSreyk %} 119f01317bcSreyk 120f01317bcSreyk 121f01317bcSreyk %token INCLUDE ERROR 122f94ca20eSmartijn %token ADD AGENTX ALLOW BOOT CDROM CONTEXT DEVICE DISABLE DISK DOWN ENABLE 123f94ca20eSmartijn %token FORMAT GROUP 124c5ee7fb3Santon %token INET6 INSTANCE INTERFACE LLADDR LOCAL LOCKED MEMORY NET NIFS OWNER 125de12a377Spd %token PATH PREFIX RDOMAIN SIZE SOCKET SWITCH UP VM VMID STAGGERED START 126f4b47ae8Sbluhm %token PARALLEL DELAY SEV 127f01317bcSreyk %token <v.number> NUMBER 128e47f0f66Sreyk %token <v.string> STRING 129e47f0f66Sreyk %type <v.lladdr> lladdr 130c5ee7fb3Santon %type <v.number> bootdevice 131f01317bcSreyk %type <v.number> disable 132f224f92aSccardenas %type <v.number> image_format 133470adcf5Sreyk %type <v.number> local 1342b2a5f0dSreyk %type <v.number> locked 135789e0822Sreyk %type <v.number> updown 136e5d5b350Sreyk %type <v.owner> owner_id 137e47f0f66Sreyk %type <v.string> optstring 138e47f0f66Sreyk %type <v.string> string 1396429e633Sreyk %type <v.string> vm_instance 140f4b47ae8Sbluhm %type <v.number> sev; 141f01317bcSreyk 142f01317bcSreyk %% 143f01317bcSreyk 144f01317bcSreyk grammar : /* empty */ 145f01317bcSreyk | grammar include '\n' 146f01317bcSreyk | grammar '\n' 147f01317bcSreyk | grammar varset '\n' 148c48cfcf4Sreyk | grammar main '\n' 149789e0822Sreyk | grammar switch '\n' 150789e0822Sreyk | grammar vm '\n' 151f01317bcSreyk | grammar error '\n' { file->errors++; } 152f01317bcSreyk ; 153f01317bcSreyk 154458ed070Sreyk include : INCLUDE string { 155f01317bcSreyk struct file *nfile; 156f01317bcSreyk 157f01317bcSreyk if ((nfile = pushfile($2, 0)) == NULL) { 158f01317bcSreyk yyerror("failed to include file %s", $2); 159f01317bcSreyk free($2); 160f01317bcSreyk YYERROR; 161f01317bcSreyk } 162f01317bcSreyk free($2); 163f01317bcSreyk 164f01317bcSreyk file = nfile; 165f01317bcSreyk lungetc('\n'); 166f01317bcSreyk } 167f01317bcSreyk ; 168f01317bcSreyk 169f01317bcSreyk varset : STRING '=' STRING { 1700c7b4ca6Sbenno char *s = $1; 1710c7b4ca6Sbenno while (*s++) { 1720c7b4ca6Sbenno if (isspace((unsigned char)*s)) { 1730c7b4ca6Sbenno yyerror("macro name cannot contain " 1740c7b4ca6Sbenno "whitespace"); 17516a0a906Skrw free($1); 17616a0a906Skrw free($3); 1770c7b4ca6Sbenno YYERROR; 1780c7b4ca6Sbenno } 1790c7b4ca6Sbenno } 180f01317bcSreyk if (symset($1, $3, 0) == -1) 181f01317bcSreyk fatalx("cannot store variable"); 182f01317bcSreyk free($1); 183f01317bcSreyk free($3); 184f01317bcSreyk } 185f01317bcSreyk ; 186f01317bcSreyk 187723f86d2Sreyk main : LOCAL INET6 { 188723f86d2Sreyk env->vmd_cfg.cfg_flags |= VMD_CFG_INET6; 189723f86d2Sreyk } 190723f86d2Sreyk | LOCAL INET6 PREFIX STRING { 1912272e586Sdv const char *err; 192c48cfcf4Sreyk 1932272e586Sdv if (parse_prefix6($4, &env->vmd_cfg.cfg_localprefix, 1942272e586Sdv &err)) { 1952272e586Sdv yyerror("invalid local inet6 prefix: %s", err); 196723f86d2Sreyk YYERROR; 1972272e586Sdv } else { 198723f86d2Sreyk env->vmd_cfg.cfg_flags |= VMD_CFG_INET6; 199723f86d2Sreyk env->vmd_cfg.cfg_flags &= ~VMD_CFG_AUTOINET6; 2002272e586Sdv } 2012272e586Sdv free($4); 202723f86d2Sreyk } 203723f86d2Sreyk | LOCAL PREFIX STRING { 2042272e586Sdv const char *err; 205723f86d2Sreyk 2062272e586Sdv if (parse_prefix4($3, &env->vmd_cfg.cfg_localprefix, 2072272e586Sdv &err)) { 2082272e586Sdv yyerror("invalid local prefix: %s", err); 209c48cfcf4Sreyk YYERROR; 210c48cfcf4Sreyk } 2112272e586Sdv free($3); 212c48cfcf4Sreyk } 2136cfffd57Sreyk | SOCKET OWNER owner_id { 2146cfffd57Sreyk env->vmd_ps.ps_csock.cs_uid = $3.uid; 2156cfffd57Sreyk env->vmd_ps.ps_csock.cs_gid = $3.gid == -1 ? 0 : $3.gid; 2166cfffd57Sreyk } 217f94ca20eSmartijn | AGENTX { 218f94ca20eSmartijn env->vmd_cfg.cfg_agentx.ax_enabled = 1; 219f94ca20eSmartijn } agentxopts { 220f94ca20eSmartijn if (env->vmd_cfg.cfg_agentx.ax_path[0] == '\0') 221f94ca20eSmartijn if (strlcpy(env->vmd_cfg.cfg_agentx.ax_path, 222f94ca20eSmartijn AGENTX_MASTER_PATH, 223f94ca20eSmartijn sizeof(env->vmd_cfg.cfg_agentx.ax_path)) >= 224f94ca20eSmartijn sizeof(env->vmd_cfg.cfg_agentx.ax_path)) { 225f94ca20eSmartijn yyerror("invalid agentx path"); 226f94ca20eSmartijn YYERROR; 227f94ca20eSmartijn } 228f94ca20eSmartijn } 229de12a377Spd | STAGGERED START PARALLEL NUMBER DELAY NUMBER { 230de12a377Spd env->vmd_cfg.cfg_flags |= VMD_CFG_STAGGERED_START; 231de12a377Spd env->vmd_cfg.delay.tv_sec = $6; 232de12a377Spd env->vmd_cfg.parallelism = $4; 233de12a377Spd } 234c48cfcf4Sreyk ; 235c48cfcf4Sreyk 236789e0822Sreyk switch : SWITCH string { 237789e0822Sreyk if ((vsw = calloc(1, sizeof(*vsw))) == NULL) 238789e0822Sreyk fatal("could not allocate switch"); 239789e0822Sreyk 240789e0822Sreyk vsw->sw_id = env->vmd_nswitches + 1; 241789e0822Sreyk vsw->sw_name = $2; 2422b2a5f0dSreyk vsw->sw_flags = VMIFF_UP; 243789e0822Sreyk 24473a98491Sdv vmc_disable = 0; 245789e0822Sreyk } '{' optnl switch_opts_l '}' { 246a123de80Smlarkin if (strnlen(vsw->sw_ifname, 247a123de80Smlarkin sizeof(vsw->sw_ifname)) == 0) { 248d2de69e7Sreyk yyerror("switch \"%s\" " 249d2de69e7Sreyk "is missing interface name", 250a123de80Smlarkin vsw->sw_name); 251a123de80Smlarkin YYERROR; 252a123de80Smlarkin } 253a123de80Smlarkin 25473a98491Sdv if (vmc_disable) { 255789e0822Sreyk log_debug("%s:%d: switch \"%s\"" 256789e0822Sreyk " skipped (disabled)", 257789e0822Sreyk file->name, yylval.lineno, vsw->sw_name); 258789e0822Sreyk } else if (!env->vmd_noaction) { 259d2de69e7Sreyk TAILQ_INSERT_TAIL(env->vmd_switches, 260d2de69e7Sreyk vsw, sw_entry); 261ea9c30d9Sedd env->vmd_nswitches++; 262ea9c30d9Sedd log_debug("%s:%d: switch \"%s\" registered", 263ea9c30d9Sedd file->name, yylval.lineno, vsw->sw_name); 264789e0822Sreyk } 265789e0822Sreyk } 266789e0822Sreyk ; 267789e0822Sreyk 268789e0822Sreyk switch_opts_l : switch_opts_l switch_opts nl 269789e0822Sreyk | switch_opts optnl 270789e0822Sreyk ; 271789e0822Sreyk 272789e0822Sreyk switch_opts : disable { 27373a98491Sdv vmc_disable = $1; 274789e0822Sreyk } 2754629ffa0Sreyk | GROUP string { 2764629ffa0Sreyk if (priv_validgroup($2) == -1) { 2774629ffa0Sreyk yyerror("invalid group name: %s", $2); 2784629ffa0Sreyk free($2); 2794629ffa0Sreyk YYERROR; 2804629ffa0Sreyk } 2814629ffa0Sreyk vsw->sw_group = $2; 2824629ffa0Sreyk } 283789e0822Sreyk | INTERFACE string { 284a123de80Smlarkin if (priv_getiftype($2, vsw_type, NULL) == -1 || 285789e0822Sreyk priv_findname(vsw_type, vmd_descsw) == -1) { 286789e0822Sreyk yyerror("invalid switch interface: %s", $2); 287789e0822Sreyk free($2); 288789e0822Sreyk YYERROR; 289789e0822Sreyk } 290789e0822Sreyk 291789e0822Sreyk if (strlcpy(vsw->sw_ifname, $2, 292789e0822Sreyk sizeof(vsw->sw_ifname)) >= sizeof(vsw->sw_ifname)) { 293789e0822Sreyk yyerror("switch interface too long: %s", $2); 294789e0822Sreyk free($2); 295789e0822Sreyk YYERROR; 296789e0822Sreyk } 297789e0822Sreyk free($2); 298789e0822Sreyk } 2992b2a5f0dSreyk | LOCKED LLADDR { 3002b2a5f0dSreyk vsw->sw_flags |= VMIFF_LOCKED; 3012b2a5f0dSreyk } 302f418e70cSreyk | RDOMAIN NUMBER { 303f418e70cSreyk if ($2 < 0 || $2 > RT_TABLEID_MAX) { 304f418e70cSreyk yyerror("invalid rdomain: %lld", $2); 305f418e70cSreyk YYERROR; 306f418e70cSreyk } 307f418e70cSreyk vsw->sw_flags |= VMIFF_RDOMAIN; 308f418e70cSreyk vsw->sw_rdomain = $2; 309f418e70cSreyk } 310789e0822Sreyk | updown { 311789e0822Sreyk if ($1) 3122b2a5f0dSreyk vsw->sw_flags |= VMIFF_UP; 313789e0822Sreyk else 3142b2a5f0dSreyk vsw->sw_flags &= ~VMIFF_UP; 315789e0822Sreyk } 316789e0822Sreyk ; 317789e0822Sreyk 3186429e633Sreyk vm : VM string vm_instance { 319789e0822Sreyk unsigned int i; 3206429e633Sreyk char *name; 321789e0822Sreyk 322789e0822Sreyk memset(&vmc, 0, sizeof(vmc)); 323b848b186Sdv vmc.vmc_kernel = -1; 324b848b186Sdv 325789e0822Sreyk vcp = &vmc.vmc_params; 32673a98491Sdv vmc_disable = 0; 32773a98491Sdv vmc_nnics = 0; 328789e0822Sreyk 3296429e633Sreyk if ($3 != NULL) { 3306429e633Sreyk /* This is an instance of a pre-configured VM */ 3316429e633Sreyk if (strlcpy(vmc.vmc_instance, $2, 3326429e633Sreyk sizeof(vmc.vmc_instance)) >= 3336429e633Sreyk sizeof(vmc.vmc_instance)) { 3346429e633Sreyk yyerror("vm %s name too long", $2); 3356429e633Sreyk free($2); 3366429e633Sreyk free($3); 3376429e633Sreyk YYERROR; 3386429e633Sreyk } 3396429e633Sreyk 3406429e633Sreyk free($2); 3416429e633Sreyk name = $3; 3426429e633Sreyk vmc.vmc_flags |= VMOP_CREATE_INSTANCE; 3436429e633Sreyk } else 3446429e633Sreyk name = $2; 3456429e633Sreyk 346d489aa7eSdv for (i = 0; i < VM_MAX_NICS_PER_VM; i++) { 347789e0822Sreyk /* Set the interface to UP by default */ 348789e0822Sreyk vmc.vmc_ifflags[i] |= IFF_UP; 349789e0822Sreyk } 350789e0822Sreyk 3516429e633Sreyk if (strlcpy(vcp->vcp_name, name, 3526429e633Sreyk sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name)) { 353f01317bcSreyk yyerror("vm name too long"); 3546429e633Sreyk free($2); 3556429e633Sreyk free($3); 356f01317bcSreyk YYERROR; 357f01317bcSreyk } 358e5d5b350Sreyk 359e5d5b350Sreyk /* set default user/group permissions */ 360476d73d1Sreyk vmc.vmc_owner.uid = 0; 361476d73d1Sreyk vmc.vmc_owner.gid = -1; 362f01317bcSreyk } '{' optnl vm_opts_l '}' { 3636429e633Sreyk struct vmd_vm *vm; 36438e15a96Sreyk int ret; 36538e15a96Sreyk 366789e0822Sreyk /* configured interfaces vs. number of interfaces */ 36773a98491Sdv if (vmc_nnics > vmc.vmc_nnics) 36873a98491Sdv vmc.vmc_nnics = vmc_nnics; 369789e0822Sreyk 370371c1bb3Sedd if (!env->vmd_noaction) { 371e5d5b350Sreyk ret = vm_register(&env->vmd_ps, &vmc, 372e5d5b350Sreyk &vm, 0, 0); 3736d5856cfSreyk if (ret == -1 && errno == EALREADY) { 3746d5856cfSreyk log_debug("%s:%d: vm \"%s\"" 375ea9c30d9Sedd " skipped (%s)", 3766d5856cfSreyk file->name, yylval.lineno, 37719700f36Sjasper vcp->vcp_name, 37819700f36Sjasper (vm->vm_state & VM_STATE_RUNNING) ? 379ea9c30d9Sedd "running" : "already exists"); 3806d5856cfSreyk } else if (ret == -1) { 3816429e633Sreyk yyerror("vm \"%s\" failed: %s", 3826429e633Sreyk vcp->vcp_name, strerror(errno)); 383f01317bcSreyk YYERROR; 38438e15a96Sreyk } else { 38573a98491Sdv if (vmc_disable) 38619700f36Sjasper vm->vm_state |= VM_STATE_DISABLED; 387de12a377Spd else 388de12a377Spd vm->vm_state |= VM_STATE_WAITING; 389d2de69e7Sreyk log_debug("%s:%d: vm \"%s\" " 390d2de69e7Sreyk "registered (%s)", 39138e15a96Sreyk file->name, yylval.lineno, 392371c1bb3Sedd vcp->vcp_name, 39373a98491Sdv vmc_disable ? 394d2de69e7Sreyk "disabled" : "enabled"); 39538e15a96Sreyk } 396b848b186Sdv vm->vm_kernel_path = kernel; 397b848b186Sdv vm->vm_kernel = -1; 39856502f88Sedd vm->vm_from_config = 1; 399f01317bcSreyk } 400b848b186Sdv kernel = NULL; 401f01317bcSreyk } 402f01317bcSreyk ; 403f01317bcSreyk 4046429e633Sreyk vm_instance : /* empty */ { $$ = NULL; } 4056429e633Sreyk | INSTANCE string { $$ = $2; } 4066429e633Sreyk ; 4076429e633Sreyk 408f01317bcSreyk vm_opts_l : vm_opts_l vm_opts nl 409f01317bcSreyk | vm_opts optnl 410f01317bcSreyk ; 411f01317bcSreyk 412f01317bcSreyk vm_opts : disable { 41373a98491Sdv vmc_disable = $1; 414f01317bcSreyk } 415f4b47ae8Sbluhm | sev { 416f4b47ae8Sbluhm vcp->vcp_sev = 1; 417f4b47ae8Sbluhm } 418f224f92aSccardenas | DISK string image_format { 419f224f92aSccardenas if (parse_disk($2, $3) != 0) { 420f01317bcSreyk yyerror("failed to parse disks: %s", $2); 421f01317bcSreyk free($2); 422f01317bcSreyk YYERROR; 423f01317bcSreyk } 424f01317bcSreyk free($2); 425619bf29aSreyk vmc.vmc_flags |= VMOP_CREATE_DISK; 426f01317bcSreyk } 427470adcf5Sreyk | local INTERFACE optstring iface_opts_o { 428789e0822Sreyk unsigned int i; 429789e0822Sreyk char type[IF_NAMESIZE]; 430789e0822Sreyk 43173a98491Sdv i = vmc_nnics; 43273a98491Sdv if (++vmc_nnics > VM_MAX_NICS_PER_VM) { 43373a98491Sdv yyerror("too many interfaces: %zu", vmc_nnics); 434470adcf5Sreyk free($3); 435f01317bcSreyk YYERROR; 436f01317bcSreyk } 437789e0822Sreyk 438470adcf5Sreyk if ($1) 439470adcf5Sreyk vmc.vmc_ifflags[i] |= VMIFF_LOCAL; 440470adcf5Sreyk if ($3 != NULL) { 441470adcf5Sreyk if (strcmp($3, "tap") != 0 && 442470adcf5Sreyk (priv_getiftype($3, type, NULL) == -1 || 443789e0822Sreyk strcmp(type, "tap") != 0)) { 444470adcf5Sreyk yyerror("invalid interface: %s", $3); 445470adcf5Sreyk free($3); 446789e0822Sreyk YYERROR; 447789e0822Sreyk } 448789e0822Sreyk 449470adcf5Sreyk if (strlcpy(vmc.vmc_ifnames[i], $3, 450789e0822Sreyk sizeof(vmc.vmc_ifnames[i])) >= 451789e0822Sreyk sizeof(vmc.vmc_ifnames[i])) { 452789e0822Sreyk yyerror("interface name too long: %s", 453470adcf5Sreyk $3); 454470adcf5Sreyk free($3); 455789e0822Sreyk YYERROR; 456789e0822Sreyk } 457789e0822Sreyk } 458470adcf5Sreyk free($3); 459619bf29aSreyk vmc.vmc_flags |= VMOP_CREATE_NETWORK; 460789e0822Sreyk } 4616b03ca83Sreyk | BOOT string { 4624a91d1ffSreyk char path[PATH_MAX]; 4634a91d1ffSreyk 464b848b186Sdv if (kernel != NULL) { 465789e0822Sreyk yyerror("kernel specified more than once"); 466789e0822Sreyk free($2); 467789e0822Sreyk YYERROR; 468789e0822Sreyk 469789e0822Sreyk } 4704a91d1ffSreyk if (realpath($2, path) == NULL) { 47129d9fdbaSbluhm yyerror("kernel path not found: %s", 47229d9fdbaSbluhm strerror(errno)); 473f01317bcSreyk free($2); 474f01317bcSreyk YYERROR; 475f01317bcSreyk } 476f01317bcSreyk free($2); 4773ac1122dSdv kernel = malloc(sizeof(path)); 4783ac1122dSdv if (kernel == NULL) 4793ac1122dSdv yyerror("malloc"); 4803ac1122dSdv memcpy(kernel, &path, sizeof(path)); 481619bf29aSreyk vmc.vmc_flags |= VMOP_CREATE_KERNEL; 482f01317bcSreyk } 483c5ee7fb3Santon | BOOT DEVICE bootdevice { 484c5ee7fb3Santon vmc.vmc_bootdevice = $3; 485c5ee7fb3Santon } 48695ab188fSccardenas | CDROM string { 48773a98491Sdv if (vmc.vmc_cdrom[0] != '\0') { 48895ab188fSccardenas yyerror("cdrom specified more than once"); 48995ab188fSccardenas free($2); 49095ab188fSccardenas YYERROR; 49195ab188fSccardenas 49295ab188fSccardenas } 49373a98491Sdv if (strlcpy(vmc.vmc_cdrom, $2, 49473a98491Sdv sizeof(vmc.vmc_cdrom)) >= 49573a98491Sdv sizeof(vmc.vmc_cdrom)) { 49695ab188fSccardenas yyerror("cdrom name too long"); 49795ab188fSccardenas free($2); 49895ab188fSccardenas YYERROR; 49995ab188fSccardenas } 50095ab188fSccardenas free($2); 50195ab188fSccardenas vmc.vmc_flags |= VMOP_CREATE_CDROM; 50295ab188fSccardenas } 503f01317bcSreyk | NIFS NUMBER { 50473a98491Sdv if (vmc.vmc_nnics != 0) { 505f01317bcSreyk yyerror("interfaces specified more than once"); 506f01317bcSreyk YYERROR; 507f01317bcSreyk } 508d489aa7eSdv if ($2 < 0 || $2 > VM_MAX_NICS_PER_VM) { 509f01317bcSreyk yyerror("too many interfaces: %lld", $2); 510f01317bcSreyk YYERROR; 511f01317bcSreyk } 51273a98491Sdv vmc.vmc_nnics = (size_t)$2; 513619bf29aSreyk vmc.vmc_flags |= VMOP_CREATE_NETWORK; 514f01317bcSreyk } 515f01317bcSreyk | MEMORY NUMBER { 516f01317bcSreyk ssize_t res; 517789e0822Sreyk if (vcp->vcp_memranges[0].vmr_size != 0) { 518f01317bcSreyk yyerror("memory specified more than once"); 519f01317bcSreyk YYERROR; 520f01317bcSreyk } 521f01317bcSreyk if ((res = parse_size(NULL, $2)) == -1) { 522f01317bcSreyk yyerror("failed to parse size: %lld", $2); 523f01317bcSreyk YYERROR; 524f01317bcSreyk } 525789e0822Sreyk vcp->vcp_memranges[0].vmr_size = (size_t)res; 526619bf29aSreyk vmc.vmc_flags |= VMOP_CREATE_MEMORY; 527f01317bcSreyk } 528f01317bcSreyk | MEMORY STRING { 529f01317bcSreyk ssize_t res; 530789e0822Sreyk if (vcp->vcp_memranges[0].vmr_size != 0) { 531f01317bcSreyk yyerror("argument specified more than once"); 532f01317bcSreyk free($2); 533f01317bcSreyk YYERROR; 534f01317bcSreyk } 535f01317bcSreyk if ((res = parse_size($2, 0)) == -1) { 536f01317bcSreyk yyerror("failed to parse size: %s", $2); 537f01317bcSreyk free($2); 538f01317bcSreyk YYERROR; 539f01317bcSreyk } 540789e0822Sreyk vcp->vcp_memranges[0].vmr_size = (size_t)res; 541619bf29aSreyk vmc.vmc_flags |= VMOP_CREATE_MEMORY; 542f01317bcSreyk } 543e5d5b350Sreyk | OWNER owner_id { 544476d73d1Sreyk vmc.vmc_owner.uid = $2.uid; 545476d73d1Sreyk vmc.vmc_owner.gid = $2.gid; 546476d73d1Sreyk } 547476d73d1Sreyk | instance 548476d73d1Sreyk ; 549476d73d1Sreyk 550476d73d1Sreyk instance : ALLOW INSTANCE '{' optnl instance_l '}' 551476d73d1Sreyk | ALLOW INSTANCE instance_flags 552476d73d1Sreyk ; 553476d73d1Sreyk 554476d73d1Sreyk instance_l : instance_flags optcommanl instance_l 555476d73d1Sreyk | instance_flags optnl 556476d73d1Sreyk ; 557476d73d1Sreyk 558476d73d1Sreyk instance_flags : BOOT { vmc.vmc_insflags |= VMOP_CREATE_KERNEL; } 559476d73d1Sreyk | MEMORY { vmc.vmc_insflags |= VMOP_CREATE_MEMORY; } 560476d73d1Sreyk | INTERFACE { vmc.vmc_insflags |= VMOP_CREATE_NETWORK; } 561476d73d1Sreyk | DISK { vmc.vmc_insflags |= VMOP_CREATE_DISK; } 562476d73d1Sreyk | CDROM { vmc.vmc_insflags |= VMOP_CREATE_CDROM; } 563476d73d1Sreyk | INSTANCE { vmc.vmc_insflags |= VMOP_CREATE_INSTANCE; } 564476d73d1Sreyk | OWNER owner_id { 565476d73d1Sreyk vmc.vmc_insowner.uid = $2.uid; 566476d73d1Sreyk vmc.vmc_insowner.gid = $2.gid; 567e5d5b350Sreyk } 568e5d5b350Sreyk ; 569e5d5b350Sreyk 5700389d116Skn owner_id : NUMBER { 571e5d5b350Sreyk $$.uid = $1; 572e5d5b350Sreyk $$.gid = -1; 573e5d5b350Sreyk } 574e5d5b350Sreyk | STRING { 575e5d5b350Sreyk char *user, *group; 576e5d5b350Sreyk struct passwd *pw; 577e5d5b350Sreyk struct group *gr; 578e5d5b350Sreyk 579e5d5b350Sreyk $$.uid = 0; 580e5d5b350Sreyk $$.gid = -1; 581e5d5b350Sreyk 582e5d5b350Sreyk user = $1; 583e5d5b350Sreyk if ((group = strchr(user, ':')) != NULL) { 584e5d5b350Sreyk if (group == user) 585e5d5b350Sreyk user = NULL; 586e5d5b350Sreyk *group++ = '\0'; 587e5d5b350Sreyk } 588e5d5b350Sreyk 589e5d5b350Sreyk if (user != NULL && *user) { 590e5d5b350Sreyk if ((pw = getpwnam(user)) == NULL) { 591e5d5b350Sreyk yyerror("failed to get user: %s", 592e5d5b350Sreyk user); 593e5d5b350Sreyk free($1); 594e5d5b350Sreyk YYERROR; 595e5d5b350Sreyk } 596e5d5b350Sreyk $$.uid = pw->pw_uid; 597e5d5b350Sreyk } 598e5d5b350Sreyk 599e5d5b350Sreyk if (group != NULL && *group) { 600e5d5b350Sreyk if ((gr = getgrnam(group)) == NULL) { 601e5d5b350Sreyk yyerror("failed to get group: %s", 602e5d5b350Sreyk group); 603e5d5b350Sreyk free($1); 604e5d5b350Sreyk YYERROR; 605e5d5b350Sreyk } 606e5d5b350Sreyk $$.gid = gr->gr_gid; 607e5d5b350Sreyk } 608e5d5b350Sreyk 609e5d5b350Sreyk free($1); 610e5d5b350Sreyk } 611f01317bcSreyk ; 612f01317bcSreyk 613f94ca20eSmartijn agentxopt : CONTEXT STRING { 614f94ca20eSmartijn if (strlcpy(env->vmd_cfg.cfg_agentx.ax_context, $2, 615f94ca20eSmartijn sizeof(env->vmd_cfg.cfg_agentx.ax_context)) >= 616f94ca20eSmartijn sizeof(env->vmd_cfg.cfg_agentx.ax_context)) { 617f94ca20eSmartijn yyerror("agentx context too large"); 618f94ca20eSmartijn free($2); 619f94ca20eSmartijn YYERROR; 620f94ca20eSmartijn } 621f94ca20eSmartijn free($2); 622f94ca20eSmartijn } 623f94ca20eSmartijn | PATH STRING { 624f94ca20eSmartijn if (strlcpy(env->vmd_cfg.cfg_agentx.ax_path, $2, 625f94ca20eSmartijn sizeof(env->vmd_cfg.cfg_agentx.ax_path)) >= 626f94ca20eSmartijn sizeof(env->vmd_cfg.cfg_agentx.ax_path)) { 627f94ca20eSmartijn yyerror("agentx path too large"); 628f94ca20eSmartijn free($2); 629f94ca20eSmartijn YYERROR; 630f94ca20eSmartijn } 631f94ca20eSmartijn free($2); 632f94ca20eSmartijn if (env->vmd_cfg.cfg_agentx.ax_path[0] != '/') { 633f94ca20eSmartijn yyerror("agentx path is not absolute"); 634f94ca20eSmartijn YYERROR; 635f94ca20eSmartijn } 636f94ca20eSmartijn } 637f94ca20eSmartijn 638f94ca20eSmartijn agentxopts : /* none */ 639f94ca20eSmartijn | agentxopts agentxopt 640f94ca20eSmartijn ; 641f94ca20eSmartijn 642f224f92aSccardenas image_format : /* none */ { 643e2ceadc1Sreyk $$ = 0; 644f224f92aSccardenas } 645f224f92aSccardenas | FORMAT string { 646e2ceadc1Sreyk if (($$ = parse_format($2)) == 0) { 647f224f92aSccardenas yyerror("unrecognized disk format %s", $2); 648f224f92aSccardenas free($2); 649f224f92aSccardenas YYERROR; 650f224f92aSccardenas } 651f224f92aSccardenas } 652f224f92aSccardenas ; 653f224f92aSccardenas 654789e0822Sreyk iface_opts_o : '{' optnl iface_opts_l '}' 6553e9d8149Sreyk | iface_opts_c 656789e0822Sreyk | /* empty */ 657789e0822Sreyk ; 658789e0822Sreyk 659789e0822Sreyk iface_opts_l : iface_opts_l iface_opts optnl 660789e0822Sreyk | iface_opts optnl 661789e0822Sreyk ; 662789e0822Sreyk 6633e9d8149Sreyk iface_opts_c : iface_opts_c iface_opts optcomma 6643e9d8149Sreyk | iface_opts 6653e9d8149Sreyk ; 6663e9d8149Sreyk 667789e0822Sreyk iface_opts : SWITCH string { 66873a98491Sdv unsigned int i = vmc_nnics; 669789e0822Sreyk 670789e0822Sreyk /* No need to check if the switch exists */ 671789e0822Sreyk if (strlcpy(vmc.vmc_ifswitch[i], $2, 672789e0822Sreyk sizeof(vmc.vmc_ifswitch[i])) >= 673789e0822Sreyk sizeof(vmc.vmc_ifswitch[i])) { 674789e0822Sreyk yyerror("switch name too long: %s", $2); 675789e0822Sreyk free($2); 676789e0822Sreyk YYERROR; 677789e0822Sreyk } 678789e0822Sreyk free($2); 679789e0822Sreyk } 6802b519c1fSreyk | GROUP string { 68173a98491Sdv unsigned int i = vmc_nnics; 6822b519c1fSreyk 6832b519c1fSreyk if (priv_validgroup($2) == -1) { 6842b519c1fSreyk yyerror("invalid group name: %s", $2); 6852b519c1fSreyk free($2); 6862b519c1fSreyk YYERROR; 6872b519c1fSreyk } 6882b519c1fSreyk 6892b519c1fSreyk /* No need to check if the group exists */ 6902b519c1fSreyk (void)strlcpy(vmc.vmc_ifgroup[i], $2, 6912b519c1fSreyk sizeof(vmc.vmc_ifgroup[i])); 6922b519c1fSreyk free($2); 6932b519c1fSreyk } 6942b2a5f0dSreyk | locked LLADDR lladdr { 6952b2a5f0dSreyk if ($1) 69673a98491Sdv vmc.vmc_ifflags[vmc_nnics] |= VMIFF_LOCKED; 69773a98491Sdv memcpy(vmc.vmc_macs[vmc_nnics], $3, ETHER_ADDR_LEN); 698789e0822Sreyk } 699f418e70cSreyk | RDOMAIN NUMBER { 700f418e70cSreyk if ($2 < 0 || $2 > RT_TABLEID_MAX) { 701f418e70cSreyk yyerror("invalid rdomain: %lld", $2); 702f418e70cSreyk YYERROR; 703f418e70cSreyk } 70473a98491Sdv vmc.vmc_ifflags[vmc_nnics] |= VMIFF_RDOMAIN; 70573a98491Sdv vmc.vmc_ifrdomain[vmc_nnics] = $2; 706f418e70cSreyk } 707789e0822Sreyk | updown { 708789e0822Sreyk if ($1) 70973a98491Sdv vmc.vmc_ifflags[vmc_nnics] |= VMIFF_UP; 710789e0822Sreyk else 71173a98491Sdv vmc.vmc_ifflags[vmc_nnics] &= ~VMIFF_UP; 712789e0822Sreyk } 713789e0822Sreyk ; 714789e0822Sreyk 715789e0822Sreyk optstring : STRING { $$ = $1; } 716789e0822Sreyk | /* empty */ { $$ = NULL; } 717789e0822Sreyk ; 718789e0822Sreyk 719458ed070Sreyk string : STRING string { 720458ed070Sreyk if (asprintf(&$$, "%s%s", $1, $2) == -1) 721458ed070Sreyk fatal("asprintf string"); 722458ed070Sreyk free($1); 723458ed070Sreyk free($2); 724458ed070Sreyk } 725458ed070Sreyk | STRING 726458ed070Sreyk ; 727458ed070Sreyk 728789e0822Sreyk lladdr : STRING { 729789e0822Sreyk struct ether_addr *ea; 730789e0822Sreyk 731789e0822Sreyk if ((ea = ether_aton($1)) == NULL) { 732789e0822Sreyk yyerror("invalid address: %s\n", $1); 733789e0822Sreyk free($1); 734789e0822Sreyk YYERROR; 735789e0822Sreyk } 736789e0822Sreyk free($1); 737789e0822Sreyk 738789e0822Sreyk memcpy($$, ea, ETHER_ADDR_LEN); 739789e0822Sreyk } 7406153f04aSdv | /* empty */ { 7416153f04aSdv memset($$, 0, ETHER_ADDR_LEN); 7426153f04aSdv } 743789e0822Sreyk ; 744789e0822Sreyk 745470adcf5Sreyk local : /* empty */ { $$ = 0; } 746470adcf5Sreyk | LOCAL { $$ = 1; } 747470adcf5Sreyk ; 748470adcf5Sreyk 7492b2a5f0dSreyk locked : /* empty */ { $$ = 0; } 7502b2a5f0dSreyk | LOCKED { $$ = 1; } 7512b2a5f0dSreyk ; 7522b2a5f0dSreyk 753789e0822Sreyk updown : UP { $$ = 1; } 754789e0822Sreyk | DOWN { $$ = 0; } 755789e0822Sreyk ; 756789e0822Sreyk 757f01317bcSreyk disable : ENABLE { $$ = 0; } 758f01317bcSreyk | DISABLE { $$ = 1; } 759f01317bcSreyk ; 760f01317bcSreyk 761f4b47ae8Sbluhm sev : SEV { $$ = 1; } 762f4b47ae8Sbluhm ; 763f4b47ae8Sbluhm 764c5ee7fb3Santon bootdevice : CDROM { $$ = VMBOOTDEV_CDROM; } 765c5ee7fb3Santon | DISK { $$ = VMBOOTDEV_DISK; } 766c5ee7fb3Santon | NET { $$ = VMBOOTDEV_NET; } 767c5ee7fb3Santon ; 768c5ee7fb3Santon 7693e9d8149Sreyk optcomma : ',' 7703e9d8149Sreyk | 7713e9d8149Sreyk ; 7723e9d8149Sreyk 773f01317bcSreyk optnl : '\n' optnl 774f01317bcSreyk | 775f01317bcSreyk ; 776f01317bcSreyk 777476d73d1Sreyk optcommanl : ',' optnl 778476d73d1Sreyk | nl 779476d73d1Sreyk ; 780476d73d1Sreyk 781f01317bcSreyk nl : '\n' optnl 782f01317bcSreyk ; 783f01317bcSreyk 784f01317bcSreyk %% 785f01317bcSreyk 786f01317bcSreyk struct keywords { 787f01317bcSreyk const char *k_name; 788f01317bcSreyk int k_val; 789f01317bcSreyk }; 790f01317bcSreyk 791f01317bcSreyk int 792f01317bcSreyk yyerror(const char *fmt, ...) 793f01317bcSreyk { 794f01317bcSreyk va_list ap; 795f01317bcSreyk char *msg; 796f01317bcSreyk 797f01317bcSreyk file->errors++; 798f01317bcSreyk va_start(ap, fmt); 799f01317bcSreyk if (vasprintf(&msg, fmt, ap) == -1) 800f01317bcSreyk fatal("yyerror vasprintf"); 801f01317bcSreyk va_end(ap); 8021dbf51d9Sreyk log_warnx("%s:%d: %s", file->name, yylval.lineno, msg); 803f01317bcSreyk free(msg); 804f01317bcSreyk return (0); 805f01317bcSreyk } 806f01317bcSreyk 807f01317bcSreyk int 808f01317bcSreyk kw_cmp(const void *k, const void *e) 809f01317bcSreyk { 810f01317bcSreyk return (strcmp(k, ((const struct keywords *)e)->k_name)); 811f01317bcSreyk } 812f01317bcSreyk 813f01317bcSreyk int 814f01317bcSreyk lookup(char *s) 815f01317bcSreyk { 816f01317bcSreyk /* this has to be sorted always */ 817f01317bcSreyk static const struct keywords keywords[] = { 818789e0822Sreyk { "add", ADD }, 819f94ca20eSmartijn { "agentx", AGENTX }, 820476d73d1Sreyk { "allow", ALLOW }, 8216b03ca83Sreyk { "boot", BOOT }, 82295ab188fSccardenas { "cdrom", CDROM }, 823f94ca20eSmartijn { "context", CONTEXT}, 824de12a377Spd { "delay", DELAY }, 825c5ee7fb3Santon { "device", DEVICE }, 826f01317bcSreyk { "disable", DISABLE }, 827f01317bcSreyk { "disk", DISK }, 828789e0822Sreyk { "down", DOWN }, 829f01317bcSreyk { "enable", ENABLE }, 830f224f92aSccardenas { "format", FORMAT }, 8312b519c1fSreyk { "group", GROUP }, 832f01317bcSreyk { "id", VMID }, 833f01317bcSreyk { "include", INCLUDE }, 834723f86d2Sreyk { "inet6", INET6 }, 8356429e633Sreyk { "instance", INSTANCE }, 836789e0822Sreyk { "interface", INTERFACE }, 837f01317bcSreyk { "interfaces", NIFS }, 838789e0822Sreyk { "lladdr", LLADDR }, 839470adcf5Sreyk { "local", LOCAL }, 8402b2a5f0dSreyk { "locked", LOCKED }, 841f01317bcSreyk { "memory", MEMORY }, 842c5ee7fb3Santon { "net", NET }, 843e5d5b350Sreyk { "owner", OWNER }, 844de12a377Spd { "parallel", PARALLEL }, 845f94ca20eSmartijn { "path", PATH }, 846c48cfcf4Sreyk { "prefix", PREFIX }, 847f418e70cSreyk { "rdomain", RDOMAIN }, 848f4b47ae8Sbluhm { "sev", SEV }, 849f01317bcSreyk { "size", SIZE }, 8506cfffd57Sreyk { "socket", SOCKET }, 851de12a377Spd { "staggered", STAGGERED }, 852de12a377Spd { "start", START }, 853789e0822Sreyk { "switch", SWITCH }, 854789e0822Sreyk { "up", UP }, 855f01317bcSreyk { "vm", VM } 856f01317bcSreyk }; 857f01317bcSreyk const struct keywords *p; 858f01317bcSreyk 859f01317bcSreyk p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 860f01317bcSreyk sizeof(keywords[0]), kw_cmp); 861f01317bcSreyk 862f01317bcSreyk if (p) 863f01317bcSreyk return (p->k_val); 864f01317bcSreyk else 865f01317bcSreyk return (STRING); 866f01317bcSreyk } 867f01317bcSreyk 868f61f6eccSdenis #define START_EXPAND 1 869f61f6eccSdenis #define DONE_EXPAND 2 870f01317bcSreyk 871f61f6eccSdenis static int expanding; 872f61f6eccSdenis 873f61f6eccSdenis int 874f61f6eccSdenis igetc(void) 875f61f6eccSdenis { 876f61f6eccSdenis int c; 877f61f6eccSdenis 878f61f6eccSdenis while (1) { 879f61f6eccSdenis if (file->ungetpos > 0) 880f61f6eccSdenis c = file->ungetbuf[--file->ungetpos]; 881f61f6eccSdenis else 882f61f6eccSdenis c = getc(file->stream); 883f61f6eccSdenis 884f61f6eccSdenis if (c == START_EXPAND) 885f61f6eccSdenis expanding = 1; 886f61f6eccSdenis else if (c == DONE_EXPAND) 887f61f6eccSdenis expanding = 0; 888f61f6eccSdenis else 889f61f6eccSdenis break; 890f61f6eccSdenis } 891f61f6eccSdenis return (c); 892f61f6eccSdenis } 893f01317bcSreyk 894f01317bcSreyk int 895f01317bcSreyk lgetc(int quotec) 896f01317bcSreyk { 897f01317bcSreyk int c, next; 898f01317bcSreyk 899f01317bcSreyk if (quotec) { 900f61f6eccSdenis if ((c = igetc()) == EOF) { 901f01317bcSreyk yyerror("reached end of file while parsing " 902f01317bcSreyk "quoted string"); 903f01317bcSreyk if (file == topfile || popfile() == EOF) 904f01317bcSreyk return (EOF); 905f01317bcSreyk return (quotec); 906f01317bcSreyk } 907f01317bcSreyk return (c); 908f01317bcSreyk } 909f01317bcSreyk 910f61f6eccSdenis while ((c = igetc()) == '\\') { 911f61f6eccSdenis next = igetc(); 912f01317bcSreyk if (next != '\n') { 913f01317bcSreyk c = next; 914f01317bcSreyk break; 915f01317bcSreyk } 916f01317bcSreyk yylval.lineno = file->lineno; 917f01317bcSreyk file->lineno++; 918f01317bcSreyk } 919f01317bcSreyk if (c == '\t' || c == ' ') { 920f01317bcSreyk /* Compress blanks to a single space. */ 921f01317bcSreyk do { 922f01317bcSreyk c = getc(file->stream); 923f01317bcSreyk } while (c == '\t' || c == ' '); 924f01317bcSreyk ungetc(c, file->stream); 925f01317bcSreyk c = ' '; 926f01317bcSreyk } 927f01317bcSreyk 928f61f6eccSdenis if (c == EOF) { 929f61f6eccSdenis /* 930f61f6eccSdenis * Fake EOL when hit EOF for the first time. This gets line 931f61f6eccSdenis * count right if last line in included file is syntactically 932f61f6eccSdenis * invalid and has no newline. 933f61f6eccSdenis */ 934f61f6eccSdenis if (file->eof_reached == 0) { 935f61f6eccSdenis file->eof_reached = 1; 936f61f6eccSdenis return ('\n'); 937f61f6eccSdenis } 938f01317bcSreyk while (c == EOF) { 939f01317bcSreyk if (file == topfile || popfile() == EOF) 940f01317bcSreyk return (EOF); 941f61f6eccSdenis c = igetc(); 942f61f6eccSdenis } 943f01317bcSreyk } 944f01317bcSreyk return (c); 945f01317bcSreyk } 946f01317bcSreyk 947f61f6eccSdenis void 948f01317bcSreyk lungetc(int c) 949f01317bcSreyk { 950f01317bcSreyk if (c == EOF) 951f61f6eccSdenis return; 952f61f6eccSdenis 953f61f6eccSdenis if (file->ungetpos >= file->ungetsize) { 954f61f6eccSdenis void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 955f61f6eccSdenis if (p == NULL) 956a062aa9dSkrw err(1, "%s", __func__); 957f61f6eccSdenis file->ungetbuf = p; 958f61f6eccSdenis file->ungetsize *= 2; 959f01317bcSreyk } 960f61f6eccSdenis file->ungetbuf[file->ungetpos++] = c; 961f01317bcSreyk } 962f01317bcSreyk 963f01317bcSreyk int 964f01317bcSreyk findeol(void) 965f01317bcSreyk { 966f01317bcSreyk int c; 967f01317bcSreyk 968f01317bcSreyk /* skip to either EOF or the first real EOL */ 969f01317bcSreyk while (1) { 970f01317bcSreyk c = lgetc(0); 971f01317bcSreyk if (c == '\n') { 972f01317bcSreyk file->lineno++; 973f01317bcSreyk break; 974f01317bcSreyk } 975f01317bcSreyk if (c == EOF) 976f01317bcSreyk break; 977f01317bcSreyk } 978f01317bcSreyk return (ERROR); 979f01317bcSreyk } 980f01317bcSreyk 981f01317bcSreyk int 982f01317bcSreyk yylex(void) 983f01317bcSreyk { 98408f6ba19Snaddy char buf[8096]; 98508f6ba19Snaddy char *p, *val; 986f01317bcSreyk int quotec, next, c; 987f01317bcSreyk int token; 988f01317bcSreyk 989f01317bcSreyk top: 990f01317bcSreyk p = buf; 991f01317bcSreyk while ((c = lgetc(0)) == ' ' || c == '\t') 992f01317bcSreyk ; /* nothing */ 993f01317bcSreyk 994f01317bcSreyk yylval.lineno = file->lineno; 995f01317bcSreyk if (c == '#') 996f01317bcSreyk while ((c = lgetc(0)) != '\n' && c != EOF) 997f01317bcSreyk ; /* nothing */ 998f61f6eccSdenis if (c == '$' && !expanding) { 999f01317bcSreyk while (1) { 1000f01317bcSreyk if ((c = lgetc(0)) == EOF) 1001f01317bcSreyk return (0); 1002f01317bcSreyk 1003f01317bcSreyk if (p + 1 >= buf + sizeof(buf) - 1) { 1004f01317bcSreyk yyerror("string too long"); 1005f01317bcSreyk return (findeol()); 1006f01317bcSreyk } 1007f01317bcSreyk if (isalnum(c) || c == '_') { 1008f01317bcSreyk *p++ = c; 1009f01317bcSreyk continue; 1010f01317bcSreyk } 1011f01317bcSreyk *p = '\0'; 1012f01317bcSreyk lungetc(c); 1013f01317bcSreyk break; 1014f01317bcSreyk } 1015f01317bcSreyk val = symget(buf); 1016f01317bcSreyk if (val == NULL) { 1017f01317bcSreyk yyerror("macro '%s' not defined", buf); 1018f01317bcSreyk return (findeol()); 1019f01317bcSreyk } 1020f61f6eccSdenis p = val + strlen(val) - 1; 1021f61f6eccSdenis lungetc(DONE_EXPAND); 1022f61f6eccSdenis while (p >= val) { 102308f6ba19Snaddy lungetc((unsigned char)*p); 1024f61f6eccSdenis p--; 1025f61f6eccSdenis } 1026f61f6eccSdenis lungetc(START_EXPAND); 1027f01317bcSreyk goto top; 1028f01317bcSreyk } 1029f01317bcSreyk 1030f01317bcSreyk switch (c) { 1031f01317bcSreyk case '\'': 1032f01317bcSreyk case '"': 1033f01317bcSreyk quotec = c; 1034f01317bcSreyk while (1) { 1035f01317bcSreyk if ((c = lgetc(quotec)) == EOF) 1036f01317bcSreyk return (0); 1037f01317bcSreyk if (c == '\n') { 1038f01317bcSreyk file->lineno++; 1039f01317bcSreyk continue; 1040f01317bcSreyk } else if (c == '\\') { 1041f01317bcSreyk if ((next = lgetc(quotec)) == EOF) 1042f01317bcSreyk return (0); 1043a1533359Ssashan if (next == quotec || next == ' ' || 1044a1533359Ssashan next == '\t') 1045f01317bcSreyk c = next; 1046f01317bcSreyk else if (next == '\n') { 1047f01317bcSreyk file->lineno++; 1048f01317bcSreyk continue; 1049f01317bcSreyk } else 1050f01317bcSreyk lungetc(next); 1051f01317bcSreyk } else if (c == quotec) { 1052f01317bcSreyk *p = '\0'; 1053f01317bcSreyk break; 1054f01317bcSreyk } else if (c == '\0') { 1055f01317bcSreyk yyerror("syntax error"); 1056f01317bcSreyk return (findeol()); 1057f01317bcSreyk } 1058f01317bcSreyk if (p + 1 >= buf + sizeof(buf) - 1) { 1059f01317bcSreyk yyerror("string too long"); 1060f01317bcSreyk return (findeol()); 1061f01317bcSreyk } 1062f01317bcSreyk *p++ = c; 1063f01317bcSreyk } 1064f01317bcSreyk yylval.v.string = strdup(buf); 1065f01317bcSreyk if (yylval.v.string == NULL) 1066f01317bcSreyk fatal("yylex: strdup"); 1067f01317bcSreyk return (STRING); 1068f01317bcSreyk } 1069f01317bcSreyk 1070f01317bcSreyk #define allowed_to_end_number(x) \ 1071f01317bcSreyk (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 1072f01317bcSreyk 1073f01317bcSreyk if (c == '-' || isdigit(c)) { 1074f01317bcSreyk do { 1075f01317bcSreyk *p++ = c; 1076915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) { 1077f01317bcSreyk yyerror("string too long"); 1078f01317bcSreyk return (findeol()); 1079f01317bcSreyk } 1080f01317bcSreyk } while ((c = lgetc(0)) != EOF && isdigit(c)); 1081f01317bcSreyk lungetc(c); 1082f01317bcSreyk if (p == buf + 1 && buf[0] == '-') 1083f01317bcSreyk goto nodigits; 1084f01317bcSreyk if (c == EOF || allowed_to_end_number(c)) { 1085f01317bcSreyk const char *errstr = NULL; 1086f01317bcSreyk 1087f01317bcSreyk *p = '\0'; 1088f01317bcSreyk yylval.v.number = strtonum(buf, LLONG_MIN, 1089f01317bcSreyk LLONG_MAX, &errstr); 1090f01317bcSreyk if (errstr) { 1091f01317bcSreyk yyerror("\"%s\" invalid number: %s", 1092f01317bcSreyk buf, errstr); 1093f01317bcSreyk return (findeol()); 1094f01317bcSreyk } 1095f01317bcSreyk return (NUMBER); 1096f01317bcSreyk } else { 1097f01317bcSreyk nodigits: 1098f01317bcSreyk while (p > buf + 1) 109908f6ba19Snaddy lungetc((unsigned char)*--p); 110008f6ba19Snaddy c = (unsigned char)*--p; 1101f01317bcSreyk if (c == '-') 1102f01317bcSreyk return (c); 1103f01317bcSreyk } 1104f01317bcSreyk } 1105f01317bcSreyk 1106f01317bcSreyk #define allowed_in_string(x) \ 1107f01317bcSreyk (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 1108f01317bcSreyk x != '{' && x != '}' && \ 1109f01317bcSreyk x != '!' && x != '=' && x != '#' && \ 1110f01317bcSreyk x != ',')) 1111f01317bcSreyk 1112458ed070Sreyk if (isalnum(c) || c == ':' || c == '_' || c == '/') { 1113f01317bcSreyk do { 1114f01317bcSreyk *p++ = c; 1115915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) { 1116f01317bcSreyk yyerror("string too long"); 1117f01317bcSreyk return (findeol()); 1118f01317bcSreyk } 1119f01317bcSreyk } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 1120f01317bcSreyk lungetc(c); 1121f01317bcSreyk *p = '\0'; 1122f01317bcSreyk if ((token = lookup(buf)) == STRING) 1123f01317bcSreyk if ((yylval.v.string = strdup(buf)) == NULL) 1124f01317bcSreyk fatal("yylex: strdup"); 1125f01317bcSreyk return (token); 1126f01317bcSreyk } 1127f01317bcSreyk if (c == '\n') { 1128f01317bcSreyk yylval.lineno = file->lineno; 1129f01317bcSreyk file->lineno++; 1130f01317bcSreyk } 1131f01317bcSreyk if (c == EOF) 1132f01317bcSreyk return (0); 1133f01317bcSreyk return (c); 1134f01317bcSreyk } 1135f01317bcSreyk 1136f01317bcSreyk struct file * 1137f01317bcSreyk pushfile(const char *name, int secret) 1138f01317bcSreyk { 1139f01317bcSreyk struct file *nfile; 1140f01317bcSreyk 1141f01317bcSreyk if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 11426a3d55f9Skrw log_warn("%s", __func__); 1143f01317bcSreyk return (NULL); 1144f01317bcSreyk } 1145f01317bcSreyk if ((nfile->name = strdup(name)) == NULL) { 11466a3d55f9Skrw log_warn("%s", __func__); 1147f01317bcSreyk free(nfile); 1148f01317bcSreyk return (NULL); 1149f01317bcSreyk } 1150f01317bcSreyk if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 1151f01317bcSreyk free(nfile->name); 1152f01317bcSreyk free(nfile); 1153f01317bcSreyk return (NULL); 1154f01317bcSreyk } 1155f61f6eccSdenis nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 1156f61f6eccSdenis nfile->ungetsize = 16; 1157f61f6eccSdenis nfile->ungetbuf = malloc(nfile->ungetsize); 1158f61f6eccSdenis if (nfile->ungetbuf == NULL) { 11596a3d55f9Skrw log_warn("%s", __func__); 1160f61f6eccSdenis fclose(nfile->stream); 1161f61f6eccSdenis free(nfile->name); 1162f61f6eccSdenis free(nfile); 1163f61f6eccSdenis return (NULL); 1164f61f6eccSdenis } 1165f01317bcSreyk TAILQ_INSERT_TAIL(&files, nfile, entry); 1166f01317bcSreyk return (nfile); 1167f01317bcSreyk } 1168f01317bcSreyk 1169f01317bcSreyk int 1170f01317bcSreyk popfile(void) 1171f01317bcSreyk { 1172f01317bcSreyk struct file *prev; 1173f01317bcSreyk 1174f01317bcSreyk if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 1175f01317bcSreyk prev->errors += file->errors; 1176f01317bcSreyk 1177f01317bcSreyk TAILQ_REMOVE(&files, file, entry); 1178f01317bcSreyk fclose(file->stream); 1179f01317bcSreyk free(file->name); 1180f61f6eccSdenis free(file->ungetbuf); 1181f01317bcSreyk free(file); 1182f01317bcSreyk file = prev; 1183f01317bcSreyk return (file ? 0 : EOF); 1184f01317bcSreyk } 1185f01317bcSreyk 1186f01317bcSreyk int 1187f01317bcSreyk parse_config(const char *filename) 1188f01317bcSreyk { 11895d396404Skn extern const char default_conffile[]; 1190f01317bcSreyk struct sym *sym, *next; 1191f01317bcSreyk 1192f01317bcSreyk if ((file = pushfile(filename, 0)) == NULL) { 11935d396404Skn /* no default config file is fine */ 11945d396404Skn if (errno == ENOENT && filename == default_conffile) { 11955d396404Skn log_debug("%s: missing", filename); 11965d396404Skn return (0); 11975d396404Skn } 1198f01317bcSreyk log_warn("failed to open %s", filename); 11998d6f4d1bSreyk if (errno == ENOENT) 1200f01317bcSreyk return (0); 12018d6f4d1bSreyk return (-1); 1202f01317bcSreyk } 1203f01317bcSreyk topfile = file; 1204f01317bcSreyk setservent(1); 1205f01317bcSreyk 1206789e0822Sreyk /* Set the default switch type */ 1207789e0822Sreyk (void)strlcpy(vsw_type, VMD_SWITCH_TYPE, sizeof(vsw_type)); 1208789e0822Sreyk 1209f94ca20eSmartijn env->vmd_cfg.cfg_agentx.ax_enabled = 0; 1210f94ca20eSmartijn env->vmd_cfg.cfg_agentx.ax_context[0] = '\0'; 1211f94ca20eSmartijn env->vmd_cfg.cfg_agentx.ax_path[0] = '\0'; 1212f94ca20eSmartijn 1213f01317bcSreyk yyparse(); 1214f01317bcSreyk errors = file->errors; 1215f01317bcSreyk popfile(); 1216f01317bcSreyk 1217f01317bcSreyk endservent(); 1218f01317bcSreyk 1219f01317bcSreyk /* Free macros and check which have not been used. */ 122046bca67bSkrw TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 1221f01317bcSreyk if (!sym->used) 1222f01317bcSreyk fprintf(stderr, "warning: macro '%s' not " 1223f01317bcSreyk "used\n", sym->nam); 1224f01317bcSreyk if (!sym->persist) { 1225f01317bcSreyk free(sym->nam); 1226f01317bcSreyk free(sym->val); 1227f01317bcSreyk TAILQ_REMOVE(&symhead, sym, entry); 1228f01317bcSreyk free(sym); 1229f01317bcSreyk } 1230f01317bcSreyk } 1231f01317bcSreyk 1232f01317bcSreyk if (errors) 1233f01317bcSreyk return (-1); 1234f01317bcSreyk 1235f01317bcSreyk return (0); 1236f01317bcSreyk } 1237f01317bcSreyk 1238f01317bcSreyk int 1239f01317bcSreyk symset(const char *nam, const char *val, int persist) 1240f01317bcSreyk { 1241f01317bcSreyk struct sym *sym; 1242f01317bcSreyk 124354c95b7aSkrw TAILQ_FOREACH(sym, &symhead, entry) { 124454c95b7aSkrw if (strcmp(nam, sym->nam) == 0) 124554c95b7aSkrw break; 124654c95b7aSkrw } 1247f01317bcSreyk 1248f01317bcSreyk if (sym != NULL) { 1249f01317bcSreyk if (sym->persist == 1) 1250f01317bcSreyk return (0); 1251f01317bcSreyk else { 1252f01317bcSreyk free(sym->nam); 1253f01317bcSreyk free(sym->val); 1254f01317bcSreyk TAILQ_REMOVE(&symhead, sym, entry); 1255f01317bcSreyk free(sym); 1256f01317bcSreyk } 1257f01317bcSreyk } 1258f01317bcSreyk if ((sym = calloc(1, sizeof(*sym))) == NULL) 1259f01317bcSreyk return (-1); 1260f01317bcSreyk 1261f01317bcSreyk sym->nam = strdup(nam); 1262f01317bcSreyk if (sym->nam == NULL) { 1263f01317bcSreyk free(sym); 1264f01317bcSreyk return (-1); 1265f01317bcSreyk } 1266f01317bcSreyk sym->val = strdup(val); 1267f01317bcSreyk if (sym->val == NULL) { 1268f01317bcSreyk free(sym->nam); 1269f01317bcSreyk free(sym); 1270f01317bcSreyk return (-1); 1271f01317bcSreyk } 1272f01317bcSreyk sym->used = 0; 1273f01317bcSreyk sym->persist = persist; 1274f01317bcSreyk TAILQ_INSERT_TAIL(&symhead, sym, entry); 1275f01317bcSreyk return (0); 1276f01317bcSreyk } 1277f01317bcSreyk 1278f01317bcSreyk int 1279f01317bcSreyk cmdline_symset(char *s) 1280f01317bcSreyk { 1281f01317bcSreyk char *sym, *val; 1282f01317bcSreyk int ret; 1283f01317bcSreyk 1284f01317bcSreyk if ((val = strrchr(s, '=')) == NULL) 1285f01317bcSreyk return (-1); 1286ed1b9eb8Smiko sym = strndup(s, val - s); 1287ed1b9eb8Smiko if (sym == NULL) 1288ed1b9eb8Smiko fatal("%s: strndup", __func__); 1289f01317bcSreyk ret = symset(sym, val + 1, 1); 1290f01317bcSreyk free(sym); 1291f01317bcSreyk 1292f01317bcSreyk return (ret); 1293f01317bcSreyk } 1294f01317bcSreyk 1295f01317bcSreyk char * 1296f01317bcSreyk symget(const char *nam) 1297f01317bcSreyk { 1298f01317bcSreyk struct sym *sym; 1299f01317bcSreyk 130054c95b7aSkrw TAILQ_FOREACH(sym, &symhead, entry) { 1301f01317bcSreyk if (strcmp(nam, sym->nam) == 0) { 1302f01317bcSreyk sym->used = 1; 1303f01317bcSreyk return (sym->val); 1304f01317bcSreyk } 130554c95b7aSkrw } 1306f01317bcSreyk return (NULL); 1307f01317bcSreyk } 1308f01317bcSreyk 1309f01317bcSreyk ssize_t 1310f01317bcSreyk parse_size(char *word, int64_t val) 1311f01317bcSreyk { 1312e545c54cSdv char result[FMT_SCALED_STRSIZE]; 1313f01317bcSreyk ssize_t size; 1314f01317bcSreyk long long res; 1315f01317bcSreyk 1316f01317bcSreyk if (word != NULL) { 1317f01317bcSreyk if (scan_scaled(word, &res) != 0) { 1318e545c54cSdv log_warn("invalid memory size: %s", word); 1319f01317bcSreyk return (-1); 1320f01317bcSreyk } 1321f01317bcSreyk val = (int64_t)res; 1322f01317bcSreyk } 1323f01317bcSreyk 1324f01317bcSreyk if (val < (1024 * 1024)) { 1325e545c54cSdv log_warnx("memory size must be at least 1MB"); 1326f01317bcSreyk return (-1); 1327e545c54cSdv } 1328f01317bcSreyk 1329e545c54cSdv if (val > VMM_MAX_VM_MEM_SIZE) { 1330e545c54cSdv if (fmt_scaled(VMM_MAX_VM_MEM_SIZE, result) == 0) 1331e545c54cSdv log_warnx("memory size too large (limit is %s)", 1332e545c54cSdv result); 1333e545c54cSdv else 1334e545c54cSdv log_warnx("memory size too large"); 1335e545c54cSdv return (-1); 1336e545c54cSdv } 1337e545c54cSdv 1338e545c54cSdv /* Round down to the megabyte. */ 1339e545c54cSdv size = (val / (1024 * 1024)) * (1024 * 1024); 1340e545c54cSdv 1341e545c54cSdv if (size != val) { 1342e545c54cSdv if (fmt_scaled(size, result) == 0) 1343ead1b146Sdv log_debug("memory size rounded to %s", result); 1344e545c54cSdv else 1345ead1b146Sdv log_debug("memory size rounded to %zd bytes", size); 1346e545c54cSdv } 1347f01317bcSreyk 1348f01317bcSreyk return ((ssize_t)size); 1349f01317bcSreyk } 1350f01317bcSreyk 1351f01317bcSreyk int 1352f224f92aSccardenas parse_disk(char *word, int type) 1353f01317bcSreyk { 1354e2ceadc1Sreyk char buf[BUFSIZ], path[PATH_MAX]; 1355e2ceadc1Sreyk int fd; 1356e2ceadc1Sreyk ssize_t len; 13576429e633Sreyk 135873a98491Sdv if (vmc.vmc_ndisks >= VM_MAX_DISKS_PER_VM) { 1359f01317bcSreyk log_warnx("too many disks"); 1360f01317bcSreyk return (-1); 1361f01317bcSreyk } 1362f01317bcSreyk 13636429e633Sreyk if (realpath(word, path) == NULL) { 13646429e633Sreyk log_warn("disk %s", word); 13656429e633Sreyk return (-1); 13666429e633Sreyk } 13676429e633Sreyk 1368e2ceadc1Sreyk if (!type) { 1369e2ceadc1Sreyk /* Use raw as the default format */ 1370e2ceadc1Sreyk type = VMDF_RAW; 1371e2ceadc1Sreyk 1372e2ceadc1Sreyk /* Try to derive the format from the file signature */ 1373e2ceadc1Sreyk if ((fd = open(path, O_RDONLY)) != -1) { 1374e2ceadc1Sreyk len = read(fd, buf, sizeof(buf)); 1375e2ceadc1Sreyk close(fd); 1376e2ceadc1Sreyk if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) && 1377e2ceadc1Sreyk strncmp(buf, VM_MAGIC_QCOW, 1378e2ceadc1Sreyk strlen(VM_MAGIC_QCOW)) == 0) { 1379e2ceadc1Sreyk /* The qcow version will be checked later */ 1380e2ceadc1Sreyk type = VMDF_QCOW2; 1381e2ceadc1Sreyk } 1382e2ceadc1Sreyk } 1383e2ceadc1Sreyk } 1384e2ceadc1Sreyk 138573a98491Sdv if (strlcpy(vmc.vmc_disks[vmc.vmc_ndisks], path, 138673a98491Sdv sizeof(vmc.vmc_disks[vmc.vmc_ndisks])) >= 138773a98491Sdv sizeof(vmc.vmc_disks[vmc.vmc_ndisks])) { 1388f01317bcSreyk log_warnx("disk path too long"); 1389f01317bcSreyk return (-1); 1390f01317bcSreyk } 139173a98491Sdv vmc.vmc_disktypes[vmc.vmc_ndisks] = type; 1392f01317bcSreyk 139373a98491Sdv vmc.vmc_ndisks++; 1394f01317bcSreyk 1395f01317bcSreyk return (0); 1396f01317bcSreyk } 1397c48cfcf4Sreyk 1398e2ceadc1Sreyk unsigned int 1399e2ceadc1Sreyk parse_format(const char *word) 1400e2ceadc1Sreyk { 1401e2ceadc1Sreyk if (strcasecmp(word, "raw") == 0) 1402e2ceadc1Sreyk return (VMDF_RAW); 1403e2ceadc1Sreyk else if (strcasecmp(word, "qcow2") == 0) 1404e2ceadc1Sreyk return (VMDF_QCOW2); 1405e2ceadc1Sreyk return (0); 1406e2ceadc1Sreyk } 1407e2ceadc1Sreyk 14082272e586Sdv /* 14092272e586Sdv * Parse an ipv4 address and prefix for local interfaces and validate 14102272e586Sdv * constraints for vmd networking. 14112272e586Sdv */ 1412c48cfcf4Sreyk int 14132272e586Sdv parse_prefix4(const char *str, struct local_prefix *out, const char **errstr) 1414c48cfcf4Sreyk { 14152272e586Sdv struct addrinfo hints, *res = NULL; 14162272e586Sdv struct sockaddr_storage ss; 14172272e586Sdv struct in_addr addr; 14182272e586Sdv int mask = 16; 14192272e586Sdv char *p, *ps; 1420c48cfcf4Sreyk 14212272e586Sdv if ((ps = strdup(str)) == NULL) 14222272e586Sdv fatal("%s: strdup", __func__); 14232272e586Sdv 14242272e586Sdv if ((p = strrchr(ps, '/')) != NULL) { 14252272e586Sdv mask = strtonum(p + 1, 1, 16, errstr); 14262272e586Sdv if (errstr != NULL && *errstr) { 14272272e586Sdv free(ps); 14282272e586Sdv return (1); 14292272e586Sdv } 14302272e586Sdv p[0] = '\0'; 1431c48cfcf4Sreyk } 1432c48cfcf4Sreyk 14332272e586Sdv /* Attempt to construct an address from the user input. */ 1434c48cfcf4Sreyk memset(&hints, 0, sizeof(hints)); 14352272e586Sdv hints.ai_family = AF_INET; 14362272e586Sdv hints.ai_socktype = SOCK_DGRAM; 1437c48cfcf4Sreyk hints.ai_flags = AI_NUMERICHOST; 14382272e586Sdv 14392272e586Sdv if (getaddrinfo(ps, NULL, &hints, &res) == 0) { 14402272e586Sdv memset(&ss, 0, sizeof(ss)); 14412272e586Sdv memcpy(&ss, res->ai_addr, res->ai_addrlen); 14422272e586Sdv addr.s_addr = ss2sin(&ss)->sin_addr.s_addr; 1443c48cfcf4Sreyk freeaddrinfo(res); 14442272e586Sdv } else { /* try 10/8 parsing */ 14452272e586Sdv memset(&addr, 0, sizeof(addr)); 14462272e586Sdv if (inet_net_pton(AF_INET, ps, &addr, sizeof(addr)) == -1) { 14472272e586Sdv if (errstr) 14482272e586Sdv *errstr = "invalid format"; 14492272e586Sdv free(ps); 14502272e586Sdv return (1); 14512272e586Sdv } 14522272e586Sdv } 14532272e586Sdv free(ps); 14542272e586Sdv 14552272e586Sdv /* 14562272e586Sdv * Validate the prefix by comparing it with the mask. Since we 14572272e586Sdv * constrain the mask length to 16 above, this also validates 14582272e586Sdv * we reserve the last 16 bits for use by vmd to assign vm id 14592272e586Sdv * and interface id. 14602272e586Sdv */ 14612272e586Sdv if ((addr.s_addr & prefixlen2mask(mask)) != addr.s_addr) { 14622272e586Sdv if (errstr) 14632272e586Sdv *errstr = "bad mask"; 14642272e586Sdv return (1); 14652272e586Sdv } 14662272e586Sdv 14672272e586Sdv /* Copy out the local prefix. */ 14682272e586Sdv out->lp_in.s_addr = addr.s_addr; 14692272e586Sdv out->lp_mask.s_addr = prefixlen2mask(mask); 1470c48cfcf4Sreyk return (0); 1471c48cfcf4Sreyk } 1472c48cfcf4Sreyk 14732272e586Sdv /* 14742272e586Sdv * Parse an ipv6 address and prefix for local interfaces and validate 14752272e586Sdv * constraints for vmd networking. 14762272e586Sdv */ 14772272e586Sdv int 14782272e586Sdv parse_prefix6(const char *str, struct local_prefix *out, const char **errstr) 14792272e586Sdv { 14802272e586Sdv struct addrinfo hints, *res = NULL; 14812272e586Sdv struct sockaddr_storage ss; 14822272e586Sdv struct in6_addr addr6, mask6; 14832272e586Sdv size_t i; 14842272e586Sdv int mask = 64, err; 14852272e586Sdv char *p, *ps; 14862272e586Sdv 14872272e586Sdv if ((ps = strdup(str)) == NULL) 14882272e586Sdv fatal("%s: strdup", __func__); 14892272e586Sdv 14902272e586Sdv if ((p = strrchr(ps, '/')) != NULL) { 14912272e586Sdv mask = strtonum(p + 1, 0, 64, errstr); 14922272e586Sdv if (errstr != NULL && *errstr) { 14932272e586Sdv free(ps); 14942272e586Sdv return (1); 14952272e586Sdv } 14962272e586Sdv p[0] = '\0'; 14972272e586Sdv } 14982272e586Sdv 14992272e586Sdv /* Attempt to construct an address from the user input. */ 15002272e586Sdv memset(&hints, 0, sizeof(hints)); 15012272e586Sdv hints.ai_family = AF_INET6; 15022272e586Sdv hints.ai_socktype = SOCK_DGRAM; 15032272e586Sdv hints.ai_flags = AI_NUMERICHOST; 15042272e586Sdv 15052272e586Sdv if ((err = getaddrinfo(ps, NULL, &hints, &res)) != 0) { 15062272e586Sdv if (errstr) 15072272e586Sdv *errstr = gai_strerror(err); 15082272e586Sdv free(ps); 15092272e586Sdv return (1); 15102272e586Sdv } 15112272e586Sdv free(ps); 15122272e586Sdv 15132272e586Sdv memset(&ss, 0, sizeof(ss)); 15142272e586Sdv memcpy(&ss, res->ai_addr, res->ai_addrlen); 15152272e586Sdv freeaddrinfo(res); 15162272e586Sdv 15172272e586Sdv memcpy(&addr6, (void*)&ss2sin6(&ss)->sin6_addr, sizeof(addr6)); 15182272e586Sdv prefixlen2mask6(mask, &mask6); 15192272e586Sdv 15202272e586Sdv /* 15212272e586Sdv * Validate the prefix by comparing it with the mask. Since we 15222272e586Sdv * constrain the mask length to 64 above, this also validates 15232272e586Sdv * that we're reserving bits for the encoding of the ipv4 15242272e586Sdv * address, the vm id, and interface id. */ 15252272e586Sdv for (i = 0; i < 16; i++) { 15262272e586Sdv if ((addr6.s6_addr[i] & mask6.s6_addr[i]) != addr6.s6_addr[i]) { 15272272e586Sdv if (errstr) 15282272e586Sdv *errstr = "bad mask"; 15292272e586Sdv return (1); 15302272e586Sdv } 15312272e586Sdv } 15322272e586Sdv 15332272e586Sdv /* Copy out the local prefix. */ 15342272e586Sdv memcpy(&out->lp_in6, &addr6, sizeof(out->lp_in6)); 15352272e586Sdv memcpy(&out->lp_mask6, &mask6, sizeof(out->lp_mask6)); 15362272e586Sdv return (0); 1537c48cfcf4Sreyk } 1538