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 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 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 * 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 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 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 char 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 411 do_remove_flow(int argc, char *argv[]) 412 { 413 char 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 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 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 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 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 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 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 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 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 858 do_set_flowprop(int argc, char **argv) 859 { 860 set_flowprop(argc, argv, B_FALSE); 861 } 862 863 static void 864 do_reset_flowprop(int argc, char **argv) 865 { 866 set_flowprop(argc, argv, B_TRUE); 867 } 868 869 static void 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 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 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 923 die_optdup(int opt) 924 { 925 die("the option -%c cannot be specified more than once", opt); 926 } 927 928 static void 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 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 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 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 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 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 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 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 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 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