2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
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.
11 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
15 * Ifparse splits up an ifconfig command line, and was written for use
16 * with the networking boot scripts; see $SRC/cmd/svc/shell/net_include.sh
18 * Ifparse can extract selected parts of the ifconfig command line,
19 * such as failover address configuration ("ifparse -f"), or everything
20 * except failover address configuration ("ifparse -s"). By default,
21 * all parts of the command line are extracted (equivalent to ("ifparse -fs").
27 * ifparse inet 1.2.3.4 up group two addif 1.2.3.5 up addif 1.2.3.6 up
29 * Produces the following on standard output:
36 * The optional "set" and "destination" keywords are added to make the
37 * output easier to process by a script or another command.
41 * ifparse -f inet 1.2.3.4 -failover up group two addif 1.2.3.5 up
47 * Only failover address configuration has been requested. Address
48 * 1.2.3.4 is a non-failover address, and so isn't output.
50 * The "failover" and "-failover" commands can occur several times for
51 * a given logical interface. Only the last one counts. For example:
53 * ifparse -f inet 1.2.3.4 -failover failover -failover failover up
57 * set 1.2.3.4 -failover failover -failover failover up
59 * No attempt is made to clean up such "pathological" command lines, by
60 * removing redundant "failover" and "-failover" commands.
63 #include <sys/types.h>
74 * Command should only appear if non-failover commands
77 * Command should only appear if failover commands are
80 * Don't buffer the command, dump it to output immediately.
82 * Indicates processing has moved on to additional
84 * Dump the buffer to output and clear buffer contents.
86 * The "set" and "destination" keywords are optional.
87 * This flag indicates that the next address not prefixed
88 * with a keyword will be a destination address.
90 * Command not valid on additional logical interfaces.
93 #define PARSEFIXED 0x01
94 #define PARSEMOVABLE 0x02
98 #define PARSELOG0 0x20
100 typedef enum { AF_UNSPEC
, AF_INET
, AF_INET6
, AF_ANY
} ac_t
;
102 #define NEXTARG (-1) /* command takes an argument */
103 #define OPTARG (-2) /* command takes an optional argument */
105 #define END_OF_TABLE (-1)
107 /* Parsemode, the type of commands requested by the user. */
110 /* Parsetype, the type of the command currently in the buffer. */
111 int parsetype
= PARSEFIXED
| PARSEMOVABLE
;
113 /* Parsebuf, pointer to the buffer. */
114 char *parsebuf
= NULL
;
116 /* Parsebuflen, the size of the buffer area. */
117 unsigned parsebuflen
= 0;
119 /* Parsedumplen, the amount of the buffer currently in use. */
120 unsigned parsedumplen
= 0;
123 * Setaddr, used to decide whether an address without a keyword
124 * prefix is a source or destination address.
126 boolean_t setaddr
= _B_FALSE
;
129 * Some ifconfig commands are only valid on the first logical interface.
130 * As soon as an "addif" command is seen, "addint" is set.
132 boolean_t addint
= _B_FALSE
;
135 * The parser table is based on that in ifconfig. A command may or
136 * may not have an argument, as indicated by whether NEXTARG/OPTARG is
137 * in the second column. Some commands can only be used with certain
138 * address families, as indicated in the third column. The fourth column
139 * contains flags that control parser action.
141 * Ifparse buffers logical interface configuration commands such as "set",
142 * "netmask" and "broadcast". This buffering continues until an "addif"
143 * command is seen, at which point the buffer is emptied, and the process
146 * Some commands do not relate to logical interface configuration and are
147 * dumped to output as soon as they are seen, such as "group" and "standby".
153 int c_parameter
; /* NEXTARG means next argv */
154 int c_af
; /* address family restrictions */
155 int c_parseflags
; /* parsing flags */
157 { "up", 0, AF_ANY
, 0 },
158 { "down", 0, AF_ANY
, 0 },
159 { "trailers", 0, AF_ANY
, PARSENOW
},
160 { "-trailers", 0, AF_ANY
, PARSENOW
},
161 { "arp", 0, AF_INET
, PARSENOW
},
162 { "-arp", 0, AF_INET
, PARSENOW
},
163 { "private", 0, AF_ANY
, 0 },
164 { "-private", 0, AF_ANY
, 0 },
165 { "router", 0, AF_ANY
, PARSELOG0
},
166 { "-router", 0, AF_ANY
, PARSELOG0
},
167 { "xmit", 0, AF_ANY
, 0 },
168 { "-xmit", 0, AF_ANY
, 0 },
169 { "-nud", 0, AF_INET6
, PARSENOW
},
170 { "nud", 0, AF_INET6
, PARSENOW
},
171 { "anycast", 0, AF_ANY
, 0 },
172 { "-anycast", 0, AF_ANY
, 0 },
173 { "local", 0, AF_ANY
, 0 },
174 { "-local", 0, AF_ANY
, 0 },
175 { "deprecated", 0, AF_ANY
, 0 },
176 { "-deprecated", 0, AF_ANY
, 0 },
177 { "preferred", 0, AF_INET6
, 0 },
178 { "-preferred", 0, AF_INET6
, 0 },
179 { "debug", 0, AF_ANY
, PARSENOW
},
180 { "verbose", 0, AF_ANY
, PARSENOW
},
181 { "netmask", NEXTARG
, AF_INET
, 0 },
182 { "metric", NEXTARG
, AF_ANY
, 0 },
183 { "mtu", NEXTARG
, AF_ANY
, 0 },
184 { "index", NEXTARG
, AF_ANY
, PARSELOG0
},
185 { "broadcast", NEXTARG
, AF_INET
, 0 },
186 { "auto-revarp", 0, AF_INET
, PARSEFIXED
},
187 { "plumb", 0, AF_ANY
, PARSENOW
},
188 { "unplumb", 0, AF_ANY
, PARSENOW
},
189 { "ipmp", 0, AF_ANY
, PARSELOG0
},
190 { "subnet", NEXTARG
, AF_ANY
, 0 },
191 { "token", NEXTARG
, AF_INET6
, PARSELOG0
},
192 { "tsrc", NEXTARG
, AF_ANY
, PARSELOG0
},
193 { "tdst", NEXTARG
, AF_ANY
, PARSELOG0
},
194 { "encr_auth_algs", NEXTARG
, AF_ANY
, PARSELOG0
},
195 { "encr_algs", NEXTARG
, AF_ANY
, PARSELOG0
},
196 { "auth_algs", NEXTARG
, AF_ANY
, PARSELOG0
},
197 { "addif", NEXTARG
, AF_ANY
, PARSEADD
},
198 { "removeif", NEXTARG
, AF_ANY
, PARSELOG0
},
199 { "modlist", 0, AF_ANY
, PARSENOW
},
200 { "modinsert", NEXTARG
, AF_ANY
, PARSENOW
},
201 { "modremove", NEXTARG
, AF_ANY
, PARSENOW
},
202 { "failover", 0, AF_ANY
, PARSEMOVABLE
},
203 { "-failover", 0, AF_ANY
, PARSEFIXED
},
204 { "standby", 0, AF_ANY
, PARSENOW
},
205 { "-standby", 0, AF_ANY
, PARSENOW
},
206 { "failed", 0, AF_ANY
, PARSENOW
},
207 { "-failed", 0, AF_ANY
, PARSENOW
},
208 { "group", NEXTARG
, AF_ANY
, PARSELOG0
},
209 { "configinfo", 0, AF_ANY
, PARSENOW
},
210 { "encaplimit", NEXTARG
, AF_ANY
, PARSELOG0
},
211 { "-encaplimit", 0, AF_ANY
, PARSELOG0
},
212 { "thoplimit", NEXTARG
, AF_ANY
, PARSELOG0
},
213 { "set", NEXTARG
, AF_ANY
, PARSESET
},
214 { "destination", NEXTARG
, AF_ANY
, 0 },
215 { "zone", NEXTARG
, AF_ANY
, 0 },
216 { "-zone", 0, AF_ANY
, 0 },
217 { "ether", OPTARG
, AF_ANY
, PARSENOW
},
218 { "usesrc", NEXTARG
, AF_ANY
, PARSENOW
},
219 { 0 /* ether addr */, 0, AF_UNSPEC
, PARSELOG0
},
220 { 0 /* set */, 0, AF_ANY
, PARSESET
},
221 { 0 /* destination */, 0, AF_ANY
, 0 },
222 { 0, END_OF_TABLE
, END_OF_TABLE
, END_OF_TABLE
},
226 /* Known address families */
232 { "ether", AF_UNSPEC
},
233 { "inet6", AF_INET6
},
238 * Append "item" to the buffer. If there isn't enough room in the buffer,
242 parse_append_buf(char *item
)
250 itemlen
= strlen(item
);
251 newdumplen
= parsedumplen
+ itemlen
;
253 /* Expand dump buffer as needed */
254 if (parsebuflen
< newdumplen
) {
255 if ((parsebuf
= realloc(parsebuf
, newdumplen
)) == NULL
) {
259 parsebuflen
= newdumplen
;
261 (void) memcpy(parsebuf
+ parsedumplen
, item
, itemlen
);
263 parsedumplen
= newdumplen
;
267 * Dump the buffer to output.
273 * When parsing, a set or addif command, we may be some way into
274 * the command before we definitely know it is movable or fixed.
275 * If we get to the end of the command, and haven't seen a
276 * "failover" or "-failover" flag, the command is movable.
278 if (!((parsemode
== PARSEFIXED
) && (parsetype
& PARSEMOVABLE
) != 0) &&
279 (parsemode
& parsetype
) != 0 && parsedumplen
!= 0) {
282 if (parsebuf
[parsedumplen
] == ' ')
285 for (i
= 0; i
< parsedumplen
; i
++)
286 (void) putchar(parsebuf
[i
]);
288 (void) putchar('\n');
290 /* The buffer is kept in case there is more parsing to do */
292 parsetype
= PARSEFIXED
| PARSEMOVABLE
;
296 * Process a command. The command will either be put in the buffer,
297 * or dumped directly to output. The current contents of the buffer
298 * may be dumped to output.
300 * The buffer holds commands relating to a particular logical interface.
301 * For example, "set", "destination", "failover", "broadcast", all relate
302 * to a particular interface. Such commands have to be buffered until
303 * all the "failover" and "-failover" commands for that interface have
304 * been seen, only then will we know whether the command is movable
305 * or not. When the "addif" command is seen, we know we are about to
306 * start processing a new logical interface, we've seen all the
307 * "failover" and "-failover" commands for the previous interface, and
308 * can decide whether the buffer contents are movable or not.
312 parsedump(char *cmd
, int param
, int flags
, char *arg
)
314 char *cmdname
; /* Command name */
315 char *cmdarg
; /* Argument to command, if it takes one, or NULL */
318 * Is command only valid on logical interface 0?
319 * If processing commands on an additional logical interface, ignore
321 * If processing commands on logical interface 0, don't buffer the
322 * command, dump it straight to output.
324 if ((flags
& PARSELOG0
) != 0) {
331 * If processing the "addif" command, a destination address may
332 * follow without the "destination" prefix. Add PARSESET to the
333 * flags so that such an anonymous address is processed correctly.
335 if ((flags
& PARSEADD
) != 0) {
341 * Commands that must be dumped straight to output are always fixed
342 * (non-movable) commands.
345 if ((flags
& PARSENOW
) != 0)
349 * Source and destination addresses do not have to be prefixed
350 * with the keywords "set" or "destination". Ifparse always
351 * inserts the optional keyword.
355 if ((flags
& PARSESET
) != 0)
358 cmdname
= "destination";
363 cmdarg
= (param
== 0) ? NULL
: arg
;
368 * The next address without a prefix will be a destination
371 if ((flags
& PARSESET
) != 0)
375 * Dump the command straight to output?
376 * Only dump the command if the parse mode specified on
377 * the command line matches the type of the command.
379 if ((flags
& PARSENOW
) != 0) {
380 if ((parsemode
& flags
) != 0) {
381 (void) fputs(cmdname
, stdout
);
382 if (cmdarg
!= NULL
) {
383 (void) fputc(' ', stdout
);
384 (void) fputs(cmdarg
, stdout
);
386 (void) fputc('\n', stdout
);
392 * Only the commands relating to a particular logical interface
393 * are buffered. When an "addif" command is seen, processing is
394 * about to start on a new logical interface, so dump the
397 if ((flags
& PARSEADD
) != 0)
401 * If the command flags indicate the command is fixed or
402 * movable, update the type of the interface in the buffer
403 * accordingly. For example, "-failover" has the "PARSEFIXED"
404 * flag, and the contents of the buffer are not movable if
405 * "-failover" is seen.
407 if ((flags
& PARSEFIXED
) != 0)
408 parsetype
&= ~PARSEMOVABLE
;
410 if ((flags
& PARSEMOVABLE
) != 0)
411 parsetype
&= ~PARSEFIXED
;
413 parsetype
|= flags
& (PARSEFIXED
| PARSEMOVABLE
);
415 parse_append_buf(cmdname
);
417 if (cmdarg
!= NULL
) {
418 parse_append_buf(" ");
419 parse_append_buf(cmdarg
);
422 parse_append_buf(" ");
426 * Parse the part of the command line following the address family
427 * specification, if any.
429 * This function is a modified version of the function "ifconfig" in
433 ifparse(int argc
, char *argv
[], struct afswtch
*afp
)
440 if (strcmp(*argv
, "auto-dhcp") == 0 || strcmp(*argv
, "dhcp") == 0) {
441 if ((parsemode
& PARSEFIXED
) != 0) {
443 (void) fputs(*argv
++, stdout
);
445 (void) fputc(' ', stdout
);
447 (void) fputc('\n', stdout
);
457 found_cmd
= _B_FALSE
;
458 for (p
= cmds
; ; p
++) {
459 assert(p
->c_parseflags
!= END_OF_TABLE
);
461 if (strcmp(*argv
, p
->c_name
) == 0) {
463 * indicate that the command was
464 * found and check to see if
465 * the address family is valid
468 if (p
->c_af
== AF_ANY
||
473 if (p
->c_af
== AF_ANY
||
478 assert(p
->c_parseflags
!= END_OF_TABLE
);
480 * If we found the keyword, but the address family
481 * did not match spit out an error
483 if (found_cmd
&& p
->c_name
== 0) {
484 (void) fprintf(stderr
, "ifparse: Operation %s not"
485 " supported for %s\n", *argv
, afp
->af_name
);
489 * else (no keyword found), we assume it's an address
492 if (p
->c_name
== 0 && setaddr
) {
493 p
++; /* got src, do dst */
494 assert(p
->c_parseflags
!= END_OF_TABLE
);
497 if (p
->c_parameter
== NEXTARG
|| p
->c_parameter
== OPTARG
) {
499 if (argc
== 0 && p
->c_parameter
== NEXTARG
) {
500 (void) fprintf(stderr
,
501 "ifparse: no argument for %s\n",
508 * Dump the command if:
510 * there's no address family
513 * there is a restriction AND
514 * the address families match
516 if ((p
->c_af
== AF_ANY
) || (af
== p
->c_af
))
517 parsedump(p
->c_name
, p
->c_parameter
, p
->c_parseflags
,
527 * Print command usage on standard error.
532 (void) fprintf(stderr
,
533 "usage: ifparse [ -fs ] <addr_family> <commands>\n");
537 main(int argc
, char *argv
[])
542 while ((c
= getopt(argc
, argv
, "fs")) != -1) {
545 parsemode
|= PARSEMOVABLE
;
548 parsemode
|= PARSEFIXED
;
557 parsemode
= PARSEFIXED
| PARSEMOVABLE
;
564 struct afswtch
*aftp
;
565 for (aftp
= afs
; aftp
->af_name
; aftp
++) {
566 if (strcmp(aftp
->af_name
, *argv
) == 0) {
574 return (ifparse(argc
, argv
, afp
));