Home | History | Annotate | Download | only in ifparse
      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