/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct show_usage_state_s { boolean_t us_plot; boolean_t us_parsable; boolean_t us_printheader; boolean_t us_first; boolean_t us_showall; ofmt_handle_t us_ofmt; } show_usage_state_t; typedef struct show_flow_state { boolean_t fs_firstonly; boolean_t fs_donefirst; pktsum_t fs_prevstats; uint32_t fs_flags; dladm_status_t fs_status; ofmt_handle_t fs_ofmt; const char *fs_flow; const char *fs_link; boolean_t fs_parsable; boolean_t fs_persist; boolean_t fs_stats; uint64_t fs_mask; } show_flow_state_t; typedef void cmdfunc_t(int, char **); static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow; static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop; static cmdfunc_t do_show_usage; static int show_flow(dladm_handle_t, dladm_flow_attr_t *, void *); static int show_flows_onelink(dladm_handle_t, datalink_id_t, void *); static void flow_stats(const char *, datalink_id_t, uint_t, char *, show_flow_state_t *); static void get_flow_stats(const char *, pktsum_t *); static int show_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *); static int show_link_flow_stats(dladm_handle_t, datalink_id_t, void *); static int remove_flow(dladm_handle_t, dladm_flow_attr_t *, void *); static int show_flowprop(dladm_handle_t, dladm_flow_attr_t *, void *); static void show_flowprop_one_flow(void *, const char *); static int show_flowprop_onelink(dladm_handle_t, datalink_id_t, void *); static void die(const char *, ...); static void die_optdup(int); static void die_opterr(int, int); static void die_dlerr(dladm_status_t, const char *, ...); static void warn(const char *, ...); static void warn_dlerr(dladm_status_t, const char *, ...); /* callback functions for printing output */ static ofmt_cb_t print_flowprop_cb, print_default_cb, print_flow_stats_cb; static void flowadm_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t); typedef struct cmd { char *c_name; void (*c_fn)(int, char **); } cmd_t; static cmd_t cmds[] = { { "add-flow", do_add_flow }, { "remove-flow", do_remove_flow }, { "show-flowprop", do_show_flowprop }, { "set-flowprop", do_set_flowprop }, { "reset-flowprop", do_reset_flowprop }, { "show-flow", do_show_flow }, { "init-flow", do_init_flow }, { "show-usage", do_show_usage } }; static const struct option longopts[] = { {"link", required_argument, 0, 'l'}, {"parsable", no_argument, 0, 'p'}, {"parseable", no_argument, 0, 'p'}, {"statistics", no_argument, 0, 's'}, {"interval", required_argument, 0, 'i'}, {"temporary", no_argument, 0, 't'}, {"root-dir", required_argument, 0, 'R'}, { 0, 0, 0, 0 } }; static const struct option prop_longopts[] = { {"link", required_argument, 0, 'l'}, {"temporary", no_argument, 0, 't'}, {"root-dir", required_argument, 0, 'R'}, {"prop", required_argument, 0, 'p'}, {"attr", required_argument, 0, 'a'}, { 0, 0, 0, 0 } }; /* * structures for 'flowadm remove-flow' */ typedef struct remove_flow_state { boolean_t fs_tempop; const char *fs_altroot; dladm_status_t fs_status; } remove_flow_state_t; #define PROTO_MAXSTR_LEN 7 #define PORT_MAXSTR_LEN 6 #define DSFIELD_MAXSTR_LEN 10 #define NULL_OFMT {NULL, 0, 0, NULL} typedef struct flow_fields_buf_s { char flow_name[MAXFLOWNAMELEN]; char flow_link[MAXLINKNAMELEN]; char flow_ipaddr[INET6_ADDRSTRLEN+4]; char flow_proto[PROTO_MAXSTR_LEN]; char flow_port[PORT_MAXSTR_LEN]; char flow_dsfield[DSFIELD_MAXSTR_LEN]; } flow_fields_buf_t; static ofmt_field_t flow_fields[] = { /* name, field width, index */ { "FLOW", 12, offsetof(flow_fields_buf_t, flow_name), print_default_cb}, { "LINK", 12, offsetof(flow_fields_buf_t, flow_link), print_default_cb}, { "IPADDR", 31, offsetof(flow_fields_buf_t, flow_ipaddr), print_default_cb}, { "PROTO", 7, offsetof(flow_fields_buf_t, flow_proto), print_default_cb}, { "PORT", 8, offsetof(flow_fields_buf_t, flow_port), print_default_cb}, { "DSFLD", 10, offsetof(flow_fields_buf_t, flow_dsfield), print_default_cb}, NULL_OFMT} ; /* * structures for 'flowadm show-flowprop' */ typedef enum { FLOWPROP_FLOW, FLOWPROP_PROPERTY, FLOWPROP_VALUE, FLOWPROP_DEFAULT, FLOWPROP_POSSIBLE } flowprop_field_index_t; static ofmt_field_t flowprop_fields[] = { /* name, fieldwidth, index, callback */ { "FLOW", 13, FLOWPROP_FLOW, print_flowprop_cb}, { "PROPERTY", 16, FLOWPROP_PROPERTY, print_flowprop_cb}, { "VALUE", 15, FLOWPROP_VALUE, print_flowprop_cb}, { "DEFAULT", 15, FLOWPROP_DEFAULT, print_flowprop_cb}, { "POSSIBLE", 21, FLOWPROP_POSSIBLE, print_flowprop_cb}, NULL_OFMT} ; #define MAX_PROP_LINE 512 typedef struct show_flowprop_state { const char *fs_flow; datalink_id_t fs_linkid; char *fs_line; char **fs_propvals; dladm_arg_list_t *fs_proplist; boolean_t fs_parsable; boolean_t fs_persist; boolean_t fs_header; dladm_status_t fs_status; dladm_status_t fs_retstatus; ofmt_handle_t fs_ofmt; } show_flowprop_state_t; typedef struct set_flowprop_state { const char *fs_name; boolean_t fs_reset; boolean_t fs_temp; dladm_status_t fs_status; } set_flowprop_state_t; typedef struct flowprop_args_s { show_flowprop_state_t *fs_state; char *fs_propname; char *fs_flowname; } flowprop_args_t; /* * structures for 'flowadm show-flow -s' (print statistics) */ typedef enum { FLOW_S_FLOW, FLOW_S_IPKTS, FLOW_S_RBYTES, FLOW_S_IERRORS, FLOW_S_OPKTS, FLOW_S_OBYTES, FLOW_S_OERRORS } flow_s_field_index_t; static ofmt_field_t flow_s_fields[] = { /* name, field width, index, callback */ { "FLOW", 15, FLOW_S_FLOW, print_flow_stats_cb}, { "IPACKETS", 10, FLOW_S_IPKTS, print_flow_stats_cb}, { "RBYTES", 8, FLOW_S_RBYTES, print_flow_stats_cb}, { "IERRORS", 10, FLOW_S_IERRORS, print_flow_stats_cb}, { "OPACKETS", 12, FLOW_S_OPKTS, print_flow_stats_cb}, { "OBYTES", 12, FLOW_S_OBYTES, print_flow_stats_cb}, { "OERRORS", 8, FLOW_S_OERRORS, print_flow_stats_cb}, NULL_OFMT} ; typedef struct flow_args_s { char *flow_s_flow; pktsum_t *flow_s_psum; } flow_args_t; /* * structures for 'flowadm show-usage' */ typedef struct usage_fields_buf_s { char usage_flow[12]; char usage_duration[10]; char usage_ipackets[9]; char usage_rbytes[10]; char usage_opackets[9]; char usage_obytes[10]; char usage_bandwidth[14]; } usage_fields_buf_t; static ofmt_field_t usage_fields[] = { /* name, field width, offset */ { "FLOW", 13, offsetof(usage_fields_buf_t, usage_flow), print_default_cb}, { "DURATION", 11, offsetof(usage_fields_buf_t, usage_duration), print_default_cb}, { "IPACKETS", 10, offsetof(usage_fields_buf_t, usage_ipackets), print_default_cb}, { "RBYTES", 11, offsetof(usage_fields_buf_t, usage_rbytes), print_default_cb}, { "OPACKETS", 10, offsetof(usage_fields_buf_t, usage_opackets), print_default_cb}, { "OBYTES", 11, offsetof(usage_fields_buf_t, usage_obytes), print_default_cb}, { "BANDWIDTH", 15, offsetof(usage_fields_buf_t, usage_bandwidth), print_default_cb}, NULL_OFMT} ; /* * structures for 'dladm show-usage link' */ typedef struct usage_l_fields_buf_s { char usage_l_flow[12]; char usage_l_stime[13]; char usage_l_etime[13]; char usage_l_rbytes[8]; char usage_l_obytes[8]; char usage_l_bandwidth[14]; } usage_l_fields_buf_t; static ofmt_field_t usage_l_fields[] = { /* name, field width, offset */ { "FLOW", 13, offsetof(usage_l_fields_buf_t, usage_l_flow), print_default_cb}, { "START", 14, offsetof(usage_l_fields_buf_t, usage_l_stime), print_default_cb}, { "END", 14, offsetof(usage_l_fields_buf_t, usage_l_etime), print_default_cb}, { "RBYTES", 9, offsetof(usage_l_fields_buf_t, usage_l_rbytes), print_default_cb}, { "OBYTES", 9, offsetof(usage_l_fields_buf_t, usage_l_obytes), print_default_cb}, { "BANDWIDTH", 15, offsetof(usage_l_fields_buf_t, usage_l_bandwidth), print_default_cb}, NULL_OFMT} ; #define PRI_HI 100 #define PRI_LO 10 #define PRI_NORM 50 #define FLOWADM_CONF "/etc/dladm/flowadm.conf" #define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n')) static char *progname; boolean_t t_arg = B_FALSE; /* changes are persistent */ char *altroot = NULL; /* * Handle to libdladm. Opened in main() before the sub-command * specific function is called. */ static dladm_handle_t handle = NULL; static const char *attr_table[] = {"local_ip", "remote_ip", "transport", "local_port", "dsfield"}; #define NATTR (sizeof (attr_table)/sizeof (char *)) static void usage(void) { (void) fprintf(stderr, gettext("usage: flowadm " " ...\n" " add-flow [-t] -l -a =[,...]\n" "\t\t [-p =,...] \n" " remove-flow [-t] {-l | }\n" " show-flow [-p] [-s [-i ]] [-l ] " "[]\n\n" " set-flowprop [-t] -p =[,...] \n" " reset-flowprop [-t] [-p ,...] \n" " show-flowprop [-cP] [-l ] [-p ,...] " "[]\n\n" " show-usage [-a] [-d | -F ] " "[-s
]\n" "\t\t [-e
] -f []\n")); /* close dladm handle if it was opened */ if (handle != NULL) dladm_close(handle); exit(1); } int main(int argc, char *argv[]) { int i, arglen, cmdlen; cmd_t *cmdp; dladm_status_t status; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); progname = argv[0]; if (argc < 2) usage(); for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { cmdp = &cmds[i]; arglen = strlen(argv[1]); cmdlen = strlen(cmdp->c_name); if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name, cmdlen) == 0)) { /* Open the libdladm handle */ if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) { die_dlerr(status, "could not open /dev/dld"); } cmdp->c_fn(argc - 1, &argv[1]); dladm_close(handle); exit(EXIT_SUCCESS); } } (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"), progname, argv[1]); usage(); return (0); } static const char * match_attr(char *attr) { int i; for (i = 0; i < NATTR; i++) { if (strlen(attr) == strlen(attr_table[i]) && strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) { return (attr); } } return (NULL); } /* ARGSUSED */ static void do_init_flow(int argc, char *argv[]) { dladm_status_t status; status = dladm_flow_init(handle); if (status != DLADM_STATUS_OK) die_dlerr(status, "flows initialization failed"); } /* ARGSUSED */ static int show_usage_date(dladm_usage_t *usage, void *arg) { show_usage_state_t *state = (show_usage_state_t *)arg; time_t stime; char timebuf[20]; dladm_flow_attr_t attr; dladm_status_t status; /* * Only show usage information for existing flows unless '-a' * is specified. */ if (!state->us_showall && ((status = dladm_flow_info(handle, usage->du_name, &attr)) != DLADM_STATUS_OK)) { return (status); } stime = usage->du_stime; (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y", localtime(&stime)); (void) printf("%s\n", timebuf); return (DLADM_STATUS_OK); } static int show_usage_time(dladm_usage_t *usage, void *arg) { show_usage_state_t *state = (show_usage_state_t *)arg; char buf[DLADM_STRSIZE]; usage_l_fields_buf_t ubuf; time_t time; double bw; dladm_flow_attr_t attr; dladm_status_t status; /* * Only show usage information for existing flows unless '-a' * is specified. */ if (!state->us_showall && ((status = dladm_flow_info(handle, usage->du_name, &attr)) != DLADM_STATUS_OK)) { return (status); } if (state->us_plot) { if (!state->us_printheader) { if (state->us_first) { (void) printf("# Time"); state->us_first = B_FALSE; } (void) printf(" %s", usage->du_name); if (usage->du_last) { (void) printf("\n"); state->us_first = B_TRUE; state->us_printheader = B_TRUE; } } else { if (state->us_first) { time = usage->du_etime; (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); state->us_first = B_FALSE; (void) printf("%s", buf); } bw = (double)usage->du_bandwidth/1000; (void) printf(" %.2f", bw); if (usage->du_last) { (void) printf("\n"); state->us_first = B_TRUE; } } return (DLADM_STATUS_OK); } bzero(&ubuf, sizeof (ubuf)); (void) snprintf(ubuf.usage_l_flow, sizeof (ubuf.usage_l_flow), "%s", usage->du_name); time = usage->du_stime; (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); (void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s", buf); time = usage->du_etime; (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); (void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s", buf); (void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes), "%llu", usage->du_rbytes); (void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes), "%llu", usage->du_obytes); (void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth), "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); ofmt_print(state->us_ofmt, (void *)&ubuf); return (DLADM_STATUS_OK); } static int show_usage_res(dladm_usage_t *usage, void *arg) { show_usage_state_t *state = (show_usage_state_t *)arg; char buf[DLADM_STRSIZE]; usage_fields_buf_t ubuf; dladm_flow_attr_t attr; dladm_status_t status; /* * Only show usage information for existing flows unless '-a' * is specified. */ if (!state->us_showall && ((status = dladm_flow_info(handle, usage->du_name, &attr)) != DLADM_STATUS_OK)) { return (status); } bzero(&ubuf, sizeof (ubuf)); (void) snprintf(ubuf.usage_flow, sizeof (ubuf.usage_flow), "%s", usage->du_name); (void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration), "%llu", usage->du_duration); (void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets), "%llu", usage->du_ipackets); (void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes), "%llu", usage->du_rbytes); (void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets), "%llu", usage->du_opackets); (void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes), "%llu", usage->du_obytes); (void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth), "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); ofmt_print(state->us_ofmt, (void *)&ubuf); return (DLADM_STATUS_OK); } static boolean_t valid_formatspec(char *formatspec_str) { if (strcmp(formatspec_str, "gnuplot") == 0) return (B_TRUE); return (B_FALSE); } /* ARGSUSED */ static void do_show_usage(int argc, char *argv[]) { char *file = NULL; int opt; dladm_status_t status; boolean_t d_arg = B_FALSE; char *stime = NULL; char *etime = NULL; char *resource = NULL; show_usage_state_t state; boolean_t o_arg = B_FALSE; boolean_t F_arg = B_FALSE; char *fields_str = NULL; char *formatspec_str = NULL; char *all_fields = "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth"; char *all_l_fields = "flow,start,end,rbytes,obytes,bandwidth"; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; bzero(&state, sizeof (show_usage_state_t)); state.us_parsable = B_FALSE; state.us_printheader = B_FALSE; state.us_plot = B_FALSE; state.us_first = B_TRUE; while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) { switch (opt) { case 'd': d_arg = B_TRUE; break; case 'a': state.us_showall = B_TRUE; break; case 'f': file = optarg; break; case 's': stime = optarg; break; case 'e': etime = optarg; break; case 'o': o_arg = B_TRUE; fields_str = optarg; break; case 'F': state.us_plot = F_arg = B_TRUE; formatspec_str = optarg; break; default: die_opterr(optopt, opt); } } if (file == NULL) die("show-usage requires a file"); if (optind == (argc-1)) { dladm_flow_attr_t attr; if (!state.us_showall && dladm_flow_info(handle, resource, &attr) != DLADM_STATUS_OK) { die("invalid flow: '%s'", resource); } resource = argv[optind]; } if (state.us_parsable) ofmtflags |= OFMT_PARSABLE; if (resource == NULL && stime == NULL && etime == NULL) { if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) fields_str = all_fields; oferr = ofmt_open(fields_str, usage_fields, ofmtflags, 0, &ofmt); } else { if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) fields_str = all_l_fields; oferr = ofmt_open(fields_str, usage_l_fields, ofmtflags, 0, &ofmt); } flowadm_ofmt_check(oferr, state.us_parsable, ofmt); state.us_ofmt = ofmt; if (F_arg && d_arg) die("incompatible -d and -F options"); if (F_arg && valid_formatspec(formatspec_str) == B_FALSE) die("Format specifier %s not supported", formatspec_str); if (d_arg) { /* Print log dates */ status = dladm_usage_dates(show_usage_date, DLADM_LOGTYPE_FLOW, file, resource, &state); } else if (resource == NULL && stime == NULL && etime == NULL && !F_arg) { /* Print summary */ status = dladm_usage_summary(show_usage_res, DLADM_LOGTYPE_FLOW, file, &state); } else if (resource != NULL) { /* Print log entries for named resource */ status = dladm_walk_usage_res(show_usage_time, DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state); } else { /* Print time and information for each link */ status = dladm_walk_usage_time(show_usage_time, DLADM_LOGTYPE_FLOW, file, stime, etime, &state); } ofmt_close(ofmt); if (status != DLADM_STATUS_OK) die_dlerr(status, "show-usage"); } static void do_add_flow(int argc, char *argv[]) { char devname[MAXLINKNAMELEN]; char *name = NULL; uint_t index; datalink_id_t linkid; char option; boolean_t l_arg = B_FALSE; char propstr[DLADM_STRSIZE]; char attrstr[DLADM_STRSIZE]; dladm_arg_list_t *proplist = NULL; dladm_arg_list_t *attrlist = NULL; dladm_status_t status; bzero(propstr, DLADM_STRSIZE); bzero(attrstr, DLADM_STRSIZE); while ((option = getopt_long(argc, argv, "tR:l:a:p:", prop_longopts, NULL)) != -1) { switch (option) { case 't': t_arg = B_TRUE; break; case 'R': altroot = optarg; break; case 'l': if (strlcpy(devname, optarg, MAXLINKNAMELEN) >= MAXLINKNAMELEN) { die("link name too long"); } if (dladm_name2info(handle, devname, &linkid, NULL, NULL, NULL) != DLADM_STATUS_OK) die("invalid link '%s'", devname); l_arg = B_TRUE; break; case 'a': (void) strlcat(attrstr, optarg, DLADM_STRSIZE); if (strlcat(attrstr, ",", DLADM_STRSIZE) >= DLADM_STRSIZE) die("attribute list too long '%s'", attrstr); break; case 'p': (void) strlcat(propstr, optarg, DLADM_STRSIZE); if (strlcat(propstr, ",", DLADM_STRSIZE) >= DLADM_STRSIZE) die("property list too long '%s'", propstr); break; default: die_opterr(optopt, option); } } if (!l_arg) { die("link is required"); } opterr = 0; index = optind; if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) { die("flow name is required"); } else { /* get flow name; required last argument */ if (strlen(argv[index]) >= MAXFLOWNAMELEN) die("flow name too long"); name = argv[index]; } if (dladm_parse_flow_attrs(attrstr, &attrlist, B_FALSE) != DLADM_STATUS_OK) die("invalid flow attribute specified"); if (dladm_parse_flow_props(propstr, &proplist, B_FALSE) != DLADM_STATUS_OK) die("invalid flow property specified"); status = dladm_flow_add(handle, linkid, attrlist, proplist, name, t_arg, altroot); if (status != DLADM_STATUS_OK) die_dlerr(status, "add flow failed"); dladm_free_attrs(attrlist); dladm_free_props(proplist); } static void do_remove_flow(int argc, char *argv[]) { char option; char *flowname = NULL; char linkname[MAXLINKNAMELEN]; datalink_id_t linkid = DATALINK_ALL_LINKID; boolean_t l_arg = B_FALSE; remove_flow_state_t state; dladm_status_t status; bzero(&state, sizeof (state)); opterr = 0; while ((option = getopt_long(argc, argv, ":tR:l:", longopts, NULL)) != -1) { switch (option) { case 't': t_arg = B_TRUE; break; case 'R': altroot = optarg; break; case 'l': if (strlcpy(linkname, optarg, MAXLINKNAMELEN) >= MAXLINKNAMELEN) { die("link name too long"); } if (dladm_name2info(handle, linkname, &linkid, NULL, NULL, NULL) != DLADM_STATUS_OK) { die("invalid link '%s'", linkname); } l_arg = B_TRUE; break; default: die_opterr(optopt, option); break; } } /* when link not specified get flow name */ if (!l_arg) { if (optind != (argc-1)) { usage(); } else { if (strlen(argv[optind]) >= MAXFLOWNAMELEN) die("flow name too long"); flowname = argv[optind]; } status = dladm_flow_remove(handle, flowname, t_arg, altroot); } else { /* if link is specified then flow name should not be there */ if (optind == argc-1) usage(); /* walk the link to find flows and remove them */ state.fs_tempop = t_arg; state.fs_altroot = altroot; state.fs_status = DLADM_STATUS_OK; status = dladm_walk_flow(remove_flow, handle, linkid, &state, B_FALSE); /* * check if dladm_walk_flow terminated early and see if the * walker function as any status for us */ if (status == DLADM_STATUS_OK) status = state.fs_status; } if (status != DLADM_STATUS_OK) die_dlerr(status, "remove flow failed"); } /* * Walker function for removing a flow through dladm_walk_flow(); */ /*ARGSUSED*/ static int remove_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) { remove_flow_state_t *state = (remove_flow_state_t *)arg; state->fs_status = dladm_flow_remove(handle, attr->fa_flowname, state->fs_tempop, state->fs_altroot); if (state->fs_status == DLADM_STATUS_OK) return (DLADM_WALK_CONTINUE); else return (DLADM_WALK_TERMINATE); } /*ARGSUSED*/ static dladm_status_t print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr, flow_fields_buf_t *fbuf) { char link[MAXLINKNAMELEN]; dladm_status_t status; if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL, NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) { return (status); } (void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name), "%s", attr->fa_flowname); (void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link), "%s", link); (void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr, sizeof (fbuf->flow_ipaddr)); (void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto, sizeof (fbuf->flow_proto)); (void) dladm_flow_attr_port2str(attr, fbuf->flow_port, sizeof (fbuf->flow_port)); (void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield, sizeof (fbuf->flow_dsfield)); return (DLADM_STATUS_OK); } /* * Walker function for showing flow attributes through dladm_walk_flow(). */ /*ARGSUSED*/ static int show_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) { show_flow_state_t *statep = arg; dladm_status_t status; flow_fields_buf_t fbuf; /* * first get all the flow attributes into fbuf; */ bzero(&fbuf, sizeof (fbuf)); status = print_flow(statep, attr, &fbuf); if (status != DLADM_STATUS_OK) goto done; ofmt_print(statep->fs_ofmt, (void *)&fbuf); done: statep->fs_status = status; return (DLADM_WALK_CONTINUE); } static void show_one_flow(void *arg, const char *name) { dladm_flow_attr_t attr; if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK) die("invalid flow: '%s'", name); else (void) show_flow(handle, &attr, arg); } /* * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to * dladm_walk_datalink_id(). Used for showing flow attributes for * all flows on all links. */ static int show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) { show_flow_state_t *state = arg; (void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist); return (DLADM_WALK_CONTINUE); } static void get_flow_stats(const char *flowname, pktsum_t *stats) { kstat_ctl_t *kcp; kstat_t *ksp; bzero(stats, sizeof (*stats)); if ((kcp = kstat_open()) == NULL) { warn("kstat open operation failed"); return; } ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow"); if (ksp != NULL) dladm_get_stats(kcp, ksp, stats); (void) kstat_close(kcp); } static boolean_t print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { flow_args_t *fargs = of_arg->ofmt_cbarg; pktsum_t *diff_stats = fargs->flow_s_psum; switch (of_arg->ofmt_id) { case FLOW_S_FLOW: (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow); break; case FLOW_S_IPKTS: (void) snprintf(buf, bufsize, "%llu", diff_stats->ipackets); break; case FLOW_S_RBYTES: (void) snprintf(buf, bufsize, "%llu", diff_stats->rbytes); break; case FLOW_S_IERRORS: (void) snprintf(buf, bufsize, "%u", diff_stats->ierrors); break; case FLOW_S_OPKTS: (void) snprintf(buf, bufsize, "%llu", diff_stats->opackets); break; case FLOW_S_OBYTES: (void) snprintf(buf, bufsize, "%llu", diff_stats->obytes); break; case FLOW_S_OERRORS: (void) snprintf(buf, bufsize, "%u", diff_stats->oerrors); break; default: die("invalid input"); break; } return (B_TRUE); } /* ARGSUSED */ static int show_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) { show_flow_state_t *state = (show_flow_state_t *)arg; char *name = attr->fa_flowname; pktsum_t stats, diff_stats; flow_args_t fargs; if (state->fs_firstonly) { if (state->fs_donefirst) return (DLADM_WALK_TERMINATE); state->fs_donefirst = B_TRUE; } else { bzero(&state->fs_prevstats, sizeof (state->fs_prevstats)); } get_flow_stats(name, &stats); dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats); fargs.flow_s_flow = name; fargs.flow_s_psum = &diff_stats; ofmt_print(state->fs_ofmt, (void *)&fargs); state->fs_prevstats = stats; return (DLADM_WALK_CONTINUE); } /* * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for * dladm_walk_datalink_id(). Used for showing flow stats for * all flows on all links. */ static int show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) { if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE) == DLADM_STATUS_OK) return (DLADM_WALK_CONTINUE); else return (DLADM_WALK_TERMINATE); } /* ARGSUSED */ static void flow_stats(const char *flow, datalink_id_t linkid, uint_t interval, char *fields_str, show_flow_state_t *state) { dladm_flow_attr_t attr; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt); flowadm_ofmt_check(oferr, state->fs_parsable, ofmt); state->fs_ofmt = ofmt; if (flow != NULL && dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK) die("invalid flow %s", flow); /* * If an interval is specified, continuously show the stats * for only the first flow. */ state->fs_firstonly = (interval != 0); for (;;) { state->fs_donefirst = B_FALSE; /* Show stats for named flow */ if (flow != NULL) { state->fs_flow = flow; (void) show_flow_stats(handle, &attr, state); /* Show all stats on a link */ } else if (linkid != DATALINK_INVALID_LINKID) { (void) dladm_walk_flow(show_flow_stats, handle, linkid, state, B_FALSE); /* Show all stats by datalink */ } else { (void) dladm_walk_datalink_id(show_link_flow_stats, handle, state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); } if (interval == 0) break; (void) fflush(stdout); (void) sleep(interval); } ofmt_close(ofmt); } static void do_show_flow(int argc, char *argv[]) { char flowname[MAXFLOWNAMELEN]; char linkname[MAXLINKNAMELEN]; datalink_id_t linkid = DATALINK_ALL_LINKID; int option; boolean_t s_arg = B_FALSE; boolean_t S_arg = B_FALSE; boolean_t i_arg = B_FALSE; boolean_t l_arg = B_FALSE; boolean_t o_arg = B_FALSE; uint32_t interval = 0; show_flow_state_t state; char *fields_str = NULL; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; bzero(&state, sizeof (state)); opterr = 0; while ((option = getopt_long(argc, argv, ":pPsSi:l:o:", longopts, NULL)) != -1) { switch (option) { case 'p': state.fs_parsable = B_TRUE; ofmtflags |= OFMT_PARSABLE; break; case 'P': state.fs_persist = B_TRUE; break; case 's': if (s_arg) die_optdup(option); s_arg = B_TRUE; break; case 'S': if (S_arg) die_optdup(option); S_arg = B_TRUE; break; case 'o': if (o_arg) die_optdup(option); o_arg = B_TRUE; fields_str = optarg; break; case 'i': if (i_arg) die_optdup(option); i_arg = B_TRUE; if (!dladm_str2interval(optarg, &interval)) die("invalid interval value '%s'", optarg); break; case 'l': if (strlcpy(linkname, optarg, MAXLINKNAMELEN) >= MAXLINKNAMELEN) die("link name too long\n"); if (dladm_name2info(handle, linkname, &linkid, NULL, NULL, NULL) != DLADM_STATUS_OK) die("invalid link '%s'", linkname); l_arg = B_TRUE; break; default: die_opterr(optopt, option); break; } } if (i_arg && !(s_arg || S_arg)) die("the -i option can be used only with -s or -S"); if (s_arg && S_arg) die("the -s option cannot be used with -S"); /* get flow name (optional last argument */ if (optind == (argc-1)) { if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) >= MAXFLOWNAMELEN) die("flow name too long"); state.fs_flow = flowname; } if (S_arg) { dladm_continuous(handle, linkid, state.fs_flow, interval, FLOW_REPORT); return; } if (s_arg) { flow_stats(state.fs_flow, linkid, interval, fields_str, &state); return; } oferr = ofmt_open(fields_str, flow_fields, ofmtflags, 0, &ofmt); flowadm_ofmt_check(oferr, state.fs_parsable, ofmt); state.fs_ofmt = ofmt; /* Show attributes of one flow */ if (state.fs_flow != NULL) { show_one_flow(&state, state.fs_flow); /* Show attributes of flows on one link */ } else if (l_arg) { (void) show_flows_onelink(handle, linkid, &state); /* Show attributes of all flows on all links */ } else { (void) dladm_walk_datalink_id(show_flows_onelink, handle, &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); } ofmt_close(ofmt); } static dladm_status_t set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val, uint_t val_cnt, boolean_t reset) { dladm_status_t status; char *errprop; status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt, DLADM_OPT_PERSIST, &errprop); if (status != DLADM_STATUS_OK) { warn_dlerr(status, "cannot persistently %s flow " "property '%s' on '%s'", reset? "reset": "set", errprop, flow); } return (status); } static void set_flowprop(int argc, char **argv, boolean_t reset) { int i, option; char errmsg[DLADM_STRSIZE]; const char *flow = NULL; char propstr[DLADM_STRSIZE]; dladm_arg_list_t *proplist = NULL; boolean_t temp = B_FALSE; dladm_status_t status = DLADM_STATUS_OK; opterr = 0; bzero(propstr, DLADM_STRSIZE); while ((option = getopt_long(argc, argv, ":p:R:t", prop_longopts, NULL)) != -1) { switch (option) { case 'p': (void) strlcat(propstr, optarg, DLADM_STRSIZE); if (strlcat(propstr, ",", DLADM_STRSIZE) >= DLADM_STRSIZE) die("property list too long '%s'", propstr); break; case 't': temp = B_TRUE; break; case 'R': status = dladm_set_rootdir(optarg); if (status != DLADM_STATUS_OK) { die_dlerr(status, "invalid directory " "specified"); } break; default: die_opterr(optopt, option); break; } } if (optind == (argc - 1)) { if (strlen(argv[optind]) >= MAXFLOWNAMELEN) die("flow name too long"); flow = argv[optind]; } else if (optind != argc) { usage(); } if (flow == NULL) die("flow name must be specified"); if (dladm_parse_flow_props(propstr, &proplist, reset) != DLADM_STATUS_OK) die("invalid flow property specified"); if (proplist == NULL) { char *errprop; if (!reset) die("flow property must be specified"); status = dladm_set_flowprop(handle, flow, NULL, NULL, 0, DLADM_OPT_ACTIVE, &errprop); if (status != DLADM_STATUS_OK) { warn_dlerr(status, "cannot reset flow property '%s' " "on '%s'", errprop, flow); } if (!temp) { dladm_status_t s; s = set_flowprop_persist(flow, NULL, NULL, 0, reset); if (s != DLADM_STATUS_OK) status = s; } goto done; } for (i = 0; i < proplist->al_count; i++) { dladm_arg_info_t *aip = &proplist->al_info[i]; char **val; uint_t count; dladm_status_t s; if (reset) { val = NULL; count = 0; } else { val = aip->ai_val; count = aip->ai_count; if (count == 0) { warn("no value specified for '%s'", aip->ai_name); status = DLADM_STATUS_BADARG; continue; } } s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count, DLADM_OPT_ACTIVE, NULL); if (s == DLADM_STATUS_OK) { if (!temp) { s = set_flowprop_persist(flow, aip->ai_name, val, count, reset); if (s != DLADM_STATUS_OK) status = s; } continue; } status = s; switch (s) { case DLADM_STATUS_NOTFOUND: warn("invalid flow property '%s'", aip->ai_name); break; case DLADM_STATUS_BADVAL: { int j; char *ptr, *lim; char **propvals = NULL; uint_t valcnt = DLADM_MAX_PROP_VALCNT; ptr = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); if (ptr == NULL) die("insufficient memory"); propvals = (char **)(void *)ptr; for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { propvals[j] = ptr + sizeof (char *) * DLADM_MAX_PROP_VALCNT + j * DLADM_PROP_VAL_MAX; } s = dladm_get_flowprop(handle, flow, DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals, &valcnt); ptr = errmsg; lim = ptr + DLADM_STRSIZE; *ptr = '\0'; for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { ptr += snprintf(ptr, lim - ptr, "%s,", propvals[j]); if (ptr >= lim) break; } if (ptr > errmsg) { *(ptr - 1) = '\0'; warn("flow property '%s' must be one of: %s", aip->ai_name, errmsg); } else warn("%s is an invalid value for " "flow property %s", *val, aip->ai_name); free(propvals); break; } default: if (reset) { warn_dlerr(status, "cannot reset flow property " "'%s' on '%s'", aip->ai_name, flow); } else { warn_dlerr(status, "cannot set flow property " "'%s' on '%s'", aip->ai_name, flow); } break; } } done: dladm_free_props(proplist); if (status != DLADM_STATUS_OK) { dladm_close(handle); exit(EXIT_FAILURE); } } static void do_set_flowprop(int argc, char **argv) { set_flowprop(argc, argv, B_FALSE); } static void do_reset_flowprop(int argc, char **argv) { set_flowprop(argc, argv, B_TRUE); } static void warn(const char *format, ...) { va_list alist; format = gettext(format); (void) fprintf(stderr, "%s: warning: ", progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) putchar('\n'); } /* PRINTFLIKE2 */ static void warn_dlerr(dladm_status_t err, const char *format, ...) { va_list alist; char errmsg[DLADM_STRSIZE]; format = gettext(format); (void) fprintf(stderr, gettext("%s: warning: "), progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); } /* PRINTFLIKE1 */ static void die(const char *format, ...) { va_list alist; format = gettext(format); (void) fprintf(stderr, "%s: ", progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) putchar('\n'); /* close dladm handle if it was opened */ if (handle != NULL) dladm_close(handle); exit(EXIT_FAILURE); } static void die_optdup(int opt) { die("the option -%c cannot be specified more than once", opt); } static void die_opterr(int opt, int opterr) { switch (opterr) { case ':': die("option '-%c' requires a value", opt); break; case '?': default: die("unrecognized option '-%c'", opt); break; } } /* PRINTFLIKE2 */ static void die_dlerr(dladm_status_t err, const char *format, ...) { va_list alist; char errmsg[DLADM_STRSIZE]; format = gettext(format); (void) fprintf(stderr, "%s: ", progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); /* close dladm handle if it was opened */ if (handle != NULL) dladm_close(handle); exit(EXIT_FAILURE); } static void print_flowprop(const char *flowname, show_flowprop_state_t *statep, const char *propname, dladm_prop_type_t type, const char *format, char **pptr) { int i; char *ptr, *lim; char buf[DLADM_STRSIZE]; char *unknown = "--", *notsup = ""; char **propvals = statep->fs_propvals; uint_t valcnt = DLADM_MAX_PROP_VALCNT; dladm_status_t status; status = dladm_get_flowprop(handle, flowname, type, propname, propvals, &valcnt); if (status != DLADM_STATUS_OK) { if (status == DLADM_STATUS_TEMPONLY) { if (type == DLADM_PROP_VAL_MODIFIABLE && statep->fs_persist) { valcnt = 1; propvals = &unknown; } else { statep->fs_status = status; statep->fs_retstatus = status; return; } } else if (status == DLADM_STATUS_NOTSUP || statep->fs_persist) { valcnt = 1; if (type == DLADM_PROP_VAL_CURRENT) propvals = &unknown; else propvals = ¬sup; } else { if ((statep->fs_proplist != NULL) && statep->fs_status == DLADM_STATUS_OK) { warn("invalid flow property '%s'", propname); } statep->fs_status = status; statep->fs_retstatus = status; return; } } statep->fs_status = DLADM_STATUS_OK; ptr = buf; lim = buf + DLADM_STRSIZE; for (i = 0; i < valcnt; i++) { if (propvals[i][0] == '\0' && !statep->fs_parsable) ptr += snprintf(ptr, lim - ptr, "--,"); else ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]); if (ptr >= lim) break; } if (valcnt > 0) buf[strlen(buf) - 1] = '\0'; lim = statep->fs_line + MAX_PROP_LINE; if (statep->fs_parsable) { *pptr += snprintf(*pptr, lim - *pptr, "%s", buf); } else { *pptr += snprintf(*pptr, lim - *pptr, format, buf); } } static boolean_t print_flowprop_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { flowprop_args_t *arg = of_arg->ofmt_cbarg; char *propname = arg->fs_propname; show_flowprop_state_t *statep = arg->fs_state; char *ptr = statep->fs_line; char *lim = ptr + MAX_PROP_LINE; char *flowname = arg->fs_flowname; switch (of_arg->ofmt_id) { case FLOWPROP_FLOW: (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow); break; case FLOWPROP_PROPERTY: (void) snprintf(ptr, lim - ptr, "%s", propname); break; case FLOWPROP_VALUE: print_flowprop(flowname, statep, propname, statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT : DLADM_PROP_VAL_CURRENT, "%s", &ptr); /* * If we failed to query the flow property, for example, query * the persistent value of a non-persistable flow property, * simply skip the output. */ if (statep->fs_status != DLADM_STATUS_OK) goto skip; ptr = statep->fs_line; break; case FLOWPROP_DEFAULT: print_flowprop(flowname, statep, propname, DLADM_PROP_VAL_DEFAULT, "%s", &ptr); if (statep->fs_status != DLADM_STATUS_OK) goto skip; ptr = statep->fs_line; break; case FLOWPROP_POSSIBLE: print_flowprop(flowname, statep, propname, DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr); if (statep->fs_status != DLADM_STATUS_OK) goto skip; ptr = statep->fs_line; break; default: die("invalid input"); break; } (void) strlcpy(buf, ptr, bufsize); return (B_TRUE); skip: buf[0] = '\0'; return ((statep->fs_status == DLADM_STATUS_OK) ? B_TRUE : B_FALSE); } static int show_one_flowprop(void *arg, const char *propname) { show_flowprop_state_t *statep = arg; flowprop_args_t fs_arg; bzero(&fs_arg, sizeof (fs_arg)); fs_arg.fs_state = statep; fs_arg.fs_propname = (char *)propname; fs_arg.fs_flowname = (char *)statep->fs_flow; ofmt_print(statep->fs_ofmt, (void *)&fs_arg); return (DLADM_WALK_CONTINUE); } /*ARGSUSED*/ /* Walker function called by dladm_walk_flow to display flow properties */ static int show_flowprop(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) { show_flowprop_one_flow(arg, attr->fa_flowname); return (DLADM_WALK_CONTINUE); } /* * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it * usable to dladm_walk_datalink_id() */ static int show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) { char name[MAXLINKNAMELEN]; if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name, sizeof (name)) != DLADM_STATUS_OK) return (DLADM_WALK_TERMINATE); (void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE); return (DLADM_WALK_CONTINUE); } static void do_show_flowprop(int argc, char **argv) { int option; dladm_arg_list_t *proplist = NULL; show_flowprop_state_t state; char *fields_str = NULL; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; opterr = 0; state.fs_propvals = NULL; state.fs_line = NULL; state.fs_parsable = B_FALSE; state.fs_persist = B_FALSE; state.fs_header = B_TRUE; state.fs_retstatus = DLADM_STATUS_OK; state.fs_linkid = DATALINK_INVALID_LINKID; state.fs_flow = NULL; while ((option = getopt_long(argc, argv, ":p:cPl:o:", prop_longopts, NULL)) != -1) { switch (option) { case 'p': if (dladm_parse_flow_props(optarg, &proplist, B_TRUE) != DLADM_STATUS_OK) die("invalid flow properties specified"); break; case 'c': state.fs_parsable = B_TRUE; ofmtflags |= OFMT_PARSABLE; break; case 'P': state.fs_persist = B_TRUE; break; case 'l': if (dladm_name2info(handle, optarg, &state.fs_linkid, NULL, NULL, NULL) != DLADM_STATUS_OK) die("invalid link '%s'", optarg); break; case 'o': fields_str = optarg; break; default: die_opterr(optopt, option); break; } } if (optind == (argc - 1)) { if (strlen(argv[optind]) >= MAXFLOWNAMELEN) die("flow name too long"); state.fs_flow = argv[optind]; } else if (optind != argc) { usage(); } state.fs_proplist = proplist; state.fs_status = DLADM_STATUS_OK; oferr = ofmt_open(fields_str, flowprop_fields, ofmtflags, 0, &ofmt); flowadm_ofmt_check(oferr, state.fs_parsable, ofmt); state.fs_ofmt = ofmt; /* Show properties for one flow */ if (state.fs_flow != NULL) { show_flowprop_one_flow(&state, state.fs_flow); /* Show properties for all flows on one link */ } else if (state.fs_linkid != DATALINK_INVALID_LINKID) { (void) show_flowprop_onelink(handle, state.fs_linkid, &state); /* Show properties for all flows on all links */ } else { (void) dladm_walk_datalink_id(show_flowprop_onelink, handle, &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); } dladm_free_props(proplist); ofmt_close(ofmt); } static void show_flowprop_one_flow(void *arg, const char *flow) { int i; char *buf; dladm_status_t status; dladm_arg_list_t *proplist = NULL; show_flowprop_state_t *statep = arg; dladm_flow_attr_t attr; const char *savep; /* * Do not print flow props for invalid flows. */ if ((status = dladm_flow_info(handle, flow, &attr)) != DLADM_STATUS_OK) { die("invalid flow: '%s'", flow); } savep = statep->fs_flow; statep->fs_flow = flow; proplist = statep->fs_proplist; buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); if (buf == NULL) die("insufficient memory"); statep->fs_propvals = (char **)(void *)buf; for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { statep->fs_propvals[i] = buf + sizeof (char *) * DLADM_MAX_PROP_VALCNT + i * DLADM_PROP_VAL_MAX; } statep->fs_line = buf + (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; /* show only specified flow properties */ if (proplist != NULL) { for (i = 0; i < proplist->al_count; i++) { if (show_one_flowprop(statep, proplist->al_info[i].ai_name) != DLADM_STATUS_OK) break; } /* show all flow properties */ } else { status = dladm_walk_flowprop(show_one_flowprop, flow, statep); if (status != DLADM_STATUS_OK) die_dlerr(status, "show-flowprop"); } free(buf); statep->fs_flow = savep; } /* * default output callback function that, when invoked from dladm_print_output, * prints string which is offset by of_arg->ofmt_id within buf. */ static boolean_t print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { char *value; value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id; (void) strlcpy(buf, value, bufsize); return (B_TRUE); } static void flowadm_ofmt_check(ofmt_status_t oferr, boolean_t parsable, ofmt_handle_t ofmt) { char buf[OFMT_BUFSIZE]; if (oferr == OFMT_SUCCESS) return; (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf)); /* * All errors are considered fatal in parsable mode. * NOMEM errors are always fatal, regardless of mode. * For other errors, we print diagnostics in human-readable * mode and processs what we can. */ if (parsable || oferr == OFMT_ENOFIELDS) { ofmt_close(ofmt); die(buf); } else { warn(buf); } }