ifparse.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2000-2003 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Ifparse splits up an ifconfig command line, and was written for use
* with the networking boot script /etc/init.d/network (which is in the
* source tree as usr/src/cmd/initpkg/init.d/network).
*
* 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 <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
/*
* 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)
#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 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 },
{ "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 },
#ifdef DEBUG
{ "getnd", NEXTARG, AF_INET6, PARSELOG0 },
{ "setnd", NEXTARG, AF_INET6, PARSELOG0 },
{ "delnd", NEXTARG, AF_INET6, PARSELOG0 },
#endif
/* XXX for testing SIOCT* ioctls. Remove */
{ "set", NEXTARG, AF_ANY, PARSESET },
{ "destination", NEXTARG, AF_ANY, 0 },
{ 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 == NEXTARG) ? arg : NULL;
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;
if (argc == 0) {
return (0);
}
af = afp->af_af;
if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) {
if (af == AF_INET) {
if ((parsemode & PARSEFIXED) != NULL) {
while (argc) {
(void) fputs(*argv++, stdout);
if (--argc != 0)
(void) fputc(' ', stdout);
else
(void) fputc('\n', stdout);
}
}
return (0);
} else {
(void) fprintf(stderr, "ifparse: dhcp not supported "
"for inet6\n");
return (1);
}
}
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) {
argc--, argv++;
if (argc == 0) {
(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 ] <addr_family> <commands>\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));
}