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