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