1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <locale.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <stropts.h>
33 #include <errno.h>
34 #include <strings.h>
35 #include <getopt.h>
36 #include <unistd.h>
37 #include <priv.h>
38 #include <netdb.h>
39 #include <libintl.h>
40 #include <libdlflow.h>
41 #include <libdllink.h>
42 #include <libdlstat.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <sys/ethernet.h>
48 #include <inet/ip.h>
49 #include <inet/ip6.h>
50 #include <stddef.h>
51 #include <ofmt.h>
52
53 typedef struct show_flow_state {
54 dladm_status_t fs_status;
55 ofmt_handle_t fs_ofmt;
56 const char *fs_flow;
57 boolean_t fs_parsable;
58 boolean_t fs_persist;
59 } show_flow_state_t;
60
61 typedef void cmdfunc_t(int, char **);
62
63 static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow;
64 static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop;
65
66 static int show_flow(dladm_handle_t, dladm_flow_attr_t *, void *);
67 static int show_flows_onelink(dladm_handle_t, datalink_id_t, void *);
68
69 static int remove_flow(dladm_handle_t, dladm_flow_attr_t *, void *);
70
71 static int show_flowprop(dladm_handle_t, dladm_flow_attr_t *, void *);
72 static void show_flowprop_one_flow(void *, const char *);
73 static int show_flowprop_onelink(dladm_handle_t, datalink_id_t, void *);
74
75 static void die(const char *, ...);
76 static void die_optdup(int);
77 static void die_opterr(int, int);
78 static void die_dlerr(dladm_status_t, const char *, ...);
79 static void warn(const char *, ...);
80 static void warn_dlerr(dladm_status_t, const char *, ...);
81
82 /* callback functions for printing output */
83 static ofmt_cb_t print_flowprop_cb, print_default_cb;
84 static void flowadm_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
85
86 typedef struct cmd {
87 char *c_name;
88 void (*c_fn)(int, char **);
89 } cmd_t;
90
91 static cmd_t cmds[] = {
92 { "add-flow", do_add_flow },
93 { "remove-flow", do_remove_flow },
94 { "show-flowprop", do_show_flowprop },
95 { "set-flowprop", do_set_flowprop },
96 { "reset-flowprop", do_reset_flowprop },
97 { "show-flow", do_show_flow },
98 { "init-flow", do_init_flow },
99 };
100
101 static const struct option longopts[] = {
102 {"link", required_argument, 0, 'l'},
103 {"parsable", no_argument, 0, 'p'},
104 {"parseable", no_argument, 0, 'p'},
105 {"temporary", no_argument, 0, 't'},
106 {"root-dir", required_argument, 0, 'R'},
107 { 0, 0, 0, 0 }
108 };
109
110 static const struct option prop_longopts[] = {
111 {"link", required_argument, 0, 'l'},
112 {"temporary", no_argument, 0, 't'},
113 {"root-dir", required_argument, 0, 'R'},
114 {"prop", required_argument, 0, 'p'},
115 {"attr", required_argument, 0, 'a'},
116 { 0, 0, 0, 0 }
117 };
118
119 /*
120 * structures for 'flowadm remove-flow'
121 */
122 typedef struct remove_flow_state {
123 boolean_t fs_tempop;
124 const char *fs_altroot;
125 dladm_status_t fs_status;
126 } remove_flow_state_t;
127
128 #define PROTO_MAXSTR_LEN 7
129 #define PORT_MAXSTR_LEN 6
130 #define DSFIELD_MAXSTR_LEN 10
131 #define NULL_OFMT {NULL, 0, 0, NULL}
132
133 typedef struct flow_fields_buf_s
134 {
135 char flow_name[MAXFLOWNAMELEN];
136 char flow_link[MAXLINKNAMELEN];
137 char flow_ipaddr[INET6_ADDRSTRLEN+4];
138 char flow_proto[PROTO_MAXSTR_LEN];
139 char flow_lport[PORT_MAXSTR_LEN];
140 char flow_rport[PORT_MAXSTR_LEN];
141 char flow_dsfield[DSFIELD_MAXSTR_LEN];
142 } flow_fields_buf_t;
143
144 static ofmt_field_t flow_fields[] = {
145 /* name, field width, index */
146 { "FLOW", 12,
147 offsetof(flow_fields_buf_t, flow_name), print_default_cb},
148 { "LINK", 12,
149 offsetof(flow_fields_buf_t, flow_link), print_default_cb},
150 { "IPADDR", 25,
151 offsetof(flow_fields_buf_t, flow_ipaddr), print_default_cb},
152 { "PROTO", 7,
153 offsetof(flow_fields_buf_t, flow_proto), print_default_cb},
154 { "LPORT", 8,
155 offsetof(flow_fields_buf_t, flow_lport), print_default_cb},
156 { "RPORT", 8,
157 offsetof(flow_fields_buf_t, flow_rport), print_default_cb},
158 { "DSFLD", 10,
159 offsetof(flow_fields_buf_t, flow_dsfield), print_default_cb},
160 NULL_OFMT}
161 ;
162
163 /*
164 * structures for 'flowadm show-flowprop'
165 */
166 typedef enum {
167 FLOWPROP_FLOW,
168 FLOWPROP_PROPERTY,
169 FLOWPROP_VALUE,
170 FLOWPROP_DEFAULT,
171 FLOWPROP_POSSIBLE
172 } flowprop_field_index_t;
173
174 static ofmt_field_t flowprop_fields[] = {
175 /* name, fieldwidth, index, callback */
176 { "FLOW", 13, FLOWPROP_FLOW, print_flowprop_cb},
177 { "PROPERTY", 16, FLOWPROP_PROPERTY, print_flowprop_cb},
178 { "VALUE", 15, FLOWPROP_VALUE, print_flowprop_cb},
179 { "DEFAULT", 15, FLOWPROP_DEFAULT, print_flowprop_cb},
180 { "POSSIBLE", 21, FLOWPROP_POSSIBLE, print_flowprop_cb},
181 NULL_OFMT}
182 ;
183
184 #define MAX_PROP_LINE 512
185
186 typedef struct show_flowprop_state {
187 const char *fs_flow;
188 datalink_id_t fs_linkid;
189 char *fs_line;
190 char **fs_propvals;
191 dladm_arg_list_t *fs_proplist;
192 boolean_t fs_parsable;
193 boolean_t fs_persist;
194 boolean_t fs_header;
195 dladm_status_t fs_status;
196 dladm_status_t fs_retstatus;
197 ofmt_handle_t fs_ofmt;
198 } show_flowprop_state_t;
199
200 typedef struct set_flowprop_state {
201 const char *fs_name;
202 boolean_t fs_reset;
203 boolean_t fs_temp;
204 dladm_status_t fs_status;
205 } set_flowprop_state_t;
206
207 typedef struct flowprop_args_s {
208 show_flowprop_state_t *fs_state;
209 char *fs_propname;
210 char *fs_flowname;
211 } flowprop_args_t;
212
213 static char *progname;
214
215 boolean_t t_arg = B_FALSE; /* changes are persistent */
216 char *altroot = NULL;
217
218 /*
219 * Handle to libdladm. Opened in main() before the sub-command
220 * specific function is called.
221 */
222 static dladm_handle_t handle = NULL;
223
224 static const char *attr_table[] =
225 {"local_ip", "remote_ip", "transport", "local_port", "remote_port",
226 "dsfield"};
227
228 #define NATTR (sizeof (attr_table)/sizeof (char *))
229
230 static void
usage(void)231 usage(void)
232 {
233 (void) fprintf(stderr, gettext("usage: flowadm <subcommand>"
234 " <args>...\n"
235 " add-flow [-t] -l <link> -a <attr>=<value>[,...]\n"
236 "\t\t [-p <prop>=<value>,...] <flow>\n"
237 " remove-flow [-t] {-l <link> | <flow>}\n"
238 " show-flow [-p] [-l <link>] "
239 "[<flow>]\n\n"
240 " set-flowprop [-t] -p <prop>=<value>[,...] <flow>\n"
241 " reset-flowprop [-t] [-p <prop>,...] <flow>\n"
242 " show-flowprop [-cP] [-l <link>] [-p <prop>,...] "
243 "[<flow>]\n"));
244
245 /* close dladm handle if it was opened */
246 if (handle != NULL)
247 dladm_close(handle);
248
249 exit(1);
250 }
251
252 int
main(int argc,char * argv[])253 main(int argc, char *argv[])
254 {
255 int i, arglen, cmdlen;
256 cmd_t *cmdp;
257 dladm_status_t status;
258
259 (void) setlocale(LC_ALL, "");
260 #if !defined(TEXT_DOMAIN)
261 #define TEXT_DOMAIN "SYS_TEST"
262 #endif
263 (void) textdomain(TEXT_DOMAIN);
264
265 progname = argv[0];
266
267 if (argc < 2)
268 usage();
269
270 for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
271 cmdp = &cmds[i];
272 arglen = strlen(argv[1]);
273 cmdlen = strlen(cmdp->c_name);
274 if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name,
275 cmdlen) == 0)) {
276 /* Open the libdladm handle */
277 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
278 die_dlerr(status,
279 "could not open /dev/dld");
280 }
281
282 cmdp->c_fn(argc - 1, &argv[1]);
283
284 dladm_close(handle);
285 exit(EXIT_SUCCESS);
286 }
287 }
288
289 (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
290 progname, argv[1]);
291 usage();
292
293 return (0);
294 }
295
296 static const char *
match_attr(char * attr)297 match_attr(char *attr)
298 {
299 int i;
300
301 for (i = 0; i < NATTR; i++) {
302 if (strlen(attr) == strlen(attr_table[i]) &&
303 strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) {
304 return (attr);
305 }
306 }
307 return (NULL);
308 }
309
310 /* ARGSUSED */
311 static void
do_init_flow(int argc,char * argv[])312 do_init_flow(int argc, char *argv[])
313 {
314 dladm_status_t status;
315
316 status = dladm_flow_init(handle);
317 if (status != DLADM_STATUS_OK)
318 die_dlerr(status, "flows initialization failed");
319 }
320
321 static void
do_add_flow(int argc,char * argv[])322 do_add_flow(int argc, char *argv[])
323 {
324 char devname[MAXLINKNAMELEN];
325 char *name = NULL;
326 uint_t index;
327 datalink_id_t linkid;
328
329 char option;
330 boolean_t l_arg = B_FALSE;
331 char propstr[DLADM_STRSIZE];
332 char attrstr[DLADM_STRSIZE];
333 dladm_arg_list_t *proplist = NULL;
334 dladm_arg_list_t *attrlist = NULL;
335 dladm_status_t status;
336
337 bzero(propstr, DLADM_STRSIZE);
338 bzero(attrstr, DLADM_STRSIZE);
339
340 while ((option = getopt_long(argc, argv, "tR:l:a:p:",
341 prop_longopts, NULL)) != -1) {
342 switch (option) {
343 case 't':
344 t_arg = B_TRUE;
345 break;
346 case 'R':
347 altroot = optarg;
348 break;
349 case 'l':
350 if (strlcpy(devname, optarg,
351 MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
352 die("link name too long");
353 }
354 if (dladm_name2info(handle, devname, &linkid, NULL,
355 NULL, NULL) != DLADM_STATUS_OK)
356 die("invalid link '%s'", devname);
357 l_arg = B_TRUE;
358 break;
359 case 'a':
360 (void) strlcat(attrstr, optarg, DLADM_STRSIZE);
361 if (strlcat(attrstr, ",", DLADM_STRSIZE) >=
362 DLADM_STRSIZE)
363 die("attribute list too long '%s'", attrstr);
364 break;
365 case 'p':
366 (void) strlcat(propstr, optarg, DLADM_STRSIZE);
367 if (strlcat(propstr, ",", DLADM_STRSIZE) >=
368 DLADM_STRSIZE)
369 die("property list too long '%s'", propstr);
370 break;
371 default:
372 die_opterr(optopt, option);
373 }
374 }
375 if (!l_arg) {
376 die("link is required");
377 }
378
379 opterr = 0;
380 index = optind;
381
382 if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) {
383 die("flow name is required");
384 } else {
385 /* get flow name; required last argument */
386 if (strlen(argv[index]) >= MAXFLOWNAMELEN)
387 die("flow name too long");
388 name = argv[index];
389 }
390
391 if (dladm_parse_flow_attrs(attrstr, &attrlist, B_FALSE)
392 != DLADM_STATUS_OK)
393 die("invalid flow attribute specified");
394 if (dladm_parse_flow_props(propstr, &proplist, B_FALSE)
395 != DLADM_STATUS_OK)
396 die("invalid flow property specified");
397
398 status = dladm_flow_add(handle, linkid, attrlist, proplist, name,
399 t_arg, altroot);
400 if (status != DLADM_STATUS_OK)
401 die_dlerr(status, "add flow failed");
402
403 dladm_free_attrs(attrlist);
404 dladm_free_props(proplist);
405 }
406
407 static void
do_remove_flow(int argc,char * argv[])408 do_remove_flow(int argc, char *argv[])
409 {
410 char option;
411 char *flowname = NULL;
412 char linkname[MAXLINKNAMELEN];
413 datalink_id_t linkid = DATALINK_ALL_LINKID;
414 boolean_t l_arg = B_FALSE;
415 remove_flow_state_t state;
416 dladm_status_t status;
417
418 bzero(&state, sizeof (state));
419
420 opterr = 0;
421 while ((option = getopt_long(argc, argv, ":tR:l:",
422 longopts, NULL)) != -1) {
423 switch (option) {
424 case 't':
425 t_arg = B_TRUE;
426 break;
427 case 'R':
428 altroot = optarg;
429 break;
430 case 'l':
431 if (strlcpy(linkname, optarg,
432 MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
433 die("link name too long");
434 }
435 if (dladm_name2info(handle, linkname, &linkid, NULL,
436 NULL, NULL) != DLADM_STATUS_OK) {
437 die("invalid link '%s'", linkname);
438 }
439 l_arg = B_TRUE;
440 break;
441 default:
442 die_opterr(optopt, option);
443 break;
444 }
445 }
446
447 /* when link not specified get flow name */
448 if (!l_arg) {
449 if (optind != (argc-1)) {
450 usage();
451 } else {
452 if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
453 die("flow name too long");
454 flowname = argv[optind];
455 }
456 status = dladm_flow_remove(handle, flowname, t_arg, altroot);
457 } else {
458 /* if link is specified then flow name should not be there */
459 if (optind == argc-1)
460 usage();
461 /* walk the link to find flows and remove them */
462 state.fs_tempop = t_arg;
463 state.fs_altroot = altroot;
464 state.fs_status = DLADM_STATUS_OK;
465 status = dladm_walk_flow(remove_flow, handle, linkid, &state,
466 B_FALSE);
467 /*
468 * check if dladm_walk_flow terminated early and see if the
469 * walker function as any status for us
470 */
471 if (status == DLADM_STATUS_OK)
472 status = state.fs_status;
473 }
474
475 if (status != DLADM_STATUS_OK)
476 die_dlerr(status, "remove flow failed");
477 }
478
479 /*
480 * Walker function for removing a flow through dladm_walk_flow();
481 */
482 /*ARGSUSED*/
483 static int
remove_flow(dladm_handle_t handle,dladm_flow_attr_t * attr,void * arg)484 remove_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
485 {
486 remove_flow_state_t *state = (remove_flow_state_t *)arg;
487
488 state->fs_status = dladm_flow_remove(handle, attr->fa_flowname,
489 state->fs_tempop, state->fs_altroot);
490
491 if (state->fs_status == DLADM_STATUS_OK)
492 return (DLADM_WALK_CONTINUE);
493 else
494 return (DLADM_WALK_TERMINATE);
495 }
496
497 /*ARGSUSED*/
498 static dladm_status_t
print_flow(show_flow_state_t * state,dladm_flow_attr_t * attr,flow_fields_buf_t * fbuf)499 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr,
500 flow_fields_buf_t *fbuf)
501 {
502 char link[MAXLINKNAMELEN];
503 dladm_status_t status;
504
505 if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL,
506 NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
507 return (status);
508 }
509
510 (void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name),
511 "%s", attr->fa_flowname);
512 (void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link),
513 "%s", link);
514
515 (void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr,
516 sizeof (fbuf->flow_ipaddr));
517 (void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto,
518 sizeof (fbuf->flow_proto));
519 if ((attr->fa_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL) != 0) {
520 (void) dladm_flow_attr_port2str(attr, fbuf->flow_lport,
521 sizeof (fbuf->flow_lport));
522 }
523 if ((attr->fa_flow_desc.fd_mask & FLOW_ULP_PORT_REMOTE) != 0) {
524 (void) dladm_flow_attr_port2str(attr, fbuf->flow_rport,
525 sizeof (fbuf->flow_rport));
526 }
527 (void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield,
528 sizeof (fbuf->flow_dsfield));
529
530 return (DLADM_STATUS_OK);
531 }
532
533 /*
534 * Walker function for showing flow attributes through dladm_walk_flow().
535 */
536 /*ARGSUSED*/
537 static int
show_flow(dladm_handle_t handle,dladm_flow_attr_t * attr,void * arg)538 show_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
539 {
540 show_flow_state_t *statep = arg;
541 dladm_status_t status;
542 flow_fields_buf_t fbuf;
543
544 /*
545 * first get all the flow attributes into fbuf;
546 */
547 bzero(&fbuf, sizeof (fbuf));
548 status = print_flow(statep, attr, &fbuf);
549
550 if (status != DLADM_STATUS_OK)
551 goto done;
552
553 ofmt_print(statep->fs_ofmt, (void *)&fbuf);
554
555 done:
556 statep->fs_status = status;
557 return (DLADM_WALK_CONTINUE);
558 }
559
560 static void
show_one_flow(void * arg,const char * name)561 show_one_flow(void *arg, const char *name)
562 {
563 dladm_flow_attr_t attr;
564
565 if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK)
566 die("invalid flow: '%s'", name);
567 else
568 (void) show_flow(handle, &attr, arg);
569 }
570
571 /*
572 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to
573 * dladm_walk_datalink_id(). Used for showing flow attributes for
574 * all flows on all links.
575 */
576 static int
show_flows_onelink(dladm_handle_t dh,datalink_id_t linkid,void * arg)577 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
578 {
579 show_flow_state_t *state = arg;
580
581 (void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist);
582
583 return (DLADM_WALK_CONTINUE);
584 }
585
586 static void
do_show_flow(int argc,char * argv[])587 do_show_flow(int argc, char *argv[])
588 {
589 char flowname[MAXFLOWNAMELEN];
590 char linkname[MAXLINKNAMELEN];
591 datalink_id_t linkid = DATALINK_ALL_LINKID;
592 int option;
593 boolean_t l_arg = B_FALSE;
594 boolean_t o_arg = B_FALSE;
595 show_flow_state_t state;
596 char *fields_str = NULL;
597 ofmt_handle_t ofmt;
598 ofmt_status_t oferr;
599 uint_t ofmtflags = 0;
600
601 bzero(&state, sizeof (state));
602
603 opterr = 0;
604 while ((option = getopt_long(argc, argv, ":pPl:o:",
605 longopts, NULL)) != -1) {
606 switch (option) {
607 case 'p':
608 state.fs_parsable = B_TRUE;
609 ofmtflags |= OFMT_PARSABLE;
610 break;
611 case 'P':
612 state.fs_persist = B_TRUE;
613 break;
614 case 'o':
615 if (o_arg)
616 die_optdup(option);
617
618 o_arg = B_TRUE;
619 fields_str = optarg;
620 break;
621 case 'l':
622 if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
623 >= MAXLINKNAMELEN)
624 die("link name too long\n");
625 if (dladm_name2info(handle, linkname, &linkid, NULL,
626 NULL, NULL) != DLADM_STATUS_OK)
627 die("invalid link '%s'", linkname);
628 l_arg = B_TRUE;
629 break;
630 default:
631 die_opterr(optopt, option);
632 break;
633 }
634 }
635
636 /* get flow name (optional last argument */
637 if (optind == (argc-1)) {
638 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
639 >= MAXFLOWNAMELEN)
640 die("flow name too long");
641 state.fs_flow = flowname;
642 }
643
644 oferr = ofmt_open(fields_str, flow_fields, ofmtflags, 0, &ofmt);
645 flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
646 state.fs_ofmt = ofmt;
647
648 /* Show attributes of one flow */
649 if (state.fs_flow != NULL) {
650 show_one_flow(&state, state.fs_flow);
651
652 /* Show attributes of flows on one link */
653 } else if (l_arg) {
654 (void) show_flows_onelink(handle, linkid, &state);
655
656 /* Show attributes of all flows on all links */
657 } else {
658 (void) dladm_walk_datalink_id(show_flows_onelink, handle,
659 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
660 DLADM_OPT_ACTIVE);
661 }
662 ofmt_close(ofmt);
663 }
664
665 static dladm_status_t
set_flowprop_persist(const char * flow,const char * prop_name,char ** prop_val,uint_t val_cnt,boolean_t reset)666 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val,
667 uint_t val_cnt, boolean_t reset)
668 {
669 dladm_status_t status;
670 char *errprop;
671
672 status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt,
673 DLADM_OPT_PERSIST, &errprop);
674
675 if (status != DLADM_STATUS_OK) {
676 warn_dlerr(status, "cannot persistently %s flow "
677 "property '%s' on '%s'", reset? "reset": "set",
678 errprop, flow);
679 }
680 return (status);
681 }
682
683 static void
set_flowprop(int argc,char ** argv,boolean_t reset)684 set_flowprop(int argc, char **argv, boolean_t reset)
685 {
686 int i, option;
687 char errmsg[DLADM_STRSIZE];
688 const char *flow = NULL;
689 char propstr[DLADM_STRSIZE];
690 dladm_arg_list_t *proplist = NULL;
691 boolean_t temp = B_FALSE;
692 dladm_status_t status = DLADM_STATUS_OK;
693
694 opterr = 0;
695 bzero(propstr, DLADM_STRSIZE);
696
697 while ((option = getopt_long(argc, argv, ":p:R:t",
698 prop_longopts, NULL)) != -1) {
699 switch (option) {
700 case 'p':
701 (void) strlcat(propstr, optarg, DLADM_STRSIZE);
702 if (strlcat(propstr, ",", DLADM_STRSIZE) >=
703 DLADM_STRSIZE)
704 die("property list too long '%s'", propstr);
705 break;
706 case 't':
707 temp = B_TRUE;
708 break;
709 case 'R':
710 status = dladm_set_rootdir(optarg);
711 if (status != DLADM_STATUS_OK) {
712 die_dlerr(status, "invalid directory "
713 "specified");
714 }
715 break;
716 default:
717 die_opterr(optopt, option);
718 break;
719 }
720 }
721
722 if (optind == (argc - 1)) {
723 if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
724 die("flow name too long");
725 flow = argv[optind];
726 } else if (optind != argc) {
727 usage();
728 }
729 if (flow == NULL)
730 die("flow name must be specified");
731
732 if (dladm_parse_flow_props(propstr, &proplist, reset)
733 != DLADM_STATUS_OK)
734 die("invalid flow property specified");
735
736 if (proplist == NULL) {
737 char *errprop;
738
739 if (!reset)
740 die("flow property must be specified");
741
742 status = dladm_set_flowprop(handle, flow, NULL, NULL, 0,
743 DLADM_OPT_ACTIVE, &errprop);
744 if (status != DLADM_STATUS_OK) {
745 warn_dlerr(status, "cannot reset flow property '%s' "
746 "on '%s'", errprop, flow);
747 }
748 if (!temp) {
749 dladm_status_t s;
750
751 s = set_flowprop_persist(flow, NULL, NULL, 0, reset);
752 if (s != DLADM_STATUS_OK)
753 status = s;
754 }
755 goto done;
756 }
757
758 for (i = 0; i < proplist->al_count; i++) {
759 dladm_arg_info_t *aip = &proplist->al_info[i];
760 char **val;
761 uint_t count;
762 dladm_status_t s;
763
764 if (reset) {
765 val = NULL;
766 count = 0;
767 } else {
768 val = aip->ai_val;
769 count = aip->ai_count;
770 if (count == 0) {
771 warn("no value specified for '%s'",
772 aip->ai_name);
773 status = DLADM_STATUS_BADARG;
774 continue;
775 }
776 }
777 s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count,
778 DLADM_OPT_ACTIVE, NULL);
779 if (s == DLADM_STATUS_OK) {
780 if (!temp) {
781 s = set_flowprop_persist(flow,
782 aip->ai_name, val, count, reset);
783 if (s != DLADM_STATUS_OK)
784 status = s;
785 }
786 continue;
787 }
788 status = s;
789 switch (s) {
790 case DLADM_STATUS_NOTFOUND:
791 warn("invalid flow property '%s'", aip->ai_name);
792 break;
793 case DLADM_STATUS_BADVAL: {
794 int j;
795 char *ptr, *lim;
796 char **propvals = NULL;
797 uint_t valcnt = DLADM_MAX_PROP_VALCNT;
798
799 ptr = malloc((sizeof (char *) +
800 DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
801 MAX_PROP_LINE);
802
803 if (ptr == NULL)
804 die("insufficient memory");
805 propvals = (char **)(void *)ptr;
806
807 for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
808 propvals[j] = ptr + sizeof (char *) *
809 DLADM_MAX_PROP_VALCNT +
810 j * DLADM_PROP_VAL_MAX;
811 }
812 s = dladm_get_flowprop(handle, flow,
813 DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals,
814 &valcnt);
815
816 ptr = errmsg;
817 lim = ptr + DLADM_STRSIZE;
818 *ptr = '\0';
819 for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
820 ptr += snprintf(ptr, lim - ptr, "%s,",
821 propvals[j]);
822 if (ptr >= lim)
823 break;
824 }
825 if (ptr > errmsg) {
826 *(ptr - 1) = '\0';
827 warn("flow property '%s' must be one of: %s",
828 aip->ai_name, errmsg);
829 } else
830 warn("%s is an invalid value for "
831 "flow property %s", *val, aip->ai_name);
832 free(propvals);
833 break;
834 }
835 default:
836 if (reset) {
837 warn_dlerr(status, "cannot reset flow property "
838 "'%s' on '%s'", aip->ai_name, flow);
839 } else {
840 warn_dlerr(status, "cannot set flow property "
841 "'%s' on '%s'", aip->ai_name, flow);
842 }
843 break;
844 }
845 }
846 done:
847 dladm_free_props(proplist);
848 if (status != DLADM_STATUS_OK) {
849 dladm_close(handle);
850 exit(EXIT_FAILURE);
851 }
852 }
853
854 static void
do_set_flowprop(int argc,char ** argv)855 do_set_flowprop(int argc, char **argv)
856 {
857 set_flowprop(argc, argv, B_FALSE);
858 }
859
860 static void
do_reset_flowprop(int argc,char ** argv)861 do_reset_flowprop(int argc, char **argv)
862 {
863 set_flowprop(argc, argv, B_TRUE);
864 }
865
866 static void
warn(const char * format,...)867 warn(const char *format, ...)
868 {
869 va_list alist;
870
871 format = gettext(format);
872 (void) fprintf(stderr, "%s: warning: ", progname);
873
874 va_start(alist, format);
875 (void) vfprintf(stderr, format, alist);
876 va_end(alist);
877
878 (void) putc('\n', stderr);
879 }
880
881 /* PRINTFLIKE2 */
882 static void
warn_dlerr(dladm_status_t err,const char * format,...)883 warn_dlerr(dladm_status_t err, const char *format, ...)
884 {
885 va_list alist;
886 char errmsg[DLADM_STRSIZE];
887
888 format = gettext(format);
889 (void) fprintf(stderr, gettext("%s: warning: "), progname);
890
891 va_start(alist, format);
892 (void) vfprintf(stderr, format, alist);
893 va_end(alist);
894 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
895 }
896
897 /* PRINTFLIKE1 */
898 static void
die(const char * format,...)899 die(const char *format, ...)
900 {
901 va_list alist;
902
903 format = gettext(format);
904 (void) fprintf(stderr, "%s: ", progname);
905
906 va_start(alist, format);
907 (void) vfprintf(stderr, format, alist);
908 va_end(alist);
909
910 (void) putc('\n', stderr);
911
912 /* close dladm handle if it was opened */
913 if (handle != NULL)
914 dladm_close(handle);
915
916 exit(EXIT_FAILURE);
917 }
918
919 static void
die_optdup(int opt)920 die_optdup(int opt)
921 {
922 die("the option -%c cannot be specified more than once", opt);
923 }
924
925 static void
die_opterr(int opt,int opterr)926 die_opterr(int opt, int opterr)
927 {
928 switch (opterr) {
929 case ':':
930 die("option '-%c' requires a value", opt);
931 break;
932 case '?':
933 default:
934 die("unrecognized option '-%c'", opt);
935 break;
936 }
937 }
938
939 /* PRINTFLIKE2 */
940 static void
die_dlerr(dladm_status_t err,const char * format,...)941 die_dlerr(dladm_status_t err, const char *format, ...)
942 {
943 va_list alist;
944 char errmsg[DLADM_STRSIZE];
945
946 format = gettext(format);
947 (void) fprintf(stderr, "%s: ", progname);
948
949 va_start(alist, format);
950 (void) vfprintf(stderr, format, alist);
951 va_end(alist);
952 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
953
954 /* close dladm handle if it was opened */
955 if (handle != NULL)
956 dladm_close(handle);
957
958 exit(EXIT_FAILURE);
959 }
960
961 static void
print_flowprop(const char * flowname,show_flowprop_state_t * statep,const char * propname,dladm_prop_type_t type,const char * format,char ** pptr)962 print_flowprop(const char *flowname, show_flowprop_state_t *statep,
963 const char *propname, dladm_prop_type_t type,
964 const char *format, char **pptr)
965 {
966 int i;
967 char *ptr, *lim;
968 char buf[DLADM_STRSIZE];
969 char *unknown = "--", *notsup = "";
970 char **propvals = statep->fs_propvals;
971 uint_t valcnt = DLADM_MAX_PROP_VALCNT;
972 dladm_status_t status;
973
974 status = dladm_get_flowprop(handle, flowname, type, propname, propvals,
975 &valcnt);
976 if (status != DLADM_STATUS_OK) {
977 if (status == DLADM_STATUS_TEMPONLY) {
978 if (type == DLADM_PROP_VAL_MODIFIABLE &&
979 statep->fs_persist) {
980 valcnt = 1;
981 propvals = &unknown;
982 } else {
983 statep->fs_status = status;
984 statep->fs_retstatus = status;
985 return;
986 }
987 } else if (status == DLADM_STATUS_NOTSUP ||
988 statep->fs_persist) {
989 valcnt = 1;
990 if (type == DLADM_PROP_VAL_CURRENT)
991 propvals = &unknown;
992 else
993 propvals = ¬sup;
994 } else {
995 if ((statep->fs_proplist != NULL) &&
996 statep->fs_status == DLADM_STATUS_OK) {
997 warn("invalid flow property '%s'", propname);
998 }
999 statep->fs_status = status;
1000 statep->fs_retstatus = status;
1001 return;
1002 }
1003 }
1004
1005 statep->fs_status = DLADM_STATUS_OK;
1006
1007 ptr = buf;
1008 lim = buf + DLADM_STRSIZE;
1009 for (i = 0; i < valcnt; i++) {
1010 if (propvals[i][0] == '\0' && !statep->fs_parsable)
1011 ptr += snprintf(ptr, lim - ptr, "--,");
1012 else
1013 ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
1014 if (ptr >= lim)
1015 break;
1016 }
1017 if (valcnt > 0)
1018 buf[strlen(buf) - 1] = '\0';
1019
1020 lim = statep->fs_line + MAX_PROP_LINE;
1021 if (statep->fs_parsable) {
1022 *pptr += snprintf(*pptr, lim - *pptr,
1023 "%s", buf);
1024 } else {
1025 *pptr += snprintf(*pptr, lim - *pptr, format, buf);
1026 }
1027 }
1028
1029 static boolean_t
print_flowprop_cb(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)1030 print_flowprop_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1031 {
1032 flowprop_args_t *arg = of_arg->ofmt_cbarg;
1033 char *propname = arg->fs_propname;
1034 show_flowprop_state_t *statep = arg->fs_state;
1035 char *ptr = statep->fs_line;
1036 char *lim = ptr + MAX_PROP_LINE;
1037 char *flowname = arg->fs_flowname;
1038
1039 switch (of_arg->ofmt_id) {
1040 case FLOWPROP_FLOW:
1041 (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow);
1042 break;
1043 case FLOWPROP_PROPERTY:
1044 (void) snprintf(ptr, lim - ptr, "%s", propname);
1045 break;
1046 case FLOWPROP_VALUE:
1047 print_flowprop(flowname, statep, propname,
1048 statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT :
1049 DLADM_PROP_VAL_CURRENT, "%s", &ptr);
1050 /*
1051 * If we failed to query the flow property, for example, query
1052 * the persistent value of a non-persistable flow property,
1053 * simply skip the output.
1054 */
1055 if (statep->fs_status != DLADM_STATUS_OK)
1056 goto skip;
1057 ptr = statep->fs_line;
1058 break;
1059 case FLOWPROP_DEFAULT:
1060 print_flowprop(flowname, statep, propname,
1061 DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
1062 if (statep->fs_status != DLADM_STATUS_OK)
1063 goto skip;
1064 ptr = statep->fs_line;
1065 break;
1066 case FLOWPROP_POSSIBLE:
1067 print_flowprop(flowname, statep, propname,
1068 DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
1069 if (statep->fs_status != DLADM_STATUS_OK)
1070 goto skip;
1071 ptr = statep->fs_line;
1072 break;
1073 default:
1074 die("invalid input");
1075 break;
1076 }
1077 (void) strlcpy(buf, ptr, bufsize);
1078 return (B_TRUE);
1079 skip:
1080 buf[0] = '\0';
1081 return ((statep->fs_status == DLADM_STATUS_OK) ?
1082 B_TRUE : B_FALSE);
1083 }
1084
1085 static int
show_one_flowprop(void * arg,const char * propname)1086 show_one_flowprop(void *arg, const char *propname)
1087 {
1088 show_flowprop_state_t *statep = arg;
1089 flowprop_args_t fs_arg;
1090
1091 bzero(&fs_arg, sizeof (fs_arg));
1092 fs_arg.fs_state = statep;
1093 fs_arg.fs_propname = (char *)propname;
1094 fs_arg.fs_flowname = (char *)statep->fs_flow;
1095
1096 ofmt_print(statep->fs_ofmt, (void *)&fs_arg);
1097
1098 return (DLADM_WALK_CONTINUE);
1099 }
1100
1101 /*ARGSUSED*/
1102 /* Walker function called by dladm_walk_flow to display flow properties */
1103 static int
show_flowprop(dladm_handle_t handle,dladm_flow_attr_t * attr,void * arg)1104 show_flowprop(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
1105 {
1106 show_flowprop_one_flow(arg, attr->fa_flowname);
1107 return (DLADM_WALK_CONTINUE);
1108 }
1109
1110 /*
1111 * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it
1112 * usable to dladm_walk_datalink_id()
1113 */
1114 static int
show_flowprop_onelink(dladm_handle_t dh,datalink_id_t linkid,void * arg)1115 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1116 {
1117 char name[MAXLINKNAMELEN];
1118
1119 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name,
1120 sizeof (name)) != DLADM_STATUS_OK)
1121 return (DLADM_WALK_TERMINATE);
1122
1123 (void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE);
1124
1125 return (DLADM_WALK_CONTINUE);
1126 }
1127
1128 static void
do_show_flowprop(int argc,char ** argv)1129 do_show_flowprop(int argc, char **argv)
1130 {
1131 int option;
1132 dladm_arg_list_t *proplist = NULL;
1133 show_flowprop_state_t state;
1134 char *fields_str = NULL;
1135 ofmt_handle_t ofmt;
1136 ofmt_status_t oferr;
1137 uint_t ofmtflags = 0;
1138
1139 opterr = 0;
1140 state.fs_propvals = NULL;
1141 state.fs_line = NULL;
1142 state.fs_parsable = B_FALSE;
1143 state.fs_persist = B_FALSE;
1144 state.fs_header = B_TRUE;
1145 state.fs_retstatus = DLADM_STATUS_OK;
1146 state.fs_linkid = DATALINK_INVALID_LINKID;
1147 state.fs_flow = NULL;
1148
1149 while ((option = getopt_long(argc, argv, ":p:cPl:o:",
1150 prop_longopts, NULL)) != -1) {
1151 switch (option) {
1152 case 'p':
1153 if (dladm_parse_flow_props(optarg, &proplist, B_TRUE)
1154 != DLADM_STATUS_OK)
1155 die("invalid flow properties specified");
1156 break;
1157 case 'c':
1158 state.fs_parsable = B_TRUE;
1159 ofmtflags |= OFMT_PARSABLE;
1160 break;
1161 case 'P':
1162 state.fs_persist = B_TRUE;
1163 break;
1164 case 'l':
1165 if (dladm_name2info(handle, optarg, &state.fs_linkid,
1166 NULL, NULL, NULL) != DLADM_STATUS_OK)
1167 die("invalid link '%s'", optarg);
1168 break;
1169 case 'o':
1170 fields_str = optarg;
1171 break;
1172 default:
1173 die_opterr(optopt, option);
1174 break;
1175 }
1176 }
1177
1178 if (optind == (argc - 1)) {
1179 if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1180 die("flow name too long");
1181 state.fs_flow = argv[optind];
1182 } else if (optind != argc) {
1183 usage();
1184 }
1185 state.fs_proplist = proplist;
1186 state.fs_status = DLADM_STATUS_OK;
1187
1188 oferr = ofmt_open(fields_str, flowprop_fields, ofmtflags, 0, &ofmt);
1189 flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
1190 state.fs_ofmt = ofmt;
1191
1192 /* Show properties for one flow */
1193 if (state.fs_flow != NULL) {
1194 show_flowprop_one_flow(&state, state.fs_flow);
1195
1196 /* Show properties for all flows on one link */
1197 } else if (state.fs_linkid != DATALINK_INVALID_LINKID) {
1198 (void) show_flowprop_onelink(handle, state.fs_linkid, &state);
1199
1200 /* Show properties for all flows on all links */
1201 } else {
1202 (void) dladm_walk_datalink_id(show_flowprop_onelink, handle,
1203 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1204 DLADM_OPT_ACTIVE);
1205 }
1206
1207 dladm_free_props(proplist);
1208 ofmt_close(ofmt);
1209 }
1210
1211 static void
show_flowprop_one_flow(void * arg,const char * flow)1212 show_flowprop_one_flow(void *arg, const char *flow)
1213 {
1214 int i;
1215 char *buf;
1216 dladm_status_t status;
1217 dladm_arg_list_t *proplist = NULL;
1218 show_flowprop_state_t *statep = arg;
1219 dladm_flow_attr_t attr;
1220 const char *savep;
1221
1222 /*
1223 * Do not print flow props for invalid flows.
1224 */
1225 if ((status = dladm_flow_info(handle, flow, &attr)) !=
1226 DLADM_STATUS_OK) {
1227 die("invalid flow: '%s'", flow);
1228 }
1229
1230 savep = statep->fs_flow;
1231 statep->fs_flow = flow;
1232
1233 proplist = statep->fs_proplist;
1234
1235 buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX)
1236 * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
1237 if (buf == NULL)
1238 die("insufficient memory");
1239
1240 statep->fs_propvals = (char **)(void *)buf;
1241 for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
1242 statep->fs_propvals[i] = buf +
1243 sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1244 i * DLADM_PROP_VAL_MAX;
1245 }
1246 statep->fs_line = buf +
1247 (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
1248
1249 /* show only specified flow properties */
1250 if (proplist != NULL) {
1251 for (i = 0; i < proplist->al_count; i++) {
1252 if (show_one_flowprop(statep,
1253 proplist->al_info[i].ai_name) != DLADM_STATUS_OK)
1254 break;
1255 }
1256
1257 /* show all flow properties */
1258 } else {
1259 status = dladm_walk_flowprop(show_one_flowprop, flow, statep);
1260 if (status != DLADM_STATUS_OK)
1261 die_dlerr(status, "show-flowprop");
1262 }
1263 free(buf);
1264 statep->fs_flow = savep;
1265 }
1266
1267 /*
1268 * default output callback function that, when invoked from dladm_print_output,
1269 * prints string which is offset by of_arg->ofmt_id within buf.
1270 */
1271 static boolean_t
print_default_cb(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)1272 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1273 {
1274 char *value;
1275
1276 value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1277 (void) strlcpy(buf, value, bufsize);
1278 return (B_TRUE);
1279 }
1280
1281 static void
flowadm_ofmt_check(ofmt_status_t oferr,boolean_t parsable,ofmt_handle_t ofmt)1282 flowadm_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
1283 ofmt_handle_t ofmt)
1284 {
1285 char buf[OFMT_BUFSIZE];
1286
1287 if (oferr == OFMT_SUCCESS)
1288 return;
1289 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
1290 /*
1291 * All errors are considered fatal in parsable mode.
1292 * NOMEM errors are always fatal, regardless of mode.
1293 * For other errors, we print diagnostics in human-readable
1294 * mode and processs what we can.
1295 */
1296 if (parsable || oferr == OFMT_ENOFIELDS) {
1297 ofmt_close(ofmt);
1298 die(buf);
1299 } else {
1300 warn(buf);
1301 }
1302 }
1303