/*
* 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 2010 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 <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>
#include <ofmt.h>
typedef struct show_flow_state {
const char *fs_flow;
typedef void cmdfunc_t(int, char **);
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 *, ...);
/* callback functions for printing output */
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 },
};
{ 0, 0, 0, 0 }
};
{ 0, 0, 0, 0 }
};
/*
* structures for 'flowadm remove-flow'
*/
typedef struct remove_flow_state {
const char *fs_altroot;
typedef struct flow_fields_buf_s
{
/* name, field width, index */
{ "FLOW", 12,
{ "LINK", 12,
{ "IPADDR", 25,
{ "PROTO", 7,
{ "LPORT", 8,
{ "RPORT", 8,
{ "DSFLD", 10,
;
/*
* structures for 'flowadm show-flowprop'
*/
typedef enum {
/* name, fieldwidth, index, callback */
;
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;
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", "remote_port",
"dsfield"};
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] [-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"));
/* close dladm handle if it was opened */
exit(1);
}
int
{
#if !defined(TEXT_DOMAIN)
#endif
(void) textdomain(TEXT_DOMAIN);
if (argc < 2)
usage();
cmdlen) == 0)) {
/* Open the libdladm handle */
}
}
}
usage();
return (0);
}
static const char *
{
int i;
for (i = 0; i < NATTR; i++) {
return (attr);
}
}
return (NULL);
}
/* ARGSUSED */
static void
{
if (status != DLADM_STATUS_OK)
}
static void
{
char option;
switch (option) {
case 't':
break;
case 'R':
break;
case 'l':
MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
die("link name too long");
}
break;
case 'a':
break;
case 'p':
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");
}
!= DLADM_STATUS_OK)
die("invalid flow attribute specified");
!= DLADM_STATUS_OK)
die("invalid flow property specified");
if (status != DLADM_STATUS_OK)
}
static void
{
char option;
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();
*/
/*ARGSUSED*/
static int
{
return (DLADM_WALK_CONTINUE);
else
return (DLADM_WALK_TERMINATE);
}
/*ARGSUSED*/
static dladm_status_t
{
return (status);
}
"%s", link);
sizeof (fbuf->flow_ipaddr));
sizeof (fbuf->flow_proto));
sizeof (fbuf->flow_lport));
}
sizeof (fbuf->flow_rport));
}
sizeof (fbuf->flow_dsfield));
return (DLADM_STATUS_OK);
}
/*
* Walker function for showing flow attributes through dladm_walk_flow().
*/
/*ARGSUSED*/
static int
{
/*
* first get all the flow attributes into fbuf;
*/
if (status != DLADM_STATUS_OK)
goto done;
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
{
int option;
opterr = 0;
switch (option) {
case 'p':
break;
case 'P':
break;
case 'o':
if (o_arg)
fields_str = optarg;
break;
case 'l':
>= MAXLINKNAMELEN)
die("link name too long\n");
break;
default:
break;
}
}
/* get flow name (optional last argument */
>= MAXFLOWNAMELEN)
die("flow name too long");
}
/* 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;
opterr = 0;
switch (option) {
case 'p':
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");
!= DLADM_STATUS_OK)
die("invalid flow property 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) {
}
}
static void
{
}
static void
{
}
static void
{
}
/* PRINTFLIKE2 */
static void
{
}
/* PRINTFLIKE1 */
static void
{
/* close dladm handle if it was opened */
}
static void
{
}
static void
{
switch (opterr) {
case ':':
break;
case '?':
default:
break;
}
}
/* PRINTFLIKE2 */
static void
{
/* close dladm handle if it was opened */
}
static void
{
int i;
&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_parsable) {
"%s", buf);
} else {
}
}
static boolean_t
{
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 (B_TRUE);
skip:
buf[0] = '\0';
}
static int
{
return (DLADM_WALK_CONTINUE);
}
/*ARGSUSED*/
/* 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
{
sizeof (name)) != DLADM_STATUS_OK)
return (DLADM_WALK_TERMINATE);
return (DLADM_WALK_CONTINUE);
}
static void
{
int option;
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':
fields_str = optarg;
break;
default:
break;
}
}
die("flow name too long");
usage();
}
/* 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)
}
}
/*
* 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
{
char *value;
return (B_TRUE);
}
static void
{
if (oferr == OFMT_SUCCESS)
return;
/*
* 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.
*/
} else {
}
}