flowadm.c revision 0790b6dc17a39eb1ef864e0916341fa2c88261f0
/*
* 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
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <locale.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <stropts.h>
#include <errno.h>
#include <kstat.h>
#include <strings.h>
#include <getopt.h>
#include <unistd.h>
#include <priv.h>
#include <netdb.h>
#include <libintl.h>
#include <libdlflow.h>
#include <libdllink.h>
#include <libdlstat.h>
#include <sys/ethernet.h>
#include <stddef.h>
#define CMD_TYPE_ANY 0xffffffff
#define STR_UNDEF_VAL "--"
/*
* data structures and routines for printing output.
*/
typedef struct print_field_s {
const char *pf_name;
const char *pf_header;
union {
}_pf_un;
typedef struct print_state_s {
typedef struct show_usage_state_s {
typedef char *(*print_callback_t)(print_field_t *, void *);
static void print_header(print_state_t *);
print_callback_t, void *);
/*
* helper function that, when invoked as flowadm(print_field(pf, buf)
* prints string which is offset by pf->pf_offset within buf.
*/
static char *flowadm_print_field(print_field_t *, void *);
#define MAX_FIELD_LEN 32
typedef void cmdfunc_t(int, char **);
static cmdfunc_t do_show_usage;
static int show_flow(dladm_flow_attr_t *, void *);
static void get_flow_stats(const char *, pktsum_t *);
static int show_flow_stats(dladm_flow_attr_t *, void *);
static int remove_flow(dladm_flow_attr_t *, void *);
static int show_flowprop(dladm_flow_attr_t *, void *);
static void show_flowprop_one_flow(void *, const char *);
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 *, ...);
typedef struct cmd {
char *c_name;
void (*c_fn)(int, char **);
} cmd_t;
{ "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 }
};
{ 0, 0, 0, 0 }
};
static const struct option prop_longopts[] = {
{ 0, 0, 0, 0 }
};
/*
* structures for 'flowadm show-flow'
*/
typedef struct show_flow_state {
const char *fs_flow;
const char *fs_link;
/*
* structures for 'flowadm remove-flow'
*/
typedef struct remove_flow_state {
const char *fs_altroot;
typedef struct flow_args_s {
const char *fa_link;
int fa_attrno; /* -1 indicates flow itself */
} flow_args_t;
#define PROTO_MAXSTR_LEN 7
#define PORT_MAXSTR_LEN 6
#define DSFIELD_MAXSTR_LEN 10
typedef struct flow_fields_buf_s
{
char flow_name[MAXNAMELEN];
char flow_link[MAXLINKNAMELEN];
char flow_proto[PROTO_MAXSTR_LEN];
char flow_port[PORT_MAXSTR_LEN];
char flow_dsfield[DSFIELD_MAXSTR_LEN];
static print_field_t flow_fields[] = {
/* name, header, field width, index, cmdtype */
{ "flow", "FLOW", 11,
{ "link", "LINK", 11,
{ "ipaddr", "IP ADDR", 30,
{ "transport", "PROTO", 6,
{ "port", "PORT", 7,
{ "dsfield", "DSFLD", 9,
;
/*
* structures for 'flowadm show-flowprop'
*/
typedef enum {
static print_field_t flowprop_fields[] = {
/* name, header, fieldwidth, index, cmdtype */
;
#define FLOWPROP_MAX_FIELDS \
(sizeof (flowprop_fields) / sizeof (print_field_t))
#define MAX_PROP_LINE 512
typedef struct show_flowprop_state {
const char *fs_flow;
char *fs_line;
char **fs_propvals;
typedef struct set_flowprop_state {
const char *fs_name;
typedef struct flowprop_args_s {
char *fs_propname;
char *fs_flowname;
/*
* structures for 'flow 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];
static print_field_t usage_fields[] = {
/* name, header, field width, offset, cmdtype */
{ "flow", "FLOW", 12,
{ "duration", "DURATION", 10,
{ "ipackets", "IPACKETS", 9,
{ "rbytes", "RBYTES", 10,
{ "opackets", "OPACKETS", 9,
{ "obytes", "OBYTES", 10,
{ "bandwidth", "BANDWIDTH", 14,
;
/*
* 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];
static print_field_t usage_l_fields[] = {
/* name, header, field width, offset, cmdtype */
{ "flow", "FLOW", 12,
{ "start", "START", 13,
{ "end", "END", 13,
{ "rbytes", "RBYTES", 8,
{ "obytes", "OBYTES", 8,
{ "bandwidth", "BANDWIDTH", 14,
;
#define USAGE_L_MAX_FIELDS \
(sizeof (usage_l_fields) /sizeof (print_field_t))
#define PRI_HI 100
#define PRI_LO 10
#define PRI_NORM 50
#define FLOWADM_CONF "/etc/dladm/flowadm.conf"
static char *progname;
/*
* Handle to libdladm. Opened in main() before the sub-command
* specific function is called.
*/
static const char *attr_table[] =
{"local_ip", "remote_ip", "transport", "local_port", "dsfield"};
#define NATTR (sizeof (attr_table)/sizeof (char *))
static void
usage(void)
{
" <args>...\n"
" add-flow [-t] -l <link> -a <attr>=<value>[,...]\n"
"\t\t [-p <prop>=<value>,...] <flow>\n"
" remove-flow [-t] {-l <link> | <flow>}\n"
" show-flow [-p] [-s [-i <interval>]] [-l <link>] "
"[<flow>]\n\n"
" set-flowprop [-t] -p <prop>=<value>[,...] <flow>\n"
" reset-flowprop [-t] [-p <prop>,...] <flow>\n"
" show-flowprop [-cP] [-l <link>] [-p <prop>,...] "
"[<flow>]\n\n"
" show-usage [-d|-p -F <format>] "
/* close dladm handle if it was opened */
exit(1);
}
int
{
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if (argc < 2)
usage();
cmdlen) == 0)) {
/* Open the libdladm handle */
}
exit(0);
}
}
usage();
return (0);
}
static const char *
match_attr(char *attr)
{
int i;
for (i = 0; i < NATTR; i++) {
return (attr);
}
}
return (NULL);
}
/* ARGSUSED */
static void
{
if (status != DLADM_STATUS_OK)
}
/* ARGSUSED */
static int
{
char timebuf[20];
return (DLADM_STATUS_OK);
}
static int
{
char buf[DLADM_STRSIZE];
double bw;
if (!state->us_printheader) {
(void) printf("# Time");
}
(void) printf("\n");
}
} else {
}
(void) printf("\n");
}
}
return (DLADM_STATUS_OK);
}
buf);
buf);
}
flowadm_print_field, (void *)&ubuf);
return (DLADM_STATUS_OK);
}
static int
{
char buf[DLADM_STRSIZE];
}
flowadm_print_field, (void *)&ubuf);
return (DLADM_STATUS_OK);
}
static boolean_t
valid_formatspec(char *formatspec_str)
{
return (B_TRUE);
return (B_FALSE);
}
/* ARGSUSED */
static void
{
int opt;
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";
switch (opt) {
case 'd':
break;
case 'p':
break;
case 'f':
break;
case 's':
break;
case 'e':
break;
case 'o':
fields_str = optarg;
break;
case 'F':
break;
default:
}
}
die("show-usage requires a file");
}
} else {
}
die("invalid fields(s) specified");
return;
}
die("plot and date options are incompatible");
die("specify format speicifier: -F <format>");
if (d_arg) {
/* Print log dates */
!p_arg) {
/* Print summary */
/* Print log entries for named resource */
} else {
/* Print time and information for each link */
}
if (status != DLADM_STATUS_OK)
}
static void
{
char devname[MAXNAMELEN];
char option;
switch (option) {
case 't':
break;
case 'R':
break;
case 'l':
MAXNAMELEN) >= MAXNAMELEN) {
die("link name too long");
}
break;
case 'a':
!= DLADM_STATUS_OK)
die("invalid flow attribute specified");
break;
case 'p':
!= DLADM_STATUS_OK)
die("invalid flow property specified");
break;
default:
}
}
if (!l_arg) {
die("link is required");
}
opterr = 0;
die("flow name is required");
} else {
/* get flow name; required last argument */
die("flow name too long");
}
if (status != DLADM_STATUS_OK)
}
static void
{
char option;
char linkname[MAXNAMELEN];
opterr = 0;
switch (option) {
case 't':
break;
case 'R':
break;
case 'l':
MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
die("link name too long");
}
}
break;
default:
break;
}
}
/* when link not specified get flow name */
if (!l_arg) {
usage();
} else {
die("flow name too long");
}
} else {
/* if link is specified then flow name should not be there */
usage();
/* walk the link to find flows and remove them */
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)
}
if (status != DLADM_STATUS_OK)
}
/*
* Walker function for removing a flow through dladm_walk_flow();
*/
static int
{
return (DLADM_WALK_CONTINUE);
else
return (DLADM_WALK_TERMINATE);
}
static char *
{
char *value;
return (value);
}
/*ARGSUSED*/
static dladm_status_t
{
char link[MAXLINKNAMELEN];
return (status);
}
"%s", link);
sizeof (fbuf->flow_ipaddr));
sizeof (fbuf->flow_proto));
sizeof (fbuf->flow_dsfield));
return (DLADM_STATUS_OK);
}
/*
* Walker function for showing flow attributes through dladm_walk_flow().
*/
static int
{
/*
* first get all the flow attributes into fbuf;
*/
if (status != DLADM_STATUS_OK)
goto done;
}
flowadm_print_field, (void *)&fbuf);
done:
return (DLADM_WALK_CONTINUE);
}
static void
{
else
}
/*
* 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
{
return (DLADM_WALK_CONTINUE);
}
static void
{
warn("kstat open operation failed");
return;
}
(void) kstat_close(kcp);
}
/* ARGSUSED */
static int
{
if (state->fs_firstonly) {
if (state->fs_donefirst)
return (DLADM_WALK_TERMINATE);
} else {
}
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
{
== DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
else
return (DLADM_WALK_TERMINATE);
}
/* ARGSUSED */
static void
{
/*
* If an interval is specified, continuously show the stats
* for only the first flow.
*/
for (;;) {
if (!state.fs_donefirst)
(void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n",
"FLOW", "IPACKETS", "RBYTES", "IERRORS",
"OPACKETS", "OBYTES", "OERRORS");
/* Show stats for named flow */
/* Show all stats on a link */
} else if (linkid != DATALINK_INVALID_LINKID) {
/* Show all stats by datalink */
} else {
}
if (interval == 0)
break;
}
}
static void
{
char flowname[MAXFLOWNAME];
char linkname[MAXNAMELEN];
int option;
char *fields_str = NULL;
char *all_fields =
"flow,link,ipaddr,transport,port,dsfield";
opterr = 0;
switch (option) {
case 'p':
break;
case 'P':
break;
case 's':
if (s_arg)
break;
case 'S':
if (S_arg)
break;
case 'o':
if (o_arg)
fields_str = optarg;
break;
case 'i':
if (i_arg)
errno = 0;
die("invalid interval value" " '%d'\n",
interval);
break;
case 'l':
>= MAXLINKNAMELEN)
die("link name too long\n");
break;
default:
break;
}
}
die("the -i option can be used only with -s or -S");
die("the -s option cannot be used with -S");
/* get flow name (optional last argument */
>= MAXFLOWNAME)
die("flow name too long");
}
if (s_arg) {
return;
}
if (S_arg) {
return;
}
CMD_TYPE_ANY, &nfields);
die("invalid fields(s) specified");
return;
}
/* Show attributes of one flow */
/* Show attributes of flows on one link */
} else if (l_arg) {
/* Show attributes of all flows on all links */
} else {
}
}
static dladm_status_t
{
char *errprop;
if (status != DLADM_STATUS_OK) {
}
return (status);
}
static void
{
int i, option;
char errmsg[DLADM_STRSIZE];
opterr = 0;
switch (option) {
case 'p':
!= DLADM_STATUS_OK)
die("invalid flow property specified");
break;
case 't':
break;
case 'R':
if (status != DLADM_STATUS_OK) {
"specified");
}
break;
default:
break;
}
}
die("flow name too long");
usage();
}
die("flow name must be specified");
char *errprop;
if (!reset)
die("flow property must be specified");
if (status != DLADM_STATUS_OK) {
}
if (!temp) {
if (s != DLADM_STATUS_OK)
status = s;
}
goto done;
}
char **val;
if (reset) {
count = 0;
} else {
if (count == 0) {
warn("no value specified for '%s'",
continue;
}
}
if (s == DLADM_STATUS_OK) {
if (!temp) {
s = set_flowprop_persist(flow,
if (s != DLADM_STATUS_OK)
status = s;
}
continue;
}
status = s;
switch (s) {
case DLADM_STATUS_NOTFOUND:
break;
case DLADM_STATUS_BADVAL: {
int j;
die("insufficient memory");
for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
j * DLADM_PROP_VAL_MAX;
}
&valcnt);
*ptr = '\0';
for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
propvals[j]);
break;
}
warn("flow property '%s' must be one of: %s",
} else
warn("%s is an invalid value for "
break;
}
default:
if (reset) {
} else {
}
break;
}
}
done:
if (status != DLADM_STATUS_OK) {
exit(1);
}
}
static void
{
}
static void
{
}
static void
{
(void) putchar('\n');
}
/* PRINTFLIKE2 */
static void
{
char errmsg[DLADM_STRSIZE];
}
/* PRINTFLIKE1 */
static void
{
(void) putchar('\n');
/* close dladm handle if it was opened */
}
static void
die_optdup(int opt)
{
}
static void
{
switch (opterr) {
case ':':
break;
case '?':
default:
break;
}
}
/* PRINTFLIKE2 */
static void
{
char errmsg[DLADM_STRSIZE];
/* close dladm handle if it was opened */
}
static void
{
int i;
char buf[DLADM_STRSIZE];
&valcnt);
if (status != DLADM_STATUS_OK) {
if (status == DLADM_STATUS_TEMPONLY) {
if (type == DLADM_PROP_VAL_MODIFIABLE &&
statep->fs_persist) {
valcnt = 1;
} else {
return;
}
} else if (status == DLADM_STATUS_NOTSUP ||
statep->fs_persist) {
valcnt = 1;
if (type == DLADM_PROP_VAL_CURRENT)
else
} else {
}
return;
}
}
for (i = 0; i < valcnt; i++) {
else
break;
}
if (valcnt > 0)
if (statep->fs_parseable) {
"%s", buf);
} else {
}
}
static char *
{
case FLOWPROP_FLOW:
break;
case FLOWPROP_PROPERTY:
break;
case FLOWPROP_VALUE:
/*
* If we failed to query the flow property, for example, query
* the persistent value of a non-persistable flow property,
* simply skip the output.
*/
goto skip;
break;
case FLOWPROP_DEFAULT:
goto skip;
break;
case FLOWPROP_POSSIBLE:
goto skip;
break;
default:
die("invalid input");
break;
}
return (ptr);
skip:
return (NULL);
else
return ("");
}
static int
{
if (!statep ->fs_parseable)
}
flowprop_callback, (void *)&fs_arg);
return (DLADM_WALK_CONTINUE);
}
/* Walker function called by dladm_walk_flow to display flow properties */
static int
{
return (DLADM_WALK_CONTINUE);
}
/*
* Wrapper of dladm_walk_flow(show_walk_fn,...) to make it
* usable to dladm_walk_datalink_id()
*/
static int
{
char name[MAXLINKNAMELEN];
sizeof (name)) != DLADM_STATUS_OK)
return (DLADM_WALK_TERMINATE);
return (DLADM_WALK_CONTINUE);
}
static void
{
int option;
char *fields_str = NULL;
char *all_fields =
"flow,property,value,default,possible";
opterr = 0;
switch (option) {
case 'p':
!= DLADM_STATUS_OK)
die("invalid flow properties specified");
break;
case 'c':
break;
case 'P':
break;
case 'l':
break;
case 'o':
else
fields_str = optarg;
break;
default:
break;
}
}
die("flow name too long");
usage();
}
die("invalid field(s) specified");
return;
}
/* Show properties for one flow */
/* Show properties for all flows on one link */
/* Show properties for all flows on all links */
} else {
}
}
static void
{
int i;
char *buf;
const char *savep;
/*
* Do not print flow props for invalid flows.
*/
}
die("insufficient memory");
for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
sizeof (char *) * DLADM_MAX_PROP_VALCNT +
i * DLADM_PROP_VAL_MAX;
}
(sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
/* show only specified flow properties */
if (show_one_flowprop(statep,
break;
}
/* show all flow properties */
} else {
if (status != DLADM_STATUS_OK)
}
}
typedef struct {
char *s_buf;
char **s_fields; /* array of pointer to the fields in s_buf */
} split_t;
/*
* Free the split_t structure pointed to by `sp'.
*/
static void
{
}
/*
* Split `str' into at most `maxfields' fields, each field at most `maxlen' in
* length. Return a pointer to a split_t containing the split fields, or NULL
* on failure.
*/
static split_t *
{
return (NULL);
return (NULL);
goto fail;
goto fail;
}
return (sp);
fail:
return (NULL);
}
static print_field_t **
{
uint_t i, j;
return (NULL);
goto fail;
for (j = 0; j < max_fields; j++) {
break;
}
}
if (!good_match)
goto fail;
}
*countp = i;
return (pf);
fail:
return (NULL);
}
static void
{
int i;
char *value;
print_field_t **pf;
for (i = 0; i < statep->ps_nfields; i++) {
}
(void) putchar('\n');
}
static void
{
int i;
print_field_t **pf;
for (i = 0; i < ps->ps_nfields; i++) {
}
(void) putchar('\n');
}
static void
{
if (parseable) {
} else {
if (value[0] == '\0')
if (statep->ps_lastfield) {
return;
}
}
}
if (!statep->ps_lastfield)
(void) putchar(' ');
}