1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 /* 6 * Copyright (c) 1983 Regents of the University of California. 7 * All rights reserved. The Berkeley software License Agreement 8 * specifies the terms and conditions for redistribution. 9 */ 10 11 /* 12 * Ifparse splits up an ifconfig command line, and was written for use 13 * with the networking boot scripts; see $SRC/cmd/svc/shell/net_include.sh 14 * 15 * Ifparse can extract selected parts of the ifconfig command line, 16 * such as failover address configuration ("ifparse -f"), or everything 17 * except failover address configuration ("ifparse -s"). By default, 18 * all parts of the command line are extracted (equivalent to ("ifparse -fs"). 19 * 20 * Examples: 21 * 22 * The command: 23 * 24 * ifparse inet 1.2.3.4 up group two addif 1.2.3.5 up addif 1.2.3.6 up 25 * 26 * Produces the following on standard output: 27 * 28 * set 1.2.3.4 up 29 * group two 30 * addif 1.2.3.5 up 31 * addif 1.2.3.6 up 32 * 33 * The optional "set" and "destination" keywords are added to make the 34 * output easier to process by a script or another command. 35 * 36 * The command: 37 * 38 * ifparse -f inet 1.2.3.4 -failover up group two addif 1.2.3.5 up 39 * 40 * Produces: 41 * 42 * addif 1.2.3.5 up 43 * 44 * Only failover address configuration has been requested. Address 45 * 1.2.3.4 is a non-failover address, and so isn't output. 46 * 47 * The "failover" and "-failover" commands can occur several times for 48 * a given logical interface. Only the last one counts. For example: 49 * 50 * ifparse -f inet 1.2.3.4 -failover failover -failover failover up 51 * 52 * Produces: 53 * 54 * set 1.2.3.4 -failover failover -failover failover up 55 * 56 * No attempt is made to clean up such "pathological" command lines, by 57 * removing redundant "failover" and "-failover" commands. 58 */ 59 60 #include <sys/types.h> 61 #include <stdlib.h> 62 #include <stdio.h> 63 #include <string.h> 64 #include <assert.h> 65 #include <unistd.h> 66 67 /* 68 * Parser flags: 69 * 70 * PARSEFIXED 71 * Command should only appear if non-failover commands 72 * are requested. 73 * PARSEMOVABLE 74 * Command should only appear if failover commands are 75 * requested. 76 * PARSENOW 77 * Don't buffer the command, dump it to output immediately. 78 * PARSEADD 79 * Indicates processing has moved on to additional 80 * logical interfaces. 81 * Dump the buffer to output and clear buffer contents. 82 * PARSESET 83 * The "set" and "destination" keywords are optional. 84 * This flag indicates that the next address not prefixed 85 * with a keyword will be a destination address. 86 * PARSELOG0 87 * Command not valid on additional logical interfaces. 88 */ 89 90 #define PARSEFIXED 0x01 91 #define PARSEMOVABLE 0x02 92 #define PARSENOW 0x04 93 #define PARSEADD 0x08 94 #define PARSESET 0x10 95 #define PARSELOG0 0x20 96 97 typedef enum { AF_UNSPEC, AF_INET, AF_INET6, AF_ANY } ac_t; 98 99 #define NEXTARG (-1) /* command takes an argument */ 100 #define OPTARG (-2) /* command takes an optional argument */ 101 102 #define END_OF_TABLE (-1) 103 104 /* Parsemode, the type of commands requested by the user. */ 105 int parsemode = 0; 106 107 /* Parsetype, the type of the command currently in the buffer. */ 108 int parsetype = PARSEFIXED | PARSEMOVABLE; 109 110 /* Parsebuf, pointer to the buffer. */ 111 char *parsebuf = NULL; 112 113 /* Parsebuflen, the size of the buffer area. */ 114 unsigned parsebuflen = 0; 115 116 /* Parsedumplen, the amount of the buffer currently in use. */ 117 unsigned parsedumplen = 0; 118 119 /* 120 * Setaddr, used to decide whether an address without a keyword 121 * prefix is a source or destination address. 122 */ 123 boolean_t setaddr = _B_FALSE; 124 125 /* 126 * Some ifconfig commands are only valid on the first logical interface. 127 * As soon as an "addif" command is seen, "addint" is set. 128 */ 129 boolean_t addint = _B_FALSE; 130 131 /* 132 * The parser table is based on that in ifconfig. A command may or 133 * may not have an argument, as indicated by whether NEXTARG/OPTARG is 134 * in the second column. Some commands can only be used with certain 135 * address families, as indicated in the third column. The fourth column 136 * contains flags that control parser action. 137 * 138 * Ifparse buffers logical interface configuration commands such as "set", 139 * "netmask" and "broadcast". This buffering continues until an "addif" 140 * command is seen, at which point the buffer is emptied, and the process 141 * starts again. 142 * 143 * Some commands do not relate to logical interface configuration and are 144 * dumped to output as soon as they are seen, such as "group" and "standby". 145 * 146 */ 147 148 struct cmd { 149 char *c_name; 150 int c_parameter; /* NEXTARG means next argv */ 151 int c_af; /* address family restrictions */ 152 int c_parseflags; /* parsing flags */ 153 } cmds[] = { 154 { "up", 0, AF_ANY, 0 }, 155 { "down", 0, AF_ANY, 0 }, 156 { "trailers", 0, AF_ANY, PARSENOW }, 157 { "-trailers", 0, AF_ANY, PARSENOW }, 158 { "arp", 0, AF_INET, PARSENOW }, 159 { "-arp", 0, AF_INET, PARSENOW }, 160 { "private", 0, AF_ANY, 0 }, 161 { "-private", 0, AF_ANY, 0 }, 162 { "router", 0, AF_ANY, PARSELOG0 }, 163 { "-router", 0, AF_ANY, PARSELOG0 }, 164 { "xmit", 0, AF_ANY, 0 }, 165 { "-xmit", 0, AF_ANY, 0 }, 166 { "-nud", 0, AF_INET6, PARSENOW }, 167 { "nud", 0, AF_INET6, PARSENOW }, 168 { "anycast", 0, AF_ANY, 0 }, 169 { "-anycast", 0, AF_ANY, 0 }, 170 { "local", 0, AF_ANY, 0 }, 171 { "-local", 0, AF_ANY, 0 }, 172 { "deprecated", 0, AF_ANY, 0 }, 173 { "-deprecated", 0, AF_ANY, 0 }, 174 { "preferred", 0, AF_INET6, 0 }, 175 { "-preferred", 0, AF_INET6, 0 }, 176 { "debug", 0, AF_ANY, PARSENOW }, 177 { "verbose", 0, AF_ANY, PARSENOW }, 178 { "netmask", NEXTARG, AF_INET, 0 }, 179 { "metric", NEXTARG, AF_ANY, 0 }, 180 { "mtu", NEXTARG, AF_ANY, 0 }, 181 { "index", NEXTARG, AF_ANY, PARSELOG0 }, 182 { "broadcast", NEXTARG, AF_INET, 0 }, 183 { "auto-revarp", 0, AF_INET, PARSEFIXED}, 184 { "plumb", 0, AF_ANY, PARSENOW }, 185 { "unplumb", 0, AF_ANY, PARSENOW }, 186 { "ipmp", 0, AF_ANY, PARSELOG0 }, 187 { "subnet", NEXTARG, AF_ANY, 0 }, 188 { "token", NEXTARG, AF_INET6, PARSELOG0 }, 189 { "tsrc", NEXTARG, AF_ANY, PARSELOG0 }, 190 { "tdst", NEXTARG, AF_ANY, PARSELOG0 }, 191 { "encr_auth_algs", NEXTARG, AF_ANY, PARSELOG0 }, 192 { "encr_algs", NEXTARG, AF_ANY, PARSELOG0 }, 193 { "auth_algs", NEXTARG, AF_ANY, PARSELOG0 }, 194 { "addif", NEXTARG, AF_ANY, PARSEADD }, 195 { "removeif", NEXTARG, AF_ANY, PARSELOG0 }, 196 { "modlist", 0, AF_ANY, PARSENOW }, 197 { "modinsert", NEXTARG, AF_ANY, PARSENOW }, 198 { "modremove", NEXTARG, AF_ANY, PARSENOW }, 199 { "failover", 0, AF_ANY, PARSEMOVABLE }, 200 { "-failover", 0, AF_ANY, PARSEFIXED }, 201 { "standby", 0, AF_ANY, PARSENOW }, 202 { "-standby", 0, AF_ANY, PARSENOW }, 203 { "failed", 0, AF_ANY, PARSENOW }, 204 { "-failed", 0, AF_ANY, PARSENOW }, 205 { "group", NEXTARG, AF_ANY, PARSELOG0 }, 206 { "configinfo", 0, AF_ANY, PARSENOW }, 207 { "encaplimit", NEXTARG, AF_ANY, PARSELOG0 }, 208 { "-encaplimit", 0, AF_ANY, PARSELOG0 }, 209 { "thoplimit", NEXTARG, AF_ANY, PARSELOG0 }, 210 { "set", NEXTARG, AF_ANY, PARSESET }, 211 { "destination", NEXTARG, AF_ANY, 0 }, 212 { "zone", NEXTARG, AF_ANY, 0 }, 213 { "-zone", 0, AF_ANY, 0 }, 214 { "all-zones", 0, AF_ANY, 0 }, 215 { "ether", OPTARG, AF_ANY, PARSENOW }, 216 { "usesrc", NEXTARG, AF_ANY, PARSENOW }, 217 { 0 /* ether addr */, 0, AF_UNSPEC, PARSELOG0 }, 218 { 0 /* set */, 0, AF_ANY, PARSESET }, 219 { 0 /* destination */, 0, AF_ANY, 0 }, 220 { 0, END_OF_TABLE, END_OF_TABLE, END_OF_TABLE}, 221 }; 222 223 224 /* Known address families */ 225 struct afswtch { 226 char *af_name; 227 short af_af; 228 } afs[] = { 229 { "inet", AF_INET }, 230 { "ether", AF_UNSPEC }, 231 { "inet6", AF_INET6 }, 232 { 0, 0 } 233 }; 234 235 /* 236 * Append "item" to the buffer. If there isn't enough room in the buffer, 237 * expand it. 238 */ 239 static void 240 parse_append_buf(char *item) 241 { 242 unsigned itemlen; 243 unsigned newdumplen; 244 245 if (item == NULL) 246 return; 247 248 itemlen = strlen(item); 249 newdumplen = parsedumplen + itemlen; 250 251 /* Expand dump buffer as needed */ 252 if (parsebuflen < newdumplen) { 253 if ((parsebuf = realloc(parsebuf, newdumplen)) == NULL) { 254 perror("ifparse"); 255 exit(1); 256 } 257 parsebuflen = newdumplen; 258 } 259 (void) memcpy(parsebuf + parsedumplen, item, itemlen); 260 261 parsedumplen = newdumplen; 262 } 263 264 /* 265 * Dump the buffer to output. 266 */ 267 static void 268 parse_dump_buf(void) 269 { 270 /* 271 * When parsing, a set or addif command, we may be some way into 272 * the command before we definitely know it is movable or fixed. 273 * If we get to the end of the command, and haven't seen a 274 * "failover" or "-failover" flag, the command is movable. 275 */ 276 if (!((parsemode == PARSEFIXED) && (parsetype & PARSEMOVABLE) != 0) && 277 (parsemode & parsetype) != 0 && parsedumplen != 0) { 278 unsigned i; 279 280 if (parsebuf[parsedumplen] == ' ') 281 parsedumplen--; 282 283 for (i = 0; i < parsedumplen; i++) 284 (void) putchar(parsebuf[i]); 285 286 (void) putchar('\n'); 287 } 288 /* The buffer is kept in case there is more parsing to do */ 289 parsedumplen = 0; 290 parsetype = PARSEFIXED | PARSEMOVABLE; 291 } 292 293 /* 294 * Process a command. The command will either be put in the buffer, 295 * or dumped directly to output. The current contents of the buffer 296 * may be dumped to output. 297 * 298 * The buffer holds commands relating to a particular logical interface. 299 * For example, "set", "destination", "failover", "broadcast", all relate 300 * to a particular interface. Such commands have to be buffered until 301 * all the "failover" and "-failover" commands for that interface have 302 * been seen, only then will we know whether the command is movable 303 * or not. When the "addif" command is seen, we know we are about to 304 * start processing a new logical interface, we've seen all the 305 * "failover" and "-failover" commands for the previous interface, and 306 * can decide whether the buffer contents are movable or not. 307 * 308 */ 309 static void 310 parsedump(char *cmd, int param, int flags, char *arg) 311 { 312 char *cmdname; /* Command name */ 313 char *cmdarg; /* Argument to command, if it takes one, or NULL */ 314 315 /* 316 * Is command only valid on logical interface 0? 317 * If processing commands on an additional logical interface, ignore 318 * the command. 319 * If processing commands on logical interface 0, don't buffer the 320 * command, dump it straight to output. 321 */ 322 if ((flags & PARSELOG0) != 0) { 323 if (addint) 324 return; 325 flags |= PARSENOW; 326 } 327 328 /* 329 * If processing the "addif" command, a destination address may 330 * follow without the "destination" prefix. Add PARSESET to the 331 * flags so that such an anonymous address is processed correctly. 332 */ 333 if ((flags & PARSEADD) != 0) { 334 flags |= PARSESET; 335 addint = _B_TRUE; 336 } 337 338 /* 339 * Commands that must be dumped straight to output are always fixed 340 * (non-movable) commands. 341 * 342 */ 343 if ((flags & PARSENOW) != 0) 344 flags |= PARSEFIXED; 345 346 /* 347 * Source and destination addresses do not have to be prefixed 348 * with the keywords "set" or "destination". Ifparse always 349 * inserts the optional keyword. 350 */ 351 if (cmd == NULL) { 352 cmdarg = arg; 353 if ((flags & PARSESET) != 0) 354 cmdname = "set"; 355 else if (setaddr) { 356 cmdname = "destination"; 357 setaddr = _B_FALSE; 358 } else 359 cmdname = ""; 360 } else { 361 cmdarg = (param == 0) ? NULL : arg; 362 cmdname = cmd; 363 } 364 365 /* 366 * The next address without a prefix will be a destination 367 * address. 368 */ 369 if ((flags & PARSESET) != 0) 370 setaddr = _B_TRUE; 371 372 /* 373 * Dump the command straight to output? 374 * Only dump the command if the parse mode specified on 375 * the command line matches the type of the command. 376 */ 377 if ((flags & PARSENOW) != 0) { 378 if ((parsemode & flags) != 0) { 379 (void) fputs(cmdname, stdout); 380 if (cmdarg != NULL) { 381 (void) fputc(' ', stdout); 382 (void) fputs(cmdarg, stdout); 383 } 384 (void) fputc('\n', stdout); 385 } 386 return; 387 } 388 389 /* 390 * Only the commands relating to a particular logical interface 391 * are buffered. When an "addif" command is seen, processing is 392 * about to start on a new logical interface, so dump the 393 * buffer to output. 394 */ 395 if ((flags & PARSEADD) != 0) 396 parse_dump_buf(); 397 398 /* 399 * If the command flags indicate the command is fixed or 400 * movable, update the type of the interface in the buffer 401 * accordingly. For example, "-failover" has the "PARSEFIXED" 402 * flag, and the contents of the buffer are not movable if 403 * "-failover" is seen. 404 */ 405 if ((flags & PARSEFIXED) != 0) 406 parsetype &= ~PARSEMOVABLE; 407 408 if ((flags & PARSEMOVABLE) != 0) 409 parsetype &= ~PARSEFIXED; 410 411 parsetype |= flags & (PARSEFIXED | PARSEMOVABLE); 412 413 parse_append_buf(cmdname); 414 415 if (cmdarg != NULL) { 416 parse_append_buf(" "); 417 parse_append_buf(cmdarg); 418 } 419 420 parse_append_buf(" "); 421 } 422 423 /* 424 * Parse the part of the command line following the address family 425 * specification, if any. 426 * 427 * This function is a modified version of the function "ifconfig" in 428 * ifconfig.c. 429 */ 430 static int 431 ifparse(int argc, char *argv[], struct afswtch *afp) 432 { 433 int af = afp->af_af; 434 435 if (argc == 0) 436 return (0); 437 438 if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) { 439 if ((parsemode & PARSEFIXED) != NULL) { 440 while (argc) { 441 (void) fputs(*argv++, stdout); 442 if (--argc != 0) 443 (void) fputc(' ', stdout); 444 else 445 (void) fputc('\n', stdout); 446 } 447 } 448 return (0); 449 } 450 451 while (argc > 0) { 452 struct cmd *p; 453 boolean_t found_cmd; 454 455 found_cmd = _B_FALSE; 456 for (p = cmds; ; p++) { 457 assert(p->c_parseflags != END_OF_TABLE); 458 if (p->c_name) { 459 if (strcmp(*argv, p->c_name) == 0) { 460 /* 461 * indicate that the command was 462 * found and check to see if 463 * the address family is valid 464 */ 465 found_cmd = _B_TRUE; 466 if (p->c_af == AF_ANY || 467 af == p->c_af) 468 break; 469 } 470 } else { 471 if (p->c_af == AF_ANY || 472 af == p->c_af) 473 break; 474 } 475 } 476 assert(p->c_parseflags != END_OF_TABLE); 477 /* 478 * If we found the keyword, but the address family 479 * did not match spit out an error 480 */ 481 if (found_cmd && p->c_name == 0) { 482 (void) fprintf(stderr, "ifparse: Operation %s not" 483 " supported for %s\n", *argv, afp->af_name); 484 return (1); 485 } 486 /* 487 * else (no keyword found), we assume it's an address 488 * of some sort 489 */ 490 if (p->c_name == 0 && setaddr) { 491 p++; /* got src, do dst */ 492 assert(p->c_parseflags != END_OF_TABLE); 493 } 494 495 if (p->c_parameter == NEXTARG || p->c_parameter == OPTARG) { 496 argc--, argv++; 497 if (argc == 0 && p->c_parameter == NEXTARG) { 498 (void) fprintf(stderr, 499 "ifparse: no argument for %s\n", 500 p->c_name); 501 return (1); 502 } 503 } 504 505 /* 506 * Dump the command if: 507 * 508 * there's no address family 509 * restriction 510 * OR 511 * there is a restriction AND 512 * the address families match 513 */ 514 if ((p->c_af == AF_ANY) || (af == p->c_af)) 515 parsedump(p->c_name, p->c_parameter, p->c_parseflags, 516 *argv); 517 argc--, argv++; 518 } 519 parse_dump_buf(); 520 521 return (0); 522 } 523 524 /* 525 * Print command usage on standard error. 526 */ 527 static void 528 usage(void) 529 { 530 (void) fprintf(stderr, 531 "usage: ifparse [ -fs ] <addr_family> <commands>\n"); 532 } 533 534 int 535 main(int argc, char *argv[]) 536 { 537 int c; 538 struct afswtch *afp; 539 540 while ((c = getopt(argc, argv, "fs")) != -1) { 541 switch ((char)c) { 542 case 'f': 543 parsemode |= PARSEMOVABLE; 544 break; 545 case 's': 546 parsemode |= PARSEFIXED; 547 break; 548 case '?': 549 usage(); 550 exit(1); 551 } 552 } 553 554 if (parsemode == 0) 555 parsemode = PARSEFIXED | PARSEMOVABLE; 556 557 argc -= optind; 558 argv += optind; 559 560 afp = afs; 561 if (argc > 0) { 562 struct afswtch *aftp; 563 for (aftp = afs; aftp->af_name; aftp++) { 564 if (strcmp(aftp->af_name, *argv) == 0) { 565 argc--; argv++; 566 afp = aftp; 567 break; 568 } 569 } 570 } 571 572 return (ifparse(argc, argv, afp)); 573 }