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