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) != 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
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