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
parse_append_buf(char * item)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
parse_dump_buf(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
parsedump(char * cmd,int param,int flags,char * arg)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
ifparse(int argc,char * argv[],struct afswtch * afp)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) != 0) {
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
usage(void)527 usage(void)
528 {
529 	(void) fprintf(stderr,
530 	    "usage: ifparse [ -fs ] <addr_family> <commands>\n");
531 }
532 
533 int
main(int argc,char * argv[])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