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 #include <unistd.h>
66
67 /*
68 * Parser flags:
69 *
70 * PARSEFIXED
71 * Command should only appear if non-failover commands
72 * are requested.
73 * PARSEMOVABLE
74 * Command should only appear if failover commands are
75 * requested.
76 * PARSENOW
77 * Don't buffer the command, dump it to output immediately.
78 * PARSEADD
79 * Indicates processing has moved on to additional
80 * logical interfaces.
81 * Dump the buffer to output and clear buffer contents.
82 * PARSESET
83 * The "set" and "destination" keywords are optional.
84 * This flag indicates that the next address not prefixed
85 * with a keyword will be a destination address.
86 * PARSELOG0
87 * Command not valid on additional logical interfaces.
88 */
89
90 #define PARSEFIXED 0x01
91 #define PARSEMOVABLE 0x02
92 #define PARSENOW 0x04
93 #define PARSEADD 0x08
94 #define PARSESET 0x10
95 #define PARSELOG0 0x20
96
97 typedef enum { AF_UNSPEC, AF_INET, AF_INET6, AF_ANY } ac_t;
98
99 #define NEXTARG (-1) /* command takes an argument */
100 #define OPTARG (-2) /* command takes an optional argument */
101
102 #define END_OF_TABLE (-1)
103
104 /* Parsemode, the type of commands requested by the user. */
105 int parsemode = 0;
106
107 /* Parsetype, the type of the command currently in the buffer. */
108 int parsetype = PARSEFIXED | PARSEMOVABLE;
109
110 /* Parsebuf, pointer to the buffer. */
111 char *parsebuf = NULL;
112
113 /* Parsebuflen, the size of the buffer area. */
114 unsigned parsebuflen = 0;
115
116 /* Parsedumplen, the amount of the buffer currently in use. */
117 unsigned parsedumplen = 0;
118
119 /*
120 * Setaddr, used to decide whether an address without a keyword
121 * prefix is a source or destination address.
122 */
123 boolean_t setaddr = _B_FALSE;
124
125 /*
126 * Some ifconfig commands are only valid on the first logical interface.
127 * As soon as an "addif" command is seen, "addint" is set.
128 */
129 boolean_t addint = _B_FALSE;
130
131 /*
132 * The parser table is based on that in ifconfig. A command may or
133 * may not have an argument, as indicated by whether NEXTARG/OPTARG is
134 * in the second column. Some commands can only be used with certain
135 * address families, as indicated in the third column. The fourth column
136 * contains flags that control parser action.
137 *
138 * Ifparse buffers logical interface configuration commands such as "set",
139 * "netmask" and "broadcast". This buffering continues until an "addif"
140 * command is seen, at which point the buffer is emptied, and the process
141 * starts again.
142 *
143 * Some commands do not relate to logical interface configuration and are
144 * dumped to output as soon as they are seen, such as "group" and "standby".
145 *
146 */
147
148 struct cmd {
149 char *c_name;
150 int c_parameter; /* NEXTARG means next argv */
151 int c_af; /* address family restrictions */
152 int c_parseflags; /* parsing flags */
153 } cmds[] = {
154 { "up", 0, AF_ANY, 0 },
155 { "down", 0, AF_ANY, 0 },
156 { "trailers", 0, AF_ANY, PARSENOW },
157 { "-trailers", 0, AF_ANY, PARSENOW },
158 { "arp", 0, AF_INET, PARSENOW },
159 { "-arp", 0, AF_INET, PARSENOW },
160 { "private", 0, AF_ANY, 0 },
161 { "-private", 0, AF_ANY, 0 },
162 { "router", 0, AF_ANY, PARSELOG0 },
163 { "-router", 0, AF_ANY, PARSELOG0 },
164 { "xmit", 0, AF_ANY, 0 },
165 { "-xmit", 0, AF_ANY, 0 },
166 { "-nud", 0, AF_INET6, PARSENOW },
167 { "nud", 0, AF_INET6, PARSENOW },
168 { "anycast", 0, AF_ANY, 0 },
169 { "-anycast", 0, AF_ANY, 0 },
170 { "local", 0, AF_ANY, 0 },
171 { "-local", 0, AF_ANY, 0 },
172 { "deprecated", 0, AF_ANY, 0 },
173 { "-deprecated", 0, AF_ANY, 0 },
174 { "preferred", 0, AF_INET6, 0 },
175 { "-preferred", 0, AF_INET6, 0 },
176 { "debug", 0, AF_ANY, PARSENOW },
177 { "verbose", 0, AF_ANY, PARSENOW },
178 { "netmask", NEXTARG, AF_INET, 0 },
179 { "metric", NEXTARG, AF_ANY, 0 },
180 { "mtu", NEXTARG, AF_ANY, 0 },
181 { "index", NEXTARG, AF_ANY, PARSELOG0 },
182 { "broadcast", NEXTARG, AF_INET, 0 },
183 { "auto-revarp", 0, AF_INET, PARSEFIXED},
184 { "plumb", 0, AF_ANY, PARSENOW },
185 { "unplumb", 0, AF_ANY, PARSENOW },
186 { "ipmp", 0, AF_ANY, PARSELOG0 },
187 { "subnet", NEXTARG, AF_ANY, 0 },
188 { "token", NEXTARG, AF_INET6, PARSELOG0 },
189 { "tsrc", NEXTARG, AF_ANY, PARSELOG0 },
190 { "tdst", NEXTARG, AF_ANY, PARSELOG0 },
191 { "encr_auth_algs", NEXTARG, AF_ANY, PARSELOG0 },
192 { "encr_algs", NEXTARG, AF_ANY, PARSELOG0 },
193 { "auth_algs", NEXTARG, AF_ANY, PARSELOG0 },
194 { "addif", NEXTARG, AF_ANY, PARSEADD },
195 { "removeif", NEXTARG, AF_ANY, PARSELOG0 },
196 { "modlist", 0, AF_ANY, PARSENOW },
197 { "modinsert", NEXTARG, AF_ANY, PARSENOW },
198 { "modremove", NEXTARG, AF_ANY, PARSENOW },
199 { "failover", 0, AF_ANY, PARSEMOVABLE },
200 { "-failover", 0, AF_ANY, PARSEFIXED },
201 { "standby", 0, AF_ANY, PARSENOW },
202 { "-standby", 0, AF_ANY, PARSENOW },
203 { "failed", 0, AF_ANY, PARSENOW },
204 { "-failed", 0, AF_ANY, PARSENOW },
205 { "group", NEXTARG, AF_ANY, PARSELOG0 },
206 { "configinfo", 0, AF_ANY, PARSENOW },
207 { "encaplimit", NEXTARG, AF_ANY, PARSELOG0 },
208 { "-encaplimit", 0, AF_ANY, PARSELOG0 },
209 { "thoplimit", NEXTARG, AF_ANY, PARSELOG0 },
210 { "set", NEXTARG, AF_ANY, PARSESET },
211 { "destination", NEXTARG, AF_ANY, 0 },
212 { "zone", NEXTARG, AF_ANY, 0 },
213 { "-zone", 0, AF_ANY, 0 },
214 { "all-zones", 0, AF_ANY, 0 },
215 { "ether", OPTARG, AF_ANY, PARSENOW },
216 { "usesrc", NEXTARG, AF_ANY, PARSENOW },
217 { 0 /* ether addr */, 0, AF_UNSPEC, PARSELOG0 },
218 { 0 /* set */, 0, AF_ANY, PARSESET },
219 { 0 /* destination */, 0, AF_ANY, 0 },
220 { 0, END_OF_TABLE, END_OF_TABLE, END_OF_TABLE},
221 };
222
223
224 /* Known address families */
225 struct afswtch {
226 char *af_name;
227 short af_af;
228 } afs[] = {
229 { "inet", AF_INET },
230 { "ether", AF_UNSPEC },
231 { "inet6", AF_INET6 },
232 { 0, 0 }
233 };
234
235 /*
236 * Append "item" to the buffer. If there isn't enough room in the buffer,
237 * expand it.
238 */
239 static void
240 parse_append_buf(char *item)
241 {
242 unsigned itemlen;
243 unsigned newdumplen;
244
245 if (item == NULL)
246 return;
247
248 itemlen = strlen(item);
249 newdumplen = parsedumplen + itemlen;
250
251 /* Expand dump buffer as needed */
252 if (parsebuflen < newdumplen) {
253 if ((parsebuf = realloc(parsebuf, newdumplen)) == NULL) {
254 perror("ifparse");
255 exit(1);
256 }
257 parsebuflen = newdumplen;
258 }
259 (void) memcpy(parsebuf + parsedumplen, item, itemlen);
260
261 parsedumplen = newdumplen;
262 }
263
264 /*
265 * Dump the buffer to output.
266 */
267 static void
268 parse_dump_buf(void)
269 {
270 /*
271 * When parsing, a set or addif command, we may be some way into
272 * the command before we definitely know it is movable or fixed.
273 * If we get to the end of the command, and haven't seen a
274 * "failover" or "-failover" flag, the command is movable.
275 */
276 if (!((parsemode == PARSEFIXED) && (parsetype & PARSEMOVABLE) != 0) &&
277 (parsemode & parsetype) != 0 && parsedumplen != 0) {
278 unsigned i;
279
280 if (parsebuf[parsedumplen] == ' ')
281 parsedumplen--;
282
283 for (i = 0; i < parsedumplen; i++)
284 (void) putchar(parsebuf[i]);
285
286 (void) putchar('\n');
287 }
288 /* The buffer is kept in case there is more parsing to do */
289 parsedumplen = 0;
290 parsetype = PARSEFIXED | PARSEMOVABLE;
291 }
292
293 /*
294 * Process a command. The command will either be put in the buffer,
295 * or dumped directly to output. The current contents of the buffer
296 * may be dumped to output.
297 *
298 * The buffer holds commands relating to a particular logical interface.
299 * For example, "set", "destination", "failover", "broadcast", all relate
300 * to a particular interface. Such commands have to be buffered until
301 * all the "failover" and "-failover" commands for that interface have
302 * been seen, only then will we know whether the command is movable
303 * or not. When the "addif" command is seen, we know we are about to
304 * start processing a new logical interface, we've seen all the
305 * "failover" and "-failover" commands for the previous interface, and
306 * can decide whether the buffer contents are movable or not.
307 *
308 */
309 static void
310 parsedump(char *cmd, int param, int flags, char *arg)
311 {
312 char *cmdname; /* Command name */
313 char *cmdarg; /* Argument to command, if it takes one, or NULL */
314
315 /*
316 * Is command only valid on logical interface 0?
317 * If processing commands on an additional logical interface, ignore
318 * the command.
319 * If processing commands on logical interface 0, don't buffer the
320 * command, dump it straight to output.
321 */
322 if ((flags & PARSELOG0) != 0) {
323 if (addint)
324 return;
325 flags |= PARSENOW;
326 }
327
328 /*
329 * If processing the "addif" command, a destination address may
330 * follow without the "destination" prefix. Add PARSESET to the
331 * flags so that such an anonymous address is processed correctly.
332 */
333 if ((flags & PARSEADD) != 0) {
334 flags |= PARSESET;
335 addint = _B_TRUE;
336 }
337
338 /*
339 * Commands that must be dumped straight to output are always fixed
340 * (non-movable) commands.
341 *
342 */
343 if ((flags & PARSENOW) != 0)
344 flags |= PARSEFIXED;
345
346 /*
347 * Source and destination addresses do not have to be prefixed
348 * with the keywords "set" or "destination". Ifparse always
349 * inserts the optional keyword.
350 */
351 if (cmd == NULL) {
352 cmdarg = arg;
353 if ((flags & PARSESET) != 0)
354 cmdname = "set";
355 else if (setaddr) {
356 cmdname = "destination";
357 setaddr = _B_FALSE;
358 } else
359 cmdname = "";
360 } else {
361 cmdarg = (param == 0) ? NULL : arg;
362 cmdname = cmd;
363 }
364
365 /*
366 * The next address without a prefix will be a destination
367 * address.
368 */
369 if ((flags & PARSESET) != 0)
370 setaddr = _B_TRUE;
371
372 /*
373 * Dump the command straight to output?
374 * Only dump the command if the parse mode specified on
375 * the command line matches the type of the command.
376 */
377 if ((flags & PARSENOW) != 0) {
378 if ((parsemode & flags) != 0) {
379 (void) fputs(cmdname, stdout);
380 if (cmdarg != NULL) {
381 (void) fputc(' ', stdout);
382 (void) fputs(cmdarg, stdout);
383 }
384 (void) fputc('\n', stdout);
385 }
386 return;
387 }
388
389 /*
390 * Only the commands relating to a particular logical interface
391 * are buffered. When an "addif" command is seen, processing is
392 * about to start on a new logical interface, so dump the
393 * buffer to output.
394 */
395 if ((flags & PARSEADD) != 0)
396 parse_dump_buf();
397
398 /*
399 * If the command flags indicate the command is fixed or
400 * movable, update the type of the interface in the buffer
401 * accordingly. For example, "-failover" has the "PARSEFIXED"
402 * flag, and the contents of the buffer are not movable if
403 * "-failover" is seen.
404 */
405 if ((flags & PARSEFIXED) != 0)
406 parsetype &= ~PARSEMOVABLE;
407
408 if ((flags & PARSEMOVABLE) != 0)
409 parsetype &= ~PARSEFIXED;
410
411 parsetype |= flags & (PARSEFIXED | PARSEMOVABLE);
412
413 parse_append_buf(cmdname);
414
415 if (cmdarg != NULL) {
416 parse_append_buf(" ");
417 parse_append_buf(cmdarg);
418 }
419
420 parse_append_buf(" ");
421 }
422
423 /*
424 * Parse the part of the command line following the address family
425 * specification, if any.
426 *
427 * This function is a modified version of the function "ifconfig" in
428 * ifconfig.c.
429 */
430 static int
431 ifparse(int argc, char *argv[], struct afswtch *afp)
432 {
433 int af = afp->af_af;
434
435 if (argc == 0)
436 return (0);
437
438 if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) {
439 if ((parsemode & PARSEFIXED) != NULL) {
440 while (argc) {
441 (void) fputs(*argv++, stdout);
442 if (--argc != 0)
443 (void) fputc(' ', stdout);
444 else
445 (void) fputc('\n', stdout);
446 }
447 }
448 return (0);
449 }
450
451 while (argc > 0) {
452 struct cmd *p;
453 boolean_t found_cmd;
454
455 found_cmd = _B_FALSE;
456 for (p = cmds; ; p++) {
457 assert(p->c_parseflags != END_OF_TABLE);
458 if (p->c_name) {
459 if (strcmp(*argv, p->c_name) == 0) {
460 /*
461 * indicate that the command was
462 * found and check to see if
463 * the address family is valid
464 */
465 found_cmd = _B_TRUE;
466 if (p->c_af == AF_ANY ||
467 af == p->c_af)
468 break;
469 }
470 } else {
471 if (p->c_af == AF_ANY ||
472 af == p->c_af)
473 break;
474 }
475 }
476 assert(p->c_parseflags != END_OF_TABLE);
477 /*
478 * If we found the keyword, but the address family
479 * did not match spit out an error
480 */
481 if (found_cmd && p->c_name == 0) {
482 (void) fprintf(stderr, "ifparse: Operation %s not"
483 " supported for %s\n", *argv, afp->af_name);
484 return (1);
485 }
486 /*
487 * else (no keyword found), we assume it's an address
488 * of some sort
489 */
490 if (p->c_name == 0 && setaddr) {
491 p++; /* got src, do dst */
492 assert(p->c_parseflags != END_OF_TABLE);
493 }
494
495 if (p->c_parameter == NEXTARG || p->c_parameter == OPTARG) {
496 argc--, argv++;
497 if (argc == 0 && p->c_parameter == NEXTARG) {
498 (void) fprintf(stderr,
499 "ifparse: no argument for %s\n",
500 p->c_name);
501 return (1);
502 }
503 }
504
505 /*
506 * Dump the command if:
507 *
508 * there's no address family
509 * restriction
510 * OR
511 * there is a restriction AND
512 * the address families match
513 */
514 if ((p->c_af == AF_ANY) || (af == p->c_af))
515 parsedump(p->c_name, p->c_parameter, p->c_parseflags,
516 *argv);
517 argc--, argv++;
518 }
519 parse_dump_buf();
520
521 return (0);
522 }
523
524 /*
525 * Print command usage on standard error.
526 */
527 static void
528 usage(void)
529 {
530 (void) fprintf(stderr,
531 "usage: ifparse [ -fs ] <addr_family> <commands>\n");
532 }
533
534 int
535 main(int argc, char *argv[])
536 {
537 int c;
538 struct afswtch *afp;
539
540 while ((c = getopt(argc, argv, "fs")) != -1) {
541 switch ((char)c) {
542 case 'f':
543 parsemode |= PARSEMOVABLE;
544 break;
545 case 's':
546 parsemode |= PARSEFIXED;
547 break;
548 case '?':
549 usage();
550 exit(1);
551 }
552 }
553
554 if (parsemode == 0)
555 parsemode = PARSEFIXED | PARSEMOVABLE;
556
557 argc -= optind;
558 argv += optind;
559
560 afp = afs;
561 if (argc > 0) {
562 struct afswtch *aftp;
563 for (aftp = afs; aftp->af_name; aftp++) {
564 if (strcmp(aftp->af_name, *argv) == 0) {
565 argc--; argv++;
566 afp = aftp;
567 break;
568 }
569 }
570 }
571
572 return (ifparse(argc, argv, afp));
573 }