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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 855 do_set_flowprop(int argc, char **argv) 856 { 857 set_flowprop(argc, argv, B_FALSE); 858 } 859 860 static void 861 do_reset_flowprop(int argc, char **argv) 862 { 863 set_flowprop(argc, argv, B_TRUE); 864 } 865 866 static void 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 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 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 920 die_optdup(int opt) 921 { 922 die("the option -%c cannot be specified more than once", opt); 923 } 924 925 static void 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 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 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 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 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 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 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 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 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 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 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