/* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* * Ifparse splits up an ifconfig command line, and was written for use * with the networking boot scripts; see $SRC/cmd/svc/shell/net_include.sh * * Ifparse can extract selected parts of the ifconfig command line, * such as failover address configuration ("ifparse -f"), or everything * except failover address configuration ("ifparse -s"). By default, * all parts of the command line are extracted (equivalent to ("ifparse -fs"). * * Examples: * * The command: * * ifparse inet 1.2.3.4 up group two addif 1.2.3.5 up addif 1.2.3.6 up * * Produces the following on standard output: * * set 1.2.3.4 up * group two * addif 1.2.3.5 up * addif 1.2.3.6 up * * The optional "set" and "destination" keywords are added to make the * output easier to process by a script or another command. * * The command: * * ifparse -f inet 1.2.3.4 -failover up group two addif 1.2.3.5 up * * Produces: * * addif 1.2.3.5 up * * Only failover address configuration has been requested. Address * 1.2.3.4 is a non-failover address, and so isn't output. * * The "failover" and "-failover" commands can occur several times for * a given logical interface. Only the last one counts. For example: * * ifparse -f inet 1.2.3.4 -failover failover -failover failover up * * Produces: * * set 1.2.3.4 -failover failover -failover failover up * * No attempt is made to clean up such "pathological" command lines, by * removing redundant "failover" and "-failover" commands. */ #include #include #include #include #include /* * Parser flags: * * PARSEFIXED * Command should only appear if non-failover commands * are requested. * PARSEMOVABLE * Command should only appear if failover commands are * requested. * PARSENOW * Don't buffer the command, dump it to output immediately. * PARSEADD * Indicates processing has moved on to additional * logical interfaces. * Dump the buffer to output and clear buffer contents. * PARSESET * The "set" and "destination" keywords are optional. * This flag indicates that the next address not prefixed * with a keyword will be a destination address. * PARSELOG0 * Command not valid on additional logical interfaces. */ #define PARSEFIXED 0x01 #define PARSEMOVABLE 0x02 #define PARSENOW 0x04 #define PARSEADD 0x08 #define PARSESET 0x10 #define PARSELOG0 0x20 typedef enum { AF_UNSPEC, AF_INET, AF_INET6, AF_ANY } ac_t; #define NEXTARG (-1) /* command takes an argument */ #define OPTARG (-2) /* command takes an optional argument */ #define END_OF_TABLE (-1) /* Parsemode, the type of commands requested by the user. */ int parsemode = 0; /* Parsetype, the type of the command currently in the buffer. */ int parsetype = PARSEFIXED | PARSEMOVABLE; /* Parsebuf, pointer to the buffer. */ char *parsebuf = NULL; /* Parsebuflen, the size of the buffer area. */ unsigned parsebuflen = 0; /* Parsedumplen, the amount of the buffer currently in use. */ unsigned parsedumplen = 0; /* * Setaddr, used to decide whether an address without a keyword * prefix is a source or destination address. */ boolean_t setaddr = _B_FALSE; /* * Some ifconfig commands are only valid on the first logical interface. * As soon as an "addif" command is seen, "addint" is set. */ boolean_t addint = _B_FALSE; /* * The parser table is based on that in ifconfig. A command may or * may not have an argument, as indicated by whether NEXTARG/OPTARG is * in the second column. Some commands can only be used with certain * address families, as indicated in the third column. The fourth column * contains flags that control parser action. * * Ifparse buffers logical interface configuration commands such as "set", * "netmask" and "broadcast". This buffering continues until an "addif" * command is seen, at which point the buffer is emptied, and the process * starts again. * * Some commands do not relate to logical interface configuration and are * dumped to output as soon as they are seen, such as "group" and "standby". * */ struct cmd { char *c_name; int c_parameter; /* NEXTARG means next argv */ int c_af; /* address family restrictions */ int c_parseflags; /* parsing flags */ } cmds[] = { { "up", 0, AF_ANY, 0 }, { "down", 0, AF_ANY, 0 }, { "trailers", 0, AF_ANY, PARSENOW }, { "-trailers", 0, AF_ANY, PARSENOW }, { "arp", 0, AF_INET, PARSENOW }, { "-arp", 0, AF_INET, PARSENOW }, { "private", 0, AF_ANY, 0 }, { "-private", 0, AF_ANY, 0 }, { "router", 0, AF_ANY, PARSELOG0 }, { "-router", 0, AF_ANY, PARSELOG0 }, { "xmit", 0, AF_ANY, 0 }, { "-xmit", 0, AF_ANY, 0 }, { "-nud", 0, AF_INET6, PARSENOW }, { "nud", 0, AF_INET6, PARSENOW }, { "anycast", 0, AF_ANY, 0 }, { "-anycast", 0, AF_ANY, 0 }, { "local", 0, AF_ANY, 0 }, { "-local", 0, AF_ANY, 0 }, { "deprecated", 0, AF_ANY, 0 }, { "-deprecated", 0, AF_ANY, 0 }, { "preferred", 0, AF_INET6, 0 }, { "-preferred", 0, AF_INET6, 0 }, { "debug", 0, AF_ANY, PARSENOW }, { "verbose", 0, AF_ANY, PARSENOW }, { "netmask", NEXTARG, AF_INET, 0 }, { "metric", NEXTARG, AF_ANY, 0 }, { "mtu", NEXTARG, AF_ANY, 0 }, { "index", NEXTARG, AF_ANY, PARSELOG0 }, { "broadcast", NEXTARG, AF_INET, 0 }, { "auto-revarp", 0, AF_INET, PARSEFIXED}, { "plumb", 0, AF_ANY, PARSENOW }, { "unplumb", 0, AF_ANY, PARSENOW }, { "ipmp", 0, AF_ANY, PARSELOG0 }, { "subnet", NEXTARG, AF_ANY, 0 }, { "token", NEXTARG, AF_INET6, PARSELOG0 }, { "tsrc", NEXTARG, AF_ANY, PARSELOG0 }, { "tdst", NEXTARG, AF_ANY, PARSELOG0 }, { "encr_auth_algs", NEXTARG, AF_ANY, PARSELOG0 }, { "encr_algs", NEXTARG, AF_ANY, PARSELOG0 }, { "auth_algs", NEXTARG, AF_ANY, PARSELOG0 }, { "addif", NEXTARG, AF_ANY, PARSEADD }, { "removeif", NEXTARG, AF_ANY, PARSELOG0 }, { "modlist", 0, AF_ANY, PARSENOW }, { "modinsert", NEXTARG, AF_ANY, PARSENOW }, { "modremove", NEXTARG, AF_ANY, PARSENOW }, { "failover", 0, AF_ANY, PARSEMOVABLE }, { "-failover", 0, AF_ANY, PARSEFIXED }, { "standby", 0, AF_ANY, PARSENOW }, { "-standby", 0, AF_ANY, PARSENOW }, { "failed", 0, AF_ANY, PARSENOW }, { "-failed", 0, AF_ANY, PARSENOW }, { "group", NEXTARG, AF_ANY, PARSELOG0 }, { "configinfo", 0, AF_ANY, PARSENOW }, { "encaplimit", NEXTARG, AF_ANY, PARSELOG0 }, { "-encaplimit", 0, AF_ANY, PARSELOG0 }, { "thoplimit", NEXTARG, AF_ANY, PARSELOG0 }, { "set", NEXTARG, AF_ANY, PARSESET }, { "destination", NEXTARG, AF_ANY, 0 }, { "zone", NEXTARG, AF_ANY, 0 }, { "-zone", 0, AF_ANY, 0 }, { "all-zones", 0, AF_ANY, 0 }, { "ether", OPTARG, AF_ANY, PARSENOW }, { "usesrc", NEXTARG, AF_ANY, PARSENOW }, { 0 /* ether addr */, 0, AF_UNSPEC, PARSELOG0 }, { 0 /* set */, 0, AF_ANY, PARSESET }, { 0 /* destination */, 0, AF_ANY, 0 }, { 0, END_OF_TABLE, END_OF_TABLE, END_OF_TABLE}, }; /* Known address families */ struct afswtch { char *af_name; short af_af; } afs[] = { { "inet", AF_INET }, { "ether", AF_UNSPEC }, { "inet6", AF_INET6 }, { 0, 0 } }; /* * Append "item" to the buffer. If there isn't enough room in the buffer, * expand it. */ static void parse_append_buf(char *item) { unsigned itemlen; unsigned newdumplen; if (item == NULL) return; itemlen = strlen(item); newdumplen = parsedumplen + itemlen; /* Expand dump buffer as needed */ if (parsebuflen < newdumplen) { if ((parsebuf = realloc(parsebuf, newdumplen)) == NULL) { perror("ifparse"); exit(1); } parsebuflen = newdumplen; } (void) memcpy(parsebuf + parsedumplen, item, itemlen); parsedumplen = newdumplen; } /* * Dump the buffer to output. */ static void parse_dump_buf(void) { /* * When parsing, a set or addif command, we may be some way into * the command before we definitely know it is movable or fixed. * If we get to the end of the command, and haven't seen a * "failover" or "-failover" flag, the command is movable. */ if (!((parsemode == PARSEFIXED) && (parsetype & PARSEMOVABLE) != 0) && (parsemode & parsetype) != 0 && parsedumplen != 0) { unsigned i; if (parsebuf[parsedumplen] == ' ') parsedumplen--; for (i = 0; i < parsedumplen; i++) (void) putchar(parsebuf[i]); (void) putchar('\n'); } /* The buffer is kept in case there is more parsing to do */ parsedumplen = 0; parsetype = PARSEFIXED | PARSEMOVABLE; } /* * Process a command. The command will either be put in the buffer, * or dumped directly to output. The current contents of the buffer * may be dumped to output. * * The buffer holds commands relating to a particular logical interface. * For example, "set", "destination", "failover", "broadcast", all relate * to a particular interface. Such commands have to be buffered until * all the "failover" and "-failover" commands for that interface have * been seen, only then will we know whether the command is movable * or not. When the "addif" command is seen, we know we are about to * start processing a new logical interface, we've seen all the * "failover" and "-failover" commands for the previous interface, and * can decide whether the buffer contents are movable or not. * */ static void parsedump(char *cmd, int param, int flags, char *arg) { char *cmdname; /* Command name */ char *cmdarg; /* Argument to command, if it takes one, or NULL */ /* * Is command only valid on logical interface 0? * If processing commands on an additional logical interface, ignore * the command. * If processing commands on logical interface 0, don't buffer the * command, dump it straight to output. */ if ((flags & PARSELOG0) != 0) { if (addint) return; flags |= PARSENOW; } /* * If processing the "addif" command, a destination address may * follow without the "destination" prefix. Add PARSESET to the * flags so that such an anonymous address is processed correctly. */ if ((flags & PARSEADD) != 0) { flags |= PARSESET; addint = _B_TRUE; } /* * Commands that must be dumped straight to output are always fixed * (non-movable) commands. * */ if ((flags & PARSENOW) != 0) flags |= PARSEFIXED; /* * Source and destination addresses do not have to be prefixed * with the keywords "set" or "destination". Ifparse always * inserts the optional keyword. */ if (cmd == NULL) { cmdarg = arg; if ((flags & PARSESET) != 0) cmdname = "set"; else if (setaddr) { cmdname = "destination"; setaddr = _B_FALSE; } else cmdname = ""; } else { cmdarg = (param == 0) ? NULL : arg; cmdname = cmd; } /* * The next address without a prefix will be a destination * address. */ if ((flags & PARSESET) != 0) setaddr = _B_TRUE; /* * Dump the command straight to output? * Only dump the command if the parse mode specified on * the command line matches the type of the command. */ if ((flags & PARSENOW) != 0) { if ((parsemode & flags) != 0) { (void) fputs(cmdname, stdout); if (cmdarg != NULL) { (void) fputc(' ', stdout); (void) fputs(cmdarg, stdout); } (void) fputc('\n', stdout); } return; } /* * Only the commands relating to a particular logical interface * are buffered. When an "addif" command is seen, processing is * about to start on a new logical interface, so dump the * buffer to output. */ if ((flags & PARSEADD) != 0) parse_dump_buf(); /* * If the command flags indicate the command is fixed or * movable, update the type of the interface in the buffer * accordingly. For example, "-failover" has the "PARSEFIXED" * flag, and the contents of the buffer are not movable if * "-failover" is seen. */ if ((flags & PARSEFIXED) != 0) parsetype &= ~PARSEMOVABLE; if ((flags & PARSEMOVABLE) != 0) parsetype &= ~PARSEFIXED; parsetype |= flags & (PARSEFIXED | PARSEMOVABLE); parse_append_buf(cmdname); if (cmdarg != NULL) { parse_append_buf(" "); parse_append_buf(cmdarg); } parse_append_buf(" "); } /* * Parse the part of the command line following the address family * specification, if any. * * This function is a modified version of the function "ifconfig" in * ifconfig.c. */ static int ifparse(int argc, char *argv[], struct afswtch *afp) { int af = afp->af_af; if (argc == 0) return (0); if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) { if ((parsemode & PARSEFIXED) != 0) { while (argc) { (void) fputs(*argv++, stdout); if (--argc != 0) (void) fputc(' ', stdout); else (void) fputc('\n', stdout); } } return (0); } while (argc > 0) { struct cmd *p; boolean_t found_cmd; found_cmd = _B_FALSE; for (p = cmds; ; p++) { assert(p->c_parseflags != END_OF_TABLE); if (p->c_name) { if (strcmp(*argv, p->c_name) == 0) { /* * indicate that the command was * found and check to see if * the address family is valid */ found_cmd = _B_TRUE; if (p->c_af == AF_ANY || af == p->c_af) break; } } else { if (p->c_af == AF_ANY || af == p->c_af) break; } } assert(p->c_parseflags != END_OF_TABLE); /* * If we found the keyword, but the address family * did not match spit out an error */ if (found_cmd && p->c_name == 0) { (void) fprintf(stderr, "ifparse: Operation %s not" " supported for %s\n", *argv, afp->af_name); return (1); } /* * else (no keyword found), we assume it's an address * of some sort */ if (p->c_name == 0 && setaddr) { p++; /* got src, do dst */ assert(p->c_parseflags != END_OF_TABLE); } if (p->c_parameter == NEXTARG || p->c_parameter == OPTARG) { argc--, argv++; if (argc == 0 && p->c_parameter == NEXTARG) { (void) fprintf(stderr, "ifparse: no argument for %s\n", p->c_name); return (1); } } /* * Dump the command if: * * there's no address family * restriction * OR * there is a restriction AND * the address families match */ if ((p->c_af == AF_ANY) || (af == p->c_af)) parsedump(p->c_name, p->c_parameter, p->c_parseflags, *argv); argc--, argv++; } parse_dump_buf(); return (0); } /* * Print command usage on standard error. */ static void usage(void) { (void) fprintf(stderr, "usage: ifparse [ -fs ] \n"); } int main(int argc, char *argv[]) { int c; struct afswtch *afp; while ((c = getopt(argc, argv, "fs")) != -1) { switch ((char)c) { case 'f': parsemode |= PARSEMOVABLE; break; case 's': parsemode |= PARSEFIXED; break; case '?': usage(); exit(1); } } if (parsemode == 0) parsemode = PARSEFIXED | PARSEMOVABLE; argc -= optind; argv += optind; afp = afs; if (argc > 0) { struct afswtch *aftp; for (aftp = afs; aftp->af_name; aftp++) { if (strcmp(aftp->af_name, *argv) == 0) { argc--; argv++; afp = aftp; break; } } } return (ifparse(argc, argv, afp)); }