ifparse.c revision 8485:633e5b5eb268
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
96typedef 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. */
104int	parsemode = 0;
105
106/* Parsetype, the type of the command currently in the buffer. */
107int	parsetype = PARSEFIXED | PARSEMOVABLE;
108
109/* Parsebuf, pointer to the buffer. */
110char	*parsebuf = NULL;
111
112/* Parsebuflen, the size of the buffer area. */
113unsigned parsebuflen = 0;
114
115/* Parsedumplen, the amount of the buffer currently in use. */
116unsigned parsedumplen = 0;
117
118/*
119 * Setaddr, used to decide whether an address without a keyword
120 * prefix is a source or destination address.
121 */
122boolean_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 */
128boolean_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
147struct	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 */
224struct 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 */
238static void
239parse_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 */
266static void
267parse_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 */
308static void
309parsedump(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 */
429static int
430ifparse(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 */
526static void
527usage(void)
528{
529	(void) fprintf(stderr,
530	    "usage: ifparse [ -fs ] <addr_family> <commands>\n");
531}
532
533int
534main(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