1 /* 2 * Copyright 2008 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 #pragma ident "%Z%%M% %I% %E% SMI" 12 13 /* 14 * Ifparse splits up an ifconfig command line, and was written for use 15 * with the networking boot scripts; see $SRC/cmd/svc/shell/net_include.sh 16 * 17 * Ifparse can extract selected parts of the ifconfig command line, 18 * such as failover address configuration ("ifparse -f"), or everything 19 * except failover address configuration ("ifparse -s"). By default, 20 * all parts of the command line are extracted (equivalent to ("ifparse -fs"). 21 * 22 * Examples: 23 * 24 * The command: 25 * 26 * ifparse inet 1.2.3.4 up group two addif 1.2.3.5 up addif 1.2.3.6 up 27 * 28 * Produces the following on standard output: 29 * 30 * set 1.2.3.4 up 31 * group two 32 * addif 1.2.3.5 up 33 * addif 1.2.3.6 up 34 * 35 * The optional "set" and "destination" keywords are added to make the 36 * output easier to process by a script or another command. 37 * 38 * The command: 39 * 40 * ifparse -f inet 1.2.3.4 -failover up group two addif 1.2.3.5 up 41 * 42 * Produces: 43 * 44 * addif 1.2.3.5 up 45 * 46 * Only failover address configuration has been requested. Address 47 * 1.2.3.4 is a non-failover address, and so isn't output. 48 * 49 * The "failover" and "-failover" commands can occur several times for 50 * a given logical interface. Only the last one counts. For example: 51 * 52 * ifparse -f inet 1.2.3.4 -failover failover -failover failover up 53 * 54 * Produces: 55 * 56 * set 1.2.3.4 -failover failover -failover failover up 57 * 58 * No attempt is made to clean up such "pathological" command lines, by 59 * removing redundant "failover" and "-failover" commands. 60 */ 61 62 #include <sys/types.h> 63 #include <stdlib.h> 64 #include <stdio.h> 65 #include <string.h> 66 #include <assert.h> 67 68 /* 69 * Parser flags: 70 * 71 * PARSEFIXED 72 * Command should only appear if non-failover commands 73 * are requested. 74 * PARSEMOVABLE 75 * Command should only appear if failover commands are 76 * requested. 77 * PARSENOW 78 * Don't buffer the command, dump it to output immediately. 79 * PARSEADD 80 * Indicates processing has moved on to additional 81 * logical interfaces. 82 * Dump the buffer to output and clear buffer contents. 83 * PARSESET 84 * The "set" and "destination" keywords are optional. 85 * This flag indicates that the next address not prefixed 86 * with a keyword will be a destination address. 87 * PARSELOG0 88 * Command not valid on additional logical interfaces. 89 */ 90 91 #define PARSEFIXED 0x01 92 #define PARSEMOVABLE 0x02 93 #define PARSENOW 0x04 94 #define PARSEADD 0x08 95 #define PARSESET 0x10 96 #define PARSELOG0 0x20 97 98 typedef enum { AF_UNSPEC, AF_INET, AF_INET6, AF_ANY } ac_t; 99 100 #define NEXTARG (-1) /* command takes an argument */ 101 #define OPTARG (-2) /* command takes an optional argument */ 102 103 #define END_OF_TABLE (-1) 104 105 /* Parsemode, the type of commands requested by the user. */ 106 int parsemode = 0; 107 108 /* Parsetype, the type of the command currently in the buffer. */ 109 int parsetype = PARSEFIXED | PARSEMOVABLE; 110 111 /* Parsebuf, pointer to the buffer. */ 112 char *parsebuf = NULL; 113 114 /* Parsebuflen, the size of the buffer area. */ 115 unsigned parsebuflen = 0; 116 117 /* Parsedumplen, the amount of the buffer currently in use. */ 118 unsigned parsedumplen = 0; 119 120 /* 121 * Setaddr, used to decide whether an address without a keyword 122 * prefix is a source or destination address. 123 */ 124 boolean_t setaddr = _B_FALSE; 125 126 /* 127 * Some ifconfig commands are only valid on the first logical interface. 128 * As soon as an "addif" command is seen, "addint" is set. 129 */ 130 boolean_t addint = _B_FALSE; 131 132 /* 133 * The parser table is based on that in ifconfig. A command may or 134 * may not have an argument, as indicated by whether NEXTARG/OPTARG is 135 * in the second column. Some commands can only be used with certain 136 * address families, as indicated in the third column. The fourth column 137 * contains flags that control parser action. 138 * 139 * Ifparse buffers logical interface configuration commands such as "set", 140 * "netmask" and "broadcast". This buffering continues until an "addif" 141 * command is seen, at which point the buffer is emptied, and the process 142 * starts again. 143 * 144 * Some commands do not relate to logical interface configuration and are 145 * dumped to output as soon as they are seen, such as "group" and "standby". 146 * 147 */ 148 149 struct cmd { 150 char *c_name; 151 int c_parameter; /* NEXTARG means next argv */ 152 int c_af; /* address family restrictions */ 153 int c_parseflags; /* parsing flags */ 154 } cmds[] = { 155 { "up", 0, AF_ANY, 0 }, 156 { "down", 0, AF_ANY, 0 }, 157 { "trailers", 0, AF_ANY, PARSENOW }, 158 { "-trailers", 0, AF_ANY, PARSENOW }, 159 { "arp", 0, AF_INET, PARSENOW }, 160 { "-arp", 0, AF_INET, PARSENOW }, 161 { "private", 0, AF_ANY, 0 }, 162 { "-private", 0, AF_ANY, 0 }, 163 { "router", 0, AF_ANY, PARSELOG0 }, 164 { "-router", 0, AF_ANY, PARSELOG0 }, 165 { "xmit", 0, AF_ANY, 0 }, 166 { "-xmit", 0, AF_ANY, 0 }, 167 { "-nud", 0, AF_INET6, PARSENOW }, 168 { "nud", 0, AF_INET6, PARSENOW }, 169 { "anycast", 0, AF_ANY, 0 }, 170 { "-anycast", 0, AF_ANY, 0 }, 171 { "local", 0, AF_ANY, 0 }, 172 { "-local", 0, AF_ANY, 0 }, 173 { "deprecated", 0, AF_ANY, 0 }, 174 { "-deprecated", 0, AF_ANY, 0 }, 175 { "preferred", 0, AF_INET6, 0 }, 176 { "-preferred", 0, AF_INET6, 0 }, 177 { "debug", 0, AF_ANY, PARSENOW }, 178 { "verbose", 0, AF_ANY, PARSENOW }, 179 { "netmask", NEXTARG, AF_INET, 0 }, 180 { "metric", NEXTARG, AF_ANY, 0 }, 181 { "mtu", NEXTARG, AF_ANY, 0 }, 182 { "index", NEXTARG, AF_ANY, PARSELOG0 }, 183 { "broadcast", NEXTARG, AF_INET, 0 }, 184 { "auto-revarp", 0, AF_INET, PARSEFIXED}, 185 { "plumb", 0, AF_ANY, PARSENOW }, 186 { "unplumb", 0, AF_ANY, PARSENOW }, 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 } 574