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