1 /* 2 * Copyright 2000-2003 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 script /etc/init.d/network (which is in the 16 * source tree as usr/src/cmd/initpkg/init.d/network). 17 * 18 * Ifparse can extract selected parts of the ifconfig command line, 19 * such as failover address configuration ("ifparse -f"), or everything 20 * except failover address configuration ("ifparse -s"). By default, 21 * all parts of the command line are extracted (equivalent to ("ifparse -fs"). 22 * 23 * Examples: 24 * 25 * The command: 26 * 27 * ifparse inet 1.2.3.4 up group two addif 1.2.3.5 up addif 1.2.3.6 up 28 * 29 * Produces the following on standard output: 30 * 31 * set 1.2.3.4 up 32 * group two 33 * addif 1.2.3.5 up 34 * addif 1.2.3.6 up 35 * 36 * The optional "set" and "destination" keywords are added to make the 37 * output easier to process by a script or another command. 38 * 39 * The command: 40 * 41 * ifparse -f inet 1.2.3.4 -failover up group two addif 1.2.3.5 up 42 * 43 * Produces: 44 * 45 * addif 1.2.3.5 up 46 * 47 * Only failover address configuration has been requested. Address 48 * 1.2.3.4 is a non-failover address, and so isn't output. 49 * 50 * The "failover" and "-failover" commands can occur several times for 51 * a given logical interface. Only the last one counts. For example: 52 * 53 * ifparse -f inet 1.2.3.4 -failover failover -failover failover up 54 * 55 * Produces: 56 * 57 * set 1.2.3.4 -failover failover -failover failover up 58 * 59 * No attempt is made to clean up such "pathological" command lines, by 60 * removing redundant "failover" and "-failover" commands. 61 */ 62 63 #include <sys/types.h> 64 #include <stdlib.h> 65 #include <stdio.h> 66 #include <string.h> 67 #include <assert.h> 68 69 /* 70 * Parser flags: 71 * 72 * PARSEFIXED 73 * Command should only appear if non-failover commands 74 * are requested. 75 * PARSEMOVABLE 76 * Command should only appear if failover commands are 77 * requested. 78 * PARSENOW 79 * Don't buffer the command, dump it to output immediately. 80 * PARSEADD 81 * Indicates processing has moved on to additional 82 * logical interfaces. 83 * Dump the buffer to output and clear buffer contents. 84 * PARSESET 85 * The "set" and "destination" keywords are optional. 86 * This flag indicates that the next address not prefixed 87 * with a keyword will be a destination address. 88 * PARSELOG0 89 * Command not valid on additional logical interfaces. 90 */ 91 92 #define PARSEFIXED 0x01 93 #define PARSEMOVABLE 0x02 94 #define PARSENOW 0x04 95 #define PARSEADD 0x08 96 #define PARSESET 0x10 97 #define PARSELOG0 0x20 98 99 typedef enum { AF_UNSPEC, AF_INET, AF_INET6, AF_ANY } ac_t; 100 101 #define NEXTARG (-1) 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 is in the 135 * second column. Some commands can only be used with certain address 136 * 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 #ifdef DEBUG 211 { "getnd", NEXTARG, AF_INET6, PARSELOG0 }, 212 { "setnd", NEXTARG, AF_INET6, PARSELOG0 }, 213 { "delnd", NEXTARG, AF_INET6, PARSELOG0 }, 214 #endif 215 /* XXX for testing SIOCT* ioctls. Remove */ 216 { "set", NEXTARG, AF_ANY, PARSESET }, 217 { "destination", NEXTARG, AF_ANY, 0 }, 218 { 0 /* ether addr */, 0, AF_UNSPEC, PARSELOG0 }, 219 { 0 /* set */, 0, AF_ANY, PARSESET }, 220 { 0 /* destination */, 0, AF_ANY, 0 }, 221 { 0, END_OF_TABLE, END_OF_TABLE, END_OF_TABLE}, 222 }; 223 224 225 /* Known address families */ 226 struct afswtch { 227 char *af_name; 228 short af_af; 229 } afs[] = { 230 { "inet", AF_INET }, 231 { "ether", AF_UNSPEC }, 232 { "inet6", AF_INET6 }, 233 { 0, 0 } 234 }; 235 236 /* 237 * Append "item" to the buffer. If there isn't enough room in the buffer, 238 * expand it. 239 */ 240 static void 241 parse_append_buf(char *item) 242 { 243 unsigned itemlen; 244 unsigned newdumplen; 245 246 if (item == NULL) 247 return; 248 249 itemlen = strlen(item); 250 newdumplen = parsedumplen + itemlen; 251 252 /* Expand dump buffer as needed */ 253 if (parsebuflen < newdumplen) { 254 if ((parsebuf = realloc(parsebuf, newdumplen)) == NULL) { 255 perror("ifparse"); 256 exit(1); 257 } 258 parsebuflen = newdumplen; 259 } 260 (void) memcpy(parsebuf + parsedumplen, item, itemlen); 261 262 parsedumplen = newdumplen; 263 } 264 265 /* 266 * Dump the buffer to output. 267 */ 268 static void 269 parse_dump_buf(void) 270 { 271 /* 272 * When parsing, a set or addif command, we may be some way into 273 * the command before we definitely know it is movable or fixed. 274 * If we get to the end of the command, and haven't seen a 275 * "failover" or "-failover" flag, the command is movable. 276 */ 277 if (!((parsemode == PARSEFIXED) && 278 (parsetype & PARSEMOVABLE) != 0) && 279 (parsemode & parsetype) != 0 && 280 parsedumplen != 0) { 281 unsigned i; 282 283 if (parsebuf[parsedumplen] == ' ') 284 parsedumplen--; 285 286 for (i = 0; i < parsedumplen; i++) 287 (void) putchar(parsebuf[i]); 288 289 (void) putchar('\n'); 290 } 291 /* The buffer is kept in case there is more parsing to do */ 292 parsedumplen = 0; 293 parsetype = PARSEFIXED | PARSEMOVABLE; 294 } 295 296 /* 297 * Process a command. The command will either be put in the buffer, 298 * or dumped directly to output. The current contents of the buffer 299 * may be dumped to output. 300 * 301 * The buffer holds commands relating to a particular logical interface. 302 * For example, "set", "destination", "failover", "broadcast", all relate 303 * to a particular interface. Such commands have to be buffered until 304 * all the "failover" and "-failover" commands for that interface have 305 * been seen, only then will we know whether the command is movable 306 * or not. When the "addif" command is seen, we know we are about to 307 * start processing a new logical interface, we've seen all the 308 * "failover" and "-failover" commands for the previous interface, and 309 * can decide whether the buffer contents are movable or not. 310 * 311 */ 312 static void 313 parsedump(char *cmd, int param, int flags, char *arg) 314 { 315 char *cmdname; /* Command name */ 316 char *cmdarg; /* Argument to command, if it takes one, or NULL */ 317 318 /* 319 * Is command only valid on logical interface 0? 320 * If processing commands on an additional logical interface, ignore 321 * the command. 322 * If processing commands on logical interface 0, don't buffer the 323 * command, dump it straight to output. 324 */ 325 if ((flags & PARSELOG0) != 0) { 326 if (addint) 327 return; 328 flags |= PARSENOW; 329 } 330 331 /* 332 * If processing the "addif" command, a destination address may 333 * follow without the "destination" prefix. Add PARSESET to the 334 * flags so that such an anonymous address is processed correctly. 335 */ 336 if ((flags & PARSEADD) != 0) { 337 flags |= PARSESET; 338 addint = _B_TRUE; 339 } 340 341 /* 342 * Commands that must be dumped straight to output are always fixed 343 * (non-movable) commands. 344 * 345 */ 346 if ((flags & PARSENOW) != 0) 347 flags |= PARSEFIXED; 348 349 /* 350 * Source and destination addresses do not have to be prefixed 351 * with the keywords "set" or "destination". Ifparse always 352 * inserts the optional keyword. 353 */ 354 if (cmd == NULL) { 355 cmdarg = arg; 356 if ((flags & PARSESET) != 0) 357 cmdname = "set"; 358 else if (setaddr) { 359 cmdname = "destination"; 360 setaddr = _B_FALSE; 361 } else 362 cmdname = ""; 363 } else { 364 cmdarg = (param == NEXTARG) ? arg : NULL; 365 cmdname = cmd; 366 } 367 368 /* 369 * The next address without a prefix will be a destination 370 * address. 371 */ 372 if ((flags & PARSESET) != 0) 373 setaddr = _B_TRUE; 374 375 /* 376 * Dump the command straight to output? 377 * Only dump the command if the parse mode specified on 378 * the command line matches the type of the command. 379 */ 380 if ((flags & PARSENOW) != 0) { 381 if ((parsemode & flags) != 0) { 382 (void) fputs(cmdname, stdout); 383 if (cmdarg != NULL) { 384 (void) fputc(' ', stdout); 385 (void) fputs(cmdarg, stdout); 386 } 387 (void) fputc('\n', stdout); 388 } 389 return; 390 } 391 392 /* 393 * Only the commands relating to a particular logical interface 394 * are buffered. When an "addif" command is seen, processing is 395 * about to start on a new logical interface, so dump the 396 * buffer to output. 397 */ 398 if ((flags & PARSEADD) != 0) 399 parse_dump_buf(); 400 401 /* 402 * If the command flags indicate the command is fixed or 403 * movable, update the type of the interface in the buffer 404 * accordingly. For example, "-failover" has the "PARSEFIXED" 405 * flag, and the contents of the buffer are not movable if 406 * "-failover" is seen. 407 */ 408 if ((flags & PARSEFIXED) != 0) 409 parsetype &= ~PARSEMOVABLE; 410 411 if ((flags & PARSEMOVABLE) != 0) 412 parsetype &= ~PARSEFIXED; 413 414 parsetype |= flags & (PARSEFIXED | PARSEMOVABLE); 415 416 parse_append_buf(cmdname); 417 418 if (cmdarg != NULL) { 419 parse_append_buf(" "); 420 parse_append_buf(cmdarg); 421 } 422 423 parse_append_buf(" "); 424 } 425 426 /* 427 * Parse the part of the command line following the address family 428 * specification, if any. 429 * 430 * This function is a modified version of the function "ifconfig" in 431 * ifconfig.c. 432 */ 433 static int 434 ifparse(int argc, char *argv[], struct afswtch *afp) 435 { 436 int af; 437 438 if (argc == 0) { 439 return (0); 440 } 441 442 af = afp->af_af; 443 444 if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) { 445 if (af == AF_INET) { 446 if ((parsemode & PARSEFIXED) != NULL) { 447 while (argc) { 448 (void) fputs(*argv++, stdout); 449 if (--argc != 0) 450 (void) fputc(' ', stdout); 451 else 452 (void) fputc('\n', stdout); 453 } 454 } 455 return (0); 456 } else { 457 (void) fprintf(stderr, "ifparse: dhcp not supported " 458 "for inet6\n"); 459 460 return (1); 461 } 462 } 463 464 while (argc > 0) { 465 struct cmd *p; 466 boolean_t found_cmd; 467 468 found_cmd = _B_FALSE; 469 for (p = cmds; ; p++) { 470 assert(p->c_parseflags != END_OF_TABLE); 471 if (p->c_name) { 472 if (strcmp(*argv, p->c_name) == 0) { 473 /* 474 * indicate that the command was 475 * found and check to see if 476 * the address family is valid 477 */ 478 found_cmd = _B_TRUE; 479 if (p->c_af == AF_ANY || 480 af == p->c_af) 481 break; 482 } 483 } else { 484 if (p->c_af == AF_ANY || 485 af == p->c_af) 486 break; 487 } 488 } 489 assert(p->c_parseflags != END_OF_TABLE); 490 /* 491 * If we found the keyword, but the address family 492 * did not match spit out an error 493 */ 494 if (found_cmd && p->c_name == 0) { 495 (void) fprintf(stderr, "ifparse: Operation %s not" 496 " supported for %s\n", *argv, afp->af_name); 497 return (1); 498 } 499 /* 500 * else (no keyword found), we assume it's an address 501 * of some sort 502 */ 503 if (p->c_name == 0 && setaddr) { 504 p++; /* got src, do dst */ 505 assert(p->c_parseflags != END_OF_TABLE); 506 } 507 if (p->c_parameter == NEXTARG) { 508 argc--, argv++; 509 if (argc == 0) { 510 (void) fprintf(stderr, 511 "ifparse: no argument for %s\n", 512 p->c_name); 513 return (1); 514 } 515 } 516 /* 517 * Dump the command if: 518 * 519 * there's no address family 520 * restriction 521 * OR 522 * there is a restriction AND 523 * the address families match 524 */ 525 if ((p->c_af == AF_ANY) || (af == p->c_af)) 526 parsedump(p->c_name, p->c_parameter, 527 p->c_parseflags, *argv); 528 argc--, argv++; 529 } 530 parse_dump_buf(); 531 532 return (0); 533 } 534 535 /* 536 * Print command usage on standard error. 537 */ 538 static void 539 usage(void) 540 { 541 (void) fprintf(stderr, 542 "usage: ifparse [ -fs ] <addr_family> <commands>\n"); 543 } 544 545 int 546 main(int argc, char *argv[]) 547 { 548 int c; 549 struct afswtch *afp; 550 551 while ((c = getopt(argc, argv, "fs")) != -1) { 552 switch ((char)c) { 553 case 'f': 554 parsemode |= PARSEMOVABLE; 555 break; 556 case 's': 557 parsemode |= PARSEFIXED; 558 break; 559 case '?': 560 usage(); 561 exit(1); 562 } 563 } 564 565 if (parsemode == 0) 566 parsemode = PARSEFIXED | PARSEMOVABLE; 567 568 argc -= optind; 569 argv += optind; 570 571 afp = afs; 572 if (argc > 0) { 573 struct afswtch *aftp; 574 for (aftp = afs; aftp->af_name; aftp++) { 575 if (strcmp(aftp->af_name, *argv) == 0) { 576 argc--; argv++; 577 afp = aftp; 578 break; 579 } 580 } 581 } 582 583 return (ifparse(argc, argv, afp)); 584 } 585