summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/sbin/ifparse/ifparse.c
blob: d73722cc55996a7c60bd473a0f7d9828a233bd49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
/*
 * 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	<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)	/* 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) != NULL) {
			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 ] <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));
}