summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdladm
diff options
context:
space:
mode:
authorEric Cheng <none@none>2008-12-04 18:16:10 -0800
committerEric Cheng <none@none>2008-12-04 18:16:10 -0800
commitda14cebe459d3275048785f25bd869cb09b5307f (patch)
treea394d2c61ec4d7591782a4a5db4e3a157c3ca89a /usr/src/lib/libdladm
parent03361682bf38acf5bcc36ee83a0d6277731eee68 (diff)
downloadillumos-joyent-da14cebe459d3275048785f25bd869cb09b5307f.tar.gz
PSARC/2006/357 Crossbow - Network Virtualization and Resource Management
6498311 Crossbow - Network Virtualization and Resource Management 6402493 DLPI provider loopback behavior should be improved 6453165 move mac capabs definitions outside mac.h 6338667 Need ability to use NAT for non-global zones 6692884 several threads hung due to deadlock scenario between aggr and mac 6768302 dls: soft_ring_bind/unbind race can panic in thread_affinity_set with cpu_id == -1 6635849 race between lacp_xmit_sm() and aggr_m_stop() ends in panic 6742712 potential message double free in the aggr driver 6754299 a potential race between aggr_m_tx() and aggr_port_delete() 6485324 mi_data_lock recursively held when enabling promiscuous mode on an aggregation 6442559 Forwarding perf bottleneck due to mac_rx() calls 6505462 assertion failure after removing a port from a snooped aggregation 6716664 need to add src/dst IP address to soft ring fanout --HG-- rename : usr/src/uts/common/io/dls/dls_soft_ring.c => usr/src/uts/common/io/mac/mac_soft_ring.c rename : usr/src/uts/common/inet/ip/ip_cksum.c => usr/src/uts/common/os/ip_cksum.c rename : usr/src/uts/common/inet/sctp_crc32.c => usr/src/uts/common/os/sctp_crc32.c rename : usr/src/uts/common/sys/dls_soft_ring.h => usr/src/uts/common/sys/mac_soft_ring.h
Diffstat (limited to 'usr/src/lib/libdladm')
-rw-r--r--usr/src/lib/libdladm/Makefile10
-rw-r--r--usr/src/lib/libdladm/Makefile.com10
-rw-r--r--usr/src/lib/libdladm/common/flowattr.c411
-rw-r--r--usr/src/lib/libdladm/common/flowprop.c611
-rw-r--r--usr/src/lib/libdladm/common/libdladm.c326
-rw-r--r--usr/src/lib/libdladm/common/libdladm.h105
-rw-r--r--usr/src/lib/libdladm/common/libdladm_impl.h68
-rw-r--r--usr/src/lib/libdladm/common/libdlaggr.c3
-rw-r--r--usr/src/lib/libdladm/common/libdlflow.c903
-rw-r--r--usr/src/lib/libdladm/common/libdlflow.h93
-rw-r--r--usr/src/lib/libdladm/common/libdlflow_impl.h138
-rw-r--r--usr/src/lib/libdladm/common/libdllink.c300
-rw-r--r--usr/src/lib/libdladm/common/libdllink.h47
-rw-r--r--usr/src/lib/libdladm/common/libdlstat.c684
-rw-r--r--usr/src/lib/libdladm/common/libdlstat.h71
-rw-r--r--usr/src/lib/libdladm/common/libdlvlan.c288
-rw-r--r--usr/src/lib/libdladm/common/libdlvlan.h6
-rw-r--r--usr/src/lib/libdladm/common/libdlvnic.c695
-rw-r--r--usr/src/lib/libdladm/common/libdlvnic.h40
-rw-r--r--usr/src/lib/libdladm/common/linkprop.c965
-rw-r--r--usr/src/lib/libdladm/common/llib-ldladm4
-rw-r--r--usr/src/lib/libdladm/common/mapfile-vers55
-rw-r--r--usr/src/lib/libdladm/common/propfuncs.c699
-rw-r--r--usr/src/lib/libdladm/common/usage.c1437
24 files changed, 7124 insertions, 845 deletions
diff --git a/usr/src/lib/libdladm/Makefile b/usr/src/lib/libdladm/Makefile
index 630a7e2e19..ebe6c51eee 100644
--- a/usr/src/lib/libdladm/Makefile
+++ b/usr/src/lib/libdladm/Makefile
@@ -22,14 +22,14 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
#
include $(SRC)/lib/Makefile.lib
HDRS = libdladm.h libdladm_impl.h libdllink.h libdlaggr.h \
libdlwlan.h libdlwlan_impl.h libdlvnic.h libdlvlan.h \
- libdlmgmt.h
+ libdlmgmt.h libdlflow.h libdlflow_impl.h libdlstat.h
+
HDRDIR = common
SUBDIRS = $(MACH)
@@ -39,7 +39,11 @@ POFILE = libdladm.po
MSGFILES = common/libdladm.c common/linkprop.c common/secobj.c \
common/libdllink.c common/libdlaggr.c \
common/libdlwlan.c common/libdlvnic.c \
- common/libdlvlan.c common/libdlmgmt.c
+ common/libdlvlan.c common/libdlmgmt.c \
+ common/flowattr.c common/flowprop.c \
+ common/propfuncs.c common/libdlflow.c \
+ common/libdlstat.c common/flowattr.c
+
XGETFLAGS = -a -x libdladm.xcl
all := TARGET = all
diff --git a/usr/src/lib/libdladm/Makefile.com b/usr/src/lib/libdladm/Makefile.com
index 0f6419bd29..50aa57e710 100644
--- a/usr/src/lib/libdladm/Makefile.com
+++ b/usr/src/lib/libdladm/Makefile.com
@@ -22,13 +22,13 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
LIBRARY = libdladm.a
VERS = .1
OBJECTS = libdladm.o secobj.o linkprop.o libdllink.o libdlaggr.o \
- libdlwlan.o libdlvnic.o libdlmgmt.o libdlvlan.o
+ libdlwlan.o libdlvnic.o libdlmgmt.o libdlvlan.o \
+ flowattr.o flowprop.o propfuncs.o libdlflow.o libdlstat.o \
+ usage.o
include ../../Makefile.lib
@@ -36,8 +36,8 @@ include ../../Makefile.lib
include ../../Makefile.rootfs
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS += -ldevinfo -lc -linetutil -lsocket -lscf -lrcm \
- -lnvpair -lkstat
+LDLIBS += -ldevinfo -lc -linetutil -lsocket -lscf -lrcm -lnvpair \
+ -lexacct -lnsl -lkstat -lcurses
SRCDIR = ../common
$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
diff --git a/usr/src/lib/libdladm/common/flowattr.c b/usr/src/lib/libdladm/common/flowattr.c
new file mode 100644
index 0000000000..4fb578e5bc
--- /dev/null
+++ b/usr/src/lib/libdladm/common/flowattr.c
@@ -0,0 +1,411 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/mac_flow.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+
+#include <libdladm.h>
+#include <libdlflow.h>
+#include <libdlflow_impl.h>
+
+#define V4_PART_OF_V6(v6) ((v6)._S6_un._S6_u32[3])
+
+/* max port number for UDP, TCP & SCTP */
+#define MAX_PORT 65535
+
+static fad_checkf_t do_check_local_ip;
+static fad_checkf_t do_check_remote_ip;
+static fad_checkf_t do_check_protocol;
+static fad_checkf_t do_check_local_port;
+
+static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *);
+
+static fattr_desc_t attr_table[] = {
+ { "local_ip", do_check_local_ip },
+ { "remote_ip", do_check_remote_ip },
+ { "transport", do_check_protocol },
+ { "local_port", do_check_local_port },
+ { "dsfield", do_check_dsfield },
+};
+
+#define DLADM_MAX_FLOWATTRS (sizeof (attr_table) / sizeof (fattr_desc_t))
+
+static dladm_status_t
+do_check_local_ip(char *attr_val, flow_desc_t *fdesc)
+{
+ return (do_check_ip_addr(attr_val, B_TRUE, fdesc));
+}
+
+static dladm_status_t
+do_check_remote_ip(char *attr_val, flow_desc_t *fdesc)
+{
+ return (do_check_ip_addr(attr_val, B_FALSE, fdesc));
+}
+
+dladm_status_t
+do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd)
+{
+ struct addrinfo *info = NULL;
+ dladm_status_t status;
+ int err, prefix_max, prefix_len = 0;
+ char *prefix_str, *endp = NULL;
+ flow_mask_t mask;
+ in6_addr_t *addr;
+ uchar_t *netmask;
+
+ if ((prefix_str = strchr(addr_str, '/')) != NULL) {
+ *prefix_str++ = '\0';
+ errno = 0;
+ prefix_len = (int)strtol(prefix_str, &endp, 10);
+ if (errno != 0 || prefix_len == 0 || *endp != '\0')
+ return (DLADM_STATUS_INVALID_PREFIXLEN);
+ }
+
+ err = getaddrinfo(addr_str, NULL, NULL, &info);
+ if (err != 0)
+ return (DLADM_STATUS_INVALID_IP);
+
+ mask = FLOW_IP_VERSION;
+ if (local) {
+ mask |= FLOW_IP_LOCAL;
+ addr = &fd->fd_local_addr;
+ netmask = (uchar_t *)&fd->fd_local_netmask;
+ } else {
+ mask |= FLOW_IP_REMOTE;
+ addr = &fd->fd_remote_addr;
+ netmask = (uchar_t *)&fd->fd_remote_netmask;
+ }
+
+ if (info->ai_family == AF_INET) {
+ IN6_INADDR_TO_V4MAPPED(&(((struct sockaddr_in *)
+ (void *)info->ai_addr)->sin_addr), addr);
+ prefix_max = IP_ABITS;
+ fd->fd_ipversion = IPV4_VERSION;
+ netmask = (uchar_t *)
+ &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask))));
+ } else if (info->ai_family == AF_INET6) {
+ *addr = ((struct sockaddr_in6 *)
+ (void *)info->ai_addr)->sin6_addr;
+ prefix_max = IPV6_ABITS;
+ fd->fd_ipversion = IPV6_VERSION;
+ } else {
+ freeaddrinfo(info);
+ return (DLADM_STATUS_INVALID_IP);
+ }
+
+ if (prefix_len == 0)
+ prefix_len = prefix_max;
+
+ status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask);
+
+ if (status != DLADM_STATUS_OK) {
+ freeaddrinfo(info);
+ return (DLADM_STATUS_INVALID_PREFIXLEN);
+ }
+
+ fd->fd_mask |= mask;
+ freeaddrinfo(info);
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+do_check_protocol(char *attr_val, flow_desc_t *fdesc)
+{
+ uint8_t protocol;
+
+ protocol = dladm_str2proto(attr_val);
+
+ if (protocol != 0) {
+ fdesc->fd_mask |= FLOW_IP_PROTOCOL;
+ fdesc->fd_protocol = protocol;
+ return (DLADM_STATUS_OK);
+ } else {
+ return (DLADM_STATUS_INVALID_PROTOCOL);
+ }
+}
+
+dladm_status_t
+do_check_local_port(char *attr_val, flow_desc_t *fdesc)
+{
+ return (do_check_port(attr_val, B_TRUE, fdesc));
+}
+
+dladm_status_t
+do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc)
+{
+ char *endp = NULL;
+ long val;
+
+ if (local) {
+ fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL;
+ val = strtol(attr_val, &endp, 10);
+ if (val < 1 || val > MAX_PORT)
+ return (DLADM_STATUS_INVALID_PORT);
+ fdesc->fd_local_port = htons((uint16_t)val);
+ } else {
+ return (DLADM_STATUS_BADVAL);
+ }
+
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Check for invalid and/or duplicate attribute specification
+ */
+static dladm_status_t
+flow_attrlist_check(dladm_arg_list_t *attrlist)
+{
+ int i, j;
+ boolean_t isset[DLADM_MAX_FLOWATTRS];
+ boolean_t matched;
+
+ for (j = 0; j < DLADM_MAX_FLOWATTRS; j++)
+ isset[j] = B_FALSE;
+
+ for (i = 0; i < attrlist->al_count; i++) {
+ matched = B_FALSE;
+ for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
+ if (strcmp(attrlist->al_info[i].ai_name,
+ attr_table[j].ad_name) == 0) {
+ if (isset[j])
+ return (DLADM_STATUS_FLOW_INCOMPATIBLE);
+ else
+ isset[j] = B_TRUE;
+ matched = B_TRUE;
+ }
+ }
+ /*
+ * if the attribute did not match any of the attribute in
+ * attr_table, then it's an invalid attribute.
+ */
+ if (!matched)
+ return (DLADM_STATUS_BADARG);
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Convert an attribute list to a flow_desc_t using the attribute ad_check()
+ * functions.
+ */
+dladm_status_t
+dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc)
+{
+ dladm_status_t status = DLADM_STATUS_BADARG;
+ int i;
+
+ for (i = 0; i < attrlist->al_count; i++) {
+ dladm_arg_info_t *aip = &attrlist->al_info[i];
+ int j;
+
+ for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
+ fattr_desc_t *adp = &attr_table[j];
+
+ if (strcasecmp(aip->ai_name, adp->ad_name) != 0)
+ continue;
+
+ if ((aip->ai_val == NULL) || (*aip->ai_val == NULL))
+ return (DLADM_STATUS_BADARG);
+
+ if (adp->ad_check != NULL)
+ status = adp->ad_check(*aip->ai_val, flowdesc);
+ else
+ status = DLADM_STATUS_BADARG;
+
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ }
+ }
+ return (status);
+}
+
+void
+dladm_free_attrs(dladm_arg_list_t *list)
+{
+ dladm_free_args(list);
+}
+
+dladm_status_t
+dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues)
+{
+
+ if (dladm_parse_args(str, listp, novalues)
+ != DLADM_STATUS_OK)
+ return (DLADM_STATUS_ATTR_PARSE_ERR);
+
+ if (flow_attrlist_check(*listp) != DLADM_STATUS_OK) {
+ dladm_free_attrs(*listp);
+ return (DLADM_STATUS_ATTR_PARSE_ERR);
+ }
+
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+do_check_dsfield(char *str, flow_desc_t *fd)
+{
+ char *mask_str, *endp = NULL;
+ uint_t mask = 0xff, value;
+
+ if ((mask_str = strchr(str, ':')) != NULL) {
+ *mask_str++ = '\0';
+ errno = 0;
+ mask = strtoul(mask_str, &endp, 16);
+ if (errno != 0 || mask == 0 || mask > 0xff ||
+ *endp != '\0')
+ return (DLADM_STATUS_INVALID_DSFMASK);
+ }
+ errno = 0;
+ endp = NULL;
+ value = strtoul(str, &endp, 16);
+ if (errno != 0 || value == 0 || value > 0xff || *endp != '\0')
+ return (DLADM_STATUS_INVALID_DSF);
+
+ fd->fd_dsfield = (uint8_t)value;
+ fd->fd_dsfield_mask = (uint8_t)mask;
+ fd->fd_mask |= FLOW_IP_DSFIELD;
+ return (DLADM_STATUS_OK);
+}
+
+char *
+dladm_proto2str(uint8_t protocol)
+{
+ if (protocol == IPPROTO_TCP)
+ return ("tcp");
+ if (protocol == IPPROTO_UDP)
+ return ("udp");
+ if (protocol == IPPROTO_SCTP)
+ return ("sctp");
+ if (protocol == IPPROTO_ICMPV6)
+ return ("icmpv6");
+ if (protocol == IPPROTO_ICMP)
+ return ("icmp");
+ else
+ return ("");
+}
+
+uint8_t
+dladm_str2proto(const char *protostr)
+{
+ if (strncasecmp(protostr, "tcp", 3) == 0)
+ return (IPPROTO_TCP);
+ else if (strncasecmp(protostr, "udp", 3) == 0)
+ return (IPPROTO_UDP);
+ else if (strncasecmp(protostr, "sctp", 4) == 0)
+ return (IPPROTO_SCTP);
+ else if (strncasecmp(protostr, "icmpv6", 6) == 0)
+ return (IPPROTO_ICMPV6);
+ else if (strncasecmp(protostr, "icmp", 4) == 0)
+ return (IPPROTO_ICMP);
+
+ return (0);
+}
+
+void
+dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
+{
+ flow_desc_t fdesc = attrp->fa_flow_desc;
+ struct in_addr ipaddr;
+ int prefix_len, prefix_max;
+ char *cp, abuf[INET6_ADDRSTRLEN];
+
+ if (fdesc.fd_mask & FLOW_IP_LOCAL) {
+ if (fdesc.fd_ipversion == IPV6_VERSION) {
+ (void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf,
+ INET6_ADDRSTRLEN);
+ cp = abuf;
+ prefix_max = IPV6_ABITS;
+ } else {
+ ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3];
+ cp = inet_ntoa(ipaddr);
+ prefix_max = IP_ABITS;
+ }
+ (void) dladm_mask2prefixlen(&fdesc.fd_local_netmask,
+ prefix_max, &prefix_len);
+ (void) snprintf(buf, buf_len, "LCL:%s/%d ", cp, prefix_len);
+ } else if (fdesc.fd_mask & FLOW_IP_REMOTE) {
+ if (fdesc.fd_ipversion == IPV6_VERSION) {
+ (void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf,
+ INET6_ADDRSTRLEN);
+ cp = abuf;
+ prefix_max = IPV6_ABITS;
+ } else {
+ ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3];
+ cp = inet_ntoa(ipaddr);
+ prefix_max = IP_ABITS;
+ }
+ (void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask,
+ prefix_max, &prefix_len);
+ (void) snprintf(buf, buf_len, "RMT:%s/%d ", cp, prefix_len);
+ } else {
+ buf[0] = '\0';
+ }
+}
+
+void
+dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
+{
+ flow_desc_t fdesc = attrp->fa_flow_desc;
+
+ (void) snprintf(buf, buf_len, "%s",
+ dladm_proto2str(fdesc.fd_protocol));
+}
+
+void
+dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
+{
+ flow_desc_t fdesc = attrp->fa_flow_desc;
+
+ if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) {
+ (void) snprintf(buf, buf_len, "%d",
+ ntohs(fdesc.fd_local_port));
+ } else {
+ buf[0] = '\0';
+ }
+}
+
+void
+dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
+{
+ flow_desc_t fdesc = attrp->fa_flow_desc;
+
+ if (fdesc.fd_mask & FLOW_IP_DSFIELD) {
+ (void) snprintf(buf, buf_len, "0x%x:0x%x",
+ fdesc.fd_dsfield, fdesc.fd_dsfield_mask);
+ } else {
+ buf[0] = '\0';
+ }
+}
diff --git a/usr/src/lib/libdladm/common/flowprop.c b/usr/src/lib/libdladm/common/flowprop.c
new file mode 100644
index 0000000000..a2125a9d33
--- /dev/null
+++ b/usr/src/lib/libdladm/common/flowprop.c
@@ -0,0 +1,611 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dld.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libdevinfo.h>
+#include <libdladm_impl.h>
+#include <libdlflow.h>
+#include <libdlflow_impl.h>
+#include <libintl.h>
+
+#include <dlfcn.h>
+#include <link.h>
+
+/*
+ * XXX duplicate define
+ */
+#define DLADM_PROP_VAL_MAX 32
+
+static dladm_status_t i_dladm_set_flowprop_db(const char *, const char *,
+ char **, uint_t);
+static dladm_status_t i_dladm_get_flowprop_db(const char *, const char *,
+ char **, uint_t *);
+
+static fpd_getf_t do_get_maxbw;
+static fpd_setf_t do_set_maxbw;
+static fpd_checkf_t do_check_maxbw;
+
+static fpd_getf_t do_get_priority;
+static fpd_setf_t do_set_priority;
+static fpd_checkf_t do_check_priority;
+
+static fprop_desc_t prop_table[] = {
+ { "maxbw", { "", NULL }, NULL, 0, B_FALSE,
+ do_set_maxbw, NULL,
+ do_get_maxbw, do_check_maxbw},
+ { "priority", { "", NULL }, NULL, 0, B_FALSE,
+ do_set_priority, NULL,
+ do_get_priority, do_check_priority}
+};
+
+#define DLADM_MAX_FLOWPROPS (sizeof (prop_table) / sizeof (fprop_desc_t))
+
+static prop_table_t prop_tbl = {
+ prop_table,
+ DLADM_MAX_FLOWPROPS
+};
+
+static resource_prop_t rsrc_prop_table[] = {
+ {"maxbw", do_extract_maxbw},
+ {"priority", do_extract_priority}
+};
+#define DLADM_MAX_RSRC_PROP (sizeof (rsrc_prop_table) / \
+ sizeof (resource_prop_t))
+
+static dladm_status_t flow_proplist_check(dladm_arg_list_t *);
+
+dladm_status_t
+dladm_set_flowprop(const char *flow, const char *prop_name, char **prop_val,
+ uint_t val_cnt, uint_t flags, char **errprop)
+{
+ dladm_status_t status = DLADM_STATUS_BADARG;
+
+ if (flow == NULL || (prop_val == NULL && val_cnt > 0) ||
+ (prop_val != NULL && val_cnt == 0) || flags == 0)
+ return (DLADM_STATUS_BADARG);
+
+ if ((flags & DLADM_OPT_ACTIVE) != 0) {
+ status = i_dladm_set_prop_temp(flow, prop_name, prop_val,
+ val_cnt, flags, errprop, &prop_tbl);
+ if (status == DLADM_STATUS_TEMPONLY &&
+ (flags & DLADM_OPT_PERSIST) != 0)
+ return (DLADM_STATUS_TEMPONLY);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ }
+ if ((flags & DLADM_OPT_PERSIST) != 0) {
+ if (i_dladm_is_prop_temponly(prop_name, errprop, &prop_tbl))
+ return (DLADM_STATUS_TEMPONLY);
+
+ status = i_dladm_set_flowprop_db(flow, prop_name,
+ prop_val, val_cnt);
+ }
+ return (status);
+}
+
+dladm_status_t
+dladm_walk_flowprop(int (*func)(void *, const char *), const char *flow,
+ void *arg)
+{
+ int i;
+
+ if (flow == NULL || func == NULL)
+ return (DLADM_STATUS_BADARG);
+
+ /* Then show data-flow properties if there are any */
+ for (i = 0; i < DLADM_MAX_FLOWPROPS; i++) {
+ if (func(arg, prop_table[i].pd_name) != DLADM_WALK_CONTINUE)
+ break;
+ }
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+dladm_get_flowprop(const char *flow, uint32_t type,
+ const char *prop_name, char **prop_val, uint_t *val_cntp)
+{
+ dladm_status_t status;
+
+ if (flow == NULL || prop_name == NULL || prop_val == NULL ||
+ val_cntp == NULL || *val_cntp == 0)
+ return (DLADM_STATUS_BADARG);
+
+ if (type == DLADM_PROP_VAL_PERSISTENT) {
+ if (i_dladm_is_prop_temponly(prop_name, NULL, &prop_tbl))
+ return (DLADM_STATUS_TEMPONLY);
+ return (i_dladm_get_flowprop_db(flow, prop_name,
+ prop_val, val_cntp));
+ }
+
+ status = i_dladm_get_prop_temp(flow, type, prop_name,
+ prop_val, val_cntp, &prop_tbl);
+ if (status != DLADM_STATUS_NOTFOUND)
+ return (status);
+
+ return (DLADM_STATUS_BADARG);
+}
+
+#define FLOWPROP_RW_DB(statep, writeop) \
+ (i_dladm_rw_db("/etc/dladm/flowprop.conf", \
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, process_prop_db, \
+ (statep), (writeop)))
+
+static dladm_status_t
+i_dladm_set_flowprop_db(const char *flow, const char *prop_name,
+ char **prop_val, uint_t val_cnt)
+{
+ prop_db_state_t state;
+
+ state.ls_op = process_prop_set;
+ state.ls_name = flow;
+ state.ls_propname = prop_name;
+ state.ls_propval = prop_val;
+ state.ls_valcntp = &val_cnt;
+ state.ls_initop = NULL;
+
+ return (FLOWPROP_RW_DB(&state, B_TRUE));
+}
+
+static dladm_status_t
+i_dladm_get_flowprop_db(const char *flow, const char *prop_name,
+ char **prop_val, uint_t *val_cntp)
+{
+ prop_db_state_t state;
+
+ state.ls_op = process_prop_get;
+ state.ls_name = flow;
+ state.ls_propname = prop_name;
+ state.ls_propval = prop_val;
+ state.ls_valcntp = val_cntp;
+ state.ls_initop = NULL;
+
+ return (FLOWPROP_RW_DB(&state, B_FALSE));
+}
+
+dladm_status_t
+i_dladm_init_flowprop_db(void)
+{
+ prop_db_state_t state;
+
+ state.ls_op = process_prop_init;
+ state.ls_name = NULL;
+ state.ls_propname = NULL;
+ state.ls_propval = NULL;
+ state.ls_valcntp = NULL;
+ state.ls_initop = dladm_set_flowprop;
+
+ return (FLOWPROP_RW_DB(&state, B_FALSE));
+}
+
+#define MIN_INFO_SIZE (4 * 1024)
+
+dladm_status_t
+dladm_flow_info(const char *flow, dladm_flow_attr_t *attr)
+{
+ dld_ioc_walkflow_t *ioc;
+ int bufsize, fd;
+ dld_flowinfo_t *flowinfo;
+
+ if ((flow == NULL) || (attr == NULL))
+ return (DLADM_STATUS_BADARG);
+
+ if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
+ return (dladm_errno2status(errno));
+
+ bufsize = MIN_INFO_SIZE;
+ if ((ioc = calloc(1, bufsize)) == NULL) {
+ (void) close(fd);
+ return (dladm_errno2status(errno));
+ }
+
+ (void) strlcpy(ioc->wf_name, flow, sizeof (ioc->wf_name));
+ ioc->wf_len = bufsize - sizeof (*ioc);
+
+ while (ioctl(fd, DLDIOC_WALKFLOW, ioc) < 0) {
+ if (errno == ENOSPC) {
+ bufsize *= 2;
+ ioc = realloc(ioc, bufsize);
+ if (ioc != NULL) {
+ (void) strlcpy(ioc->wf_name, flow,
+ MAXNAMELEN);
+ ioc->wf_len = bufsize - sizeof (*ioc);
+ continue;
+ }
+ }
+ free(ioc);
+ (void) close(fd);
+ return (dladm_errno2status(errno));
+ }
+
+ bzero(attr, sizeof (*attr));
+
+ flowinfo = (dld_flowinfo_t *)(void *)(ioc + 1);
+
+ attr->fa_linkid = flowinfo->fi_linkid;
+ bcopy(&flowinfo->fi_flowname, &attr->fa_flowname,
+ sizeof (attr->fa_flowname));
+ bcopy(&flowinfo->fi_flow_desc, &attr->fa_flow_desc,
+ sizeof (attr->fa_flow_desc));
+ bcopy(&flowinfo->fi_resource_props, &attr->fa_resource_props,
+ sizeof (attr->fa_resource_props));
+
+ free(ioc);
+ (void) close(fd);
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_get_maxbw(const char *flow, char **prop_val, uint_t *val_cnt)
+{
+ mac_resource_props_t *mrp;
+ char buf[DLADM_STRSIZE];
+ dladm_flow_attr_t fa;
+ dladm_status_t status;
+
+ status = dladm_flow_info(flow, &fa);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ mrp = &(fa.fa_resource_props);
+
+ *val_cnt = 1;
+ if (mrp->mrp_mask & MRP_MAXBW) {
+ (void) snprintf(prop_val[0], DLADM_STRSIZE, "%s",
+ dladm_bw2str(mrp->mrp_maxbw, buf));
+ } else {
+ return (DLADM_STATUS_NOTSUP);
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_set_maxbw(const char *flow, val_desc_t *vdp, uint_t val_cnt)
+{
+ dld_ioc_modifyflow_t attr;
+ int fd;
+ mac_resource_props_t mrp;
+ void *val;
+
+ if (val_cnt != 1)
+ return (DLADM_STATUS_BADVALCNT);
+
+ bzero(&mrp, sizeof (mrp));
+ if (vdp != NULL && (val = (void *)vdp->vd_val) != NULL) {
+ bcopy(val, &mrp.mrp_maxbw, sizeof (int64_t));
+ free(val);
+ } else {
+ mrp.mrp_maxbw = MRP_MAXBW_RESETVAL;
+ }
+ mrp.mrp_mask = MRP_MAXBW;
+
+ bzero(&attr, sizeof (attr));
+ (void) strlcpy(attr.mf_name, flow, sizeof (attr.mf_name));
+ bcopy(&mrp, &attr.mf_resource_props, sizeof (mac_resource_props_t));
+
+ fd = open(DLD_CONTROL_DEV, O_RDWR);
+ if (fd < 0) {
+ return (dladm_errno2status(errno));
+ }
+
+ if (ioctl(fd, DLDIOC_MODIFYFLOW, &attr) < 0) {
+ (void) close(fd);
+ return (dladm_errno2status(errno));
+ }
+ (void) close(fd);
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_check_maxbw(fprop_desc_t *pdp, char **prop_val, uint_t val_cnt,
+ val_desc_t **vdpp)
+{
+ uint64_t *maxbw;
+ val_desc_t *vdp = NULL;
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ if (val_cnt != 1)
+ return (DLADM_STATUS_BADVALCNT);
+
+ maxbw = malloc(sizeof (uint64_t));
+ if (maxbw == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ status = dladm_str2bw(*prop_val, maxbw);
+ if (status != DLADM_STATUS_OK) {
+ free(maxbw);
+ return (status);
+ }
+
+ if ((*maxbw < MRP_MAXBW_MINVAL) && (*maxbw != 0)) {
+ free(maxbw);
+ return (DLADM_STATUS_MINMAXBW);
+ }
+
+ vdp = malloc(sizeof (val_desc_t));
+ if (vdp == NULL) {
+ free(maxbw);
+ return (DLADM_STATUS_NOMEM);
+ }
+
+ vdp->vd_val = (uintptr_t)maxbw;
+ *vdpp = vdp;
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_get_priority(const char *flow, char **prop_val, uint_t *val_cnt)
+{
+ mac_resource_props_t *mrp;
+ char buf[DLADM_STRSIZE];
+ dladm_flow_attr_t fa;
+ dladm_status_t status;
+
+ bzero(&fa, sizeof (dladm_flow_attr_t));
+ status = dladm_flow_info(flow, &fa);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ mrp = &(fa.fa_resource_props);
+
+ *val_cnt = 1;
+ if (mrp->mrp_mask & MRP_PRIORITY) {
+ (void) snprintf(prop_val[0], DLADM_STRSIZE, "%s",
+ dladm_pri2str(mrp->mrp_priority, buf));
+ } else {
+ return (DLADM_STATUS_NOTSUP);
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_set_priority(const char *flow, val_desc_t *vdp, uint_t val_cnt)
+{
+ dld_ioc_modifyflow_t attr;
+ int fd;
+ mac_resource_props_t mrp;
+ void *val;
+
+ if (val_cnt != 1)
+ return (DLADM_STATUS_BADVALCNT);
+
+ bzero(&mrp, sizeof (mrp));
+ if (vdp != NULL && (val = (void *)vdp->vd_val) != NULL) {
+ bcopy(val, &mrp.mrp_priority, sizeof (mac_priority_level_t));
+ free(val);
+ } else {
+ mrp.mrp_priority = MPL_RESET;
+ }
+ mrp.mrp_mask = MRP_PRIORITY;
+
+ bzero(&attr, sizeof (attr));
+ (void) strlcpy(attr.mf_name, flow, sizeof (attr.mf_name));
+ bcopy(&mrp, &attr.mf_resource_props, sizeof (mac_resource_props_t));
+
+ fd = open(DLD_CONTROL_DEV, O_RDWR);
+ if (fd < 0) {
+ return (dladm_errno2status(errno));
+ }
+
+ if (ioctl(fd, DLDIOC_MODIFYFLOW, &attr) < 0) {
+ (void) close(fd);
+ return (dladm_errno2status(errno));
+ }
+ (void) close(fd);
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_check_priority(fprop_desc_t *pdp, char **prop_val, uint_t val_cnt,
+ val_desc_t **vdpp)
+{
+ mac_priority_level_t *pri;
+ val_desc_t *vdp = NULL;
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ if (val_cnt != 1)
+ return (DLADM_STATUS_BADVALCNT);
+
+ pri = malloc(sizeof (mac_priority_level_t));
+ if (pri == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ status = dladm_str2pri(*prop_val, pri);
+ if (status != DLADM_STATUS_OK) {
+ free(pri);
+ return (status);
+ }
+
+ if (*pri == -1) {
+ free(pri);
+ return (DLADM_STATUS_BADVAL);
+ }
+
+ vdp = malloc(sizeof (val_desc_t));
+ if (vdp == NULL) {
+ free(pri);
+ return (DLADM_STATUS_NOMEM);
+ }
+
+ vdp->vd_val = (uintptr_t)pri;
+ *vdpp = vdp;
+ return (DLADM_STATUS_OK);
+}
+
+static dladm_status_t
+flow_proplist_check(dladm_arg_list_t *proplist)
+{
+ int i, j;
+ boolean_t matched;
+
+ for (i = 0; i < proplist->al_count; i++) {
+ matched = B_FALSE;
+ for (j = 0; j < DLADM_MAX_FLOWPROPS; j++) {
+ if (strcmp(proplist->al_info[i].ai_name,
+ prop_table[j].pd_name) == 0)
+ matched = B_TRUE;
+ }
+ if (!matched)
+ return (DLADM_STATUS_BADPROP);
+ }
+ return (DLADM_STATUS_OK);
+
+}
+
+dladm_status_t
+dladm_parse_flow_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
+{
+ dladm_status_t status;
+
+ status = dladm_parse_args(str, listp, novalues);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ status = flow_proplist_check(*listp);
+ if (status != DLADM_STATUS_OK) {
+ dladm_free_props(*listp);
+ return (status);
+ }
+
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Retrieve the named property from a proplist, check the value and
+ * convert to a kernel structure.
+ */
+static dladm_status_t
+i_dladm_flow_proplist_extract_one(dladm_arg_list_t *proplist,
+ const char *name, void *val)
+{
+ dladm_status_t status;
+ dladm_arg_info_t *aip = NULL;
+ int i, j;
+
+ /* Find named property in proplist */
+ for (i = 0; i < proplist->al_count; i++) {
+ aip = &proplist->al_info[i];
+ if (strcasecmp(aip->ai_name, name) == 0)
+ break;
+ }
+
+ /* Property not in list */
+ if (i == proplist->al_count)
+ return (DLADM_STATUS_OK);
+
+ for (i = 0; i < DLADM_MAX_FLOWPROPS; i++) {
+ fprop_desc_t *pdp = &prop_table[i];
+ val_desc_t *vdp;
+
+ vdp = malloc(sizeof (val_desc_t) * aip->ai_count);
+ if (vdp == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ if (strcasecmp(aip->ai_name, pdp->pd_name) != 0)
+ continue;
+
+ if (aip->ai_val == NULL)
+ return (DLADM_STATUS_BADARG);
+
+ /* Check property value */
+ if (pdp->pd_check != NULL) {
+ status = pdp->pd_check(pdp, aip->ai_val,
+ aip->ai_count, &vdp);
+ } else {
+ status = DLADM_STATUS_BADARG;
+ }
+
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ for (j = 0; j < DLADM_MAX_RSRC_PROP; j++) {
+ resource_prop_t *rpp = &rsrc_prop_table[j];
+
+ if (strcasecmp(aip->ai_name, rpp->rp_name) != 0)
+ continue;
+
+ /* Extract kernel structure */
+ if (rpp->rp_extract != NULL) {
+ status = rpp->rp_extract(vdp, val,
+ aip->ai_count);
+ } else {
+ status = DLADM_STATUS_BADARG;
+ }
+ break;
+ }
+
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ break;
+ }
+ return (status);
+}
+
+/*
+ * Extract properties from a proplist and convert to mac_resource_props_t.
+ */
+dladm_status_t
+dladm_flow_proplist_extract(dladm_arg_list_t *proplist,
+ mac_resource_props_t *mrp)
+{
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ status = i_dladm_flow_proplist_extract_one(proplist, "maxbw", mrp);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ status = i_dladm_flow_proplist_extract_one(proplist, "priority", mrp);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ return (status);
+}
+
+dladm_status_t
+i_dladm_set_flow_proplist_db(char *flow, dladm_arg_list_t *proplist)
+{
+ dladm_status_t status, ssave = DLADM_STATUS_OK;
+ dladm_arg_info_t ai;
+ int i;
+
+ for (i = 0; i < proplist->al_count; i++) {
+ ai = proplist->al_info[i];
+ status = i_dladm_set_flowprop_db(flow, ai.ai_name,
+ ai.ai_val, ai.ai_count);
+ if (status != DLADM_STATUS_OK)
+ ssave = status;
+ }
+ return (ssave);
+}
diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c
index fa588df066..cc6bf542f7 100644
--- a/usr/src/lib/libdladm/common/libdladm.c
+++ b/usr/src/lib/libdladm/common/libdladm.c
@@ -29,6 +29,7 @@
#include <fcntl.h>
#include <strings.h>
#include <dirent.h>
+#include <stdlib.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <libdladm_impl.h>
@@ -89,7 +90,7 @@ dladm_status2str(dladm_status_t status, char *buf)
s = "I/O error";
break;
case DLADM_STATUS_TEMPONLY:
- s = "change cannot be persistent, specify -t please";
+ s = "change cannot be persistent";
break;
case DLADM_STATUS_TIMEDOUT:
s = "operation timed out";
@@ -127,6 +128,117 @@ dladm_status2str(dladm_status_t status, char *buf)
case DLADM_STATUS_NONOTIF:
s = "link notification is not supported";
break;
+ case DLADM_STATUS_BADTIMEVAL:
+ s = "invalid time range";
+ break;
+ case DLADM_STATUS_INVALIDMACADDR:
+ s = "invalid MAC address value";
+ break;
+ case DLADM_STATUS_INVALIDMACADDRNIC:
+ s = "MAC address reserved for use by underlying data-link";
+ break;
+ case DLADM_STATUS_INVALIDMACADDRINUSE:
+ s = "MAC address is already in use";
+ break;
+ case DLADM_STATUS_MACFACTORYSLOTINVALID:
+ s = "invalid factory MAC address slot";
+ break;
+ case DLADM_STATUS_MACFACTORYSLOTUSED:
+ s = "factory MAC address slot already used";
+ break;
+ case DLADM_STATUS_MACFACTORYSLOTALLUSED:
+ s = "all factory MAC address slots are in use";
+ break;
+ case DLADM_STATUS_MACFACTORYNOTSUP:
+ s = "factory MAC address slots not supported";
+ break;
+ case DLADM_STATUS_INVALIDMACPREFIX:
+ s = "Invalid MAC address prefix value";
+ break;
+ case DLADM_STATUS_INVALIDMACPREFIXLEN:
+ s = "Invalid MAC address prefix length";
+ break;
+ case DLADM_STATUS_CPUMAX:
+ s = "non-existent processor ID";
+ break;
+ case DLADM_STATUS_CPUERR:
+ s = "could not determine processor status";
+ break;
+ case DLADM_STATUS_CPUNOTONLINE:
+ s = "processor not online";
+ break;
+ case DLADM_STATUS_DB_NOTFOUND:
+ s = "database not found";
+ break;
+ case DLADM_STATUS_DB_PARSE_ERR:
+ s = "database parse error";
+ break;
+ case DLADM_STATUS_PROP_PARSE_ERR:
+ s = "property parse error";
+ break;
+ case DLADM_STATUS_ATTR_PARSE_ERR:
+ s = "attribute parse error";
+ break;
+ case DLADM_STATUS_FLOW_DB_ERR:
+ s = "flow database error";
+ break;
+ case DLADM_STATUS_FLOW_DB_OPEN_ERR:
+ s = "flow database open error";
+ break;
+ case DLADM_STATUS_FLOW_DB_PARSE_ERR:
+ s = "flow database parse error";
+ break;
+ case DLADM_STATUS_FLOWPROP_DB_PARSE_ERR:
+ s = "flow property database parse error";
+ break;
+ case DLADM_STATUS_FLOW_ADD_ERR:
+ s = "flow add error";
+ break;
+ case DLADM_STATUS_FLOW_WALK_ERR:
+ s = "flow walk error";
+ break;
+ case DLADM_STATUS_FLOW_IDENTICAL:
+ s = "a flow with identical attributes exists";
+ break;
+ case DLADM_STATUS_FLOW_INCOMPATIBLE:
+ s = "flow(s) with incompatible attributes exists";
+ break;
+ case DLADM_STATUS_FLOW_EXISTS:
+ s = "link still has flows";
+ break;
+ case DLADM_STATUS_PERSIST_FLOW_EXISTS:
+ s = "persistent flow with the same name exists";
+ break;
+ case DLADM_STATUS_INVALID_IP:
+ s = "invalid IP address";
+ break;
+ case DLADM_STATUS_INVALID_PREFIXLEN:
+ s = "invalid IP prefix length";
+ break;
+ case DLADM_STATUS_INVALID_PROTOCOL:
+ s = "invalid IP protocol";
+ break;
+ case DLADM_STATUS_INVALID_PORT:
+ s = "invalid port number";
+ break;
+ case DLADM_STATUS_INVALID_DSF:
+ s = "invalid dsfield";
+ break;
+ case DLADM_STATUS_INVALID_DSFMASK:
+ s = "invalid dsfield mask";
+ break;
+ case DLADM_STATUS_INVALID_MACMARGIN:
+ s = "MTU check failed, use lower MTU or -f option";
+ break;
+ case DLADM_STATUS_BADPROP:
+ s = "invalid property";
+ break;
+ case DLADM_STATUS_MINMAXBW:
+ s = "minimum value for maxbw is 1.2M";
+ break;
+ case DLADM_STATUS_NO_HWRINGS:
+ s = "request hw rings failed";
+ break;
default:
s = "<unknown error>";
break;
@@ -169,11 +281,100 @@ dladm_errno2status(int err)
return (DLADM_STATUS_LINKBUSY);
case EAGAIN:
return (DLADM_STATUS_TRYAGAIN);
+ case ENOTEMPTY:
+ return (DLADM_STATUS_FLOW_EXISTS);
+ case EOPNOTSUPP:
+ return (DLADM_STATUS_FLOW_INCOMPATIBLE);
+ case EALREADY:
+ return (DLADM_STATUS_FLOW_IDENTICAL);
default:
return (DLADM_STATUS_FAILED);
}
}
+dladm_status_t
+dladm_str2bw(char *oarg, uint64_t *bw)
+{
+ char *endp = NULL;
+ int64_t n;
+ int mult = 1;
+
+ n = strtoull(oarg, &endp, 10);
+
+ if ((errno != 0) || (strlen(endp) > 1))
+ return (DLADM_STATUS_BADARG);
+
+ if (n < 0)
+ return (DLADM_STATUS_BADVAL);
+
+ switch (*endp) {
+ case 'k':
+ case 'K':
+ mult = 1000;
+ break;
+ case 'm':
+ case 'M':
+ case '\0':
+ mult = 1000000;
+ break;
+ case 'g':
+ case 'G':
+ mult = 1000000000;
+ break;
+ case '%':
+ /*
+ * percentages not supported for now,
+ * see RFE 6540675
+ */
+ return (DLADM_STATUS_NOTSUP);
+ default:
+ return (DLADM_STATUS_BADVAL);
+ }
+
+ *bw = n * mult;
+
+ /* check for overflow */
+ if (*bw / mult != n)
+ return (DLADM_STATUS_BADARG);
+
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Convert bandwidth in bps to a string in mpbs. For values greater
+ * than 1mbps or 1000000, print a whole mbps value. For values that
+ * have fractional Mbps in whole Kbps , print the bandwidth in a manner
+ * simlilar to a floating point format.
+ *
+ * bps string
+ * 0 0
+ * 100 0
+ * 2000 0.002
+ * 431000 0.431
+ * 1000000 1
+ * 1030000 1.030
+ * 100000000 100
+ */
+const char *
+dladm_bw2str(int64_t bw, char *buf)
+{
+ int kbps, mbps;
+
+ kbps = (bw%1000000)/1000;
+ mbps = bw/1000000;
+ if (kbps != 0) {
+ if (mbps == 0)
+ (void) snprintf(buf, DLADM_STRSIZE, "0.%03u", kbps);
+ else
+ (void) snprintf(buf, DLADM_STRSIZE, "%5u.%03u", mbps,
+ kbps);
+ } else {
+ (void) snprintf(buf, DLADM_STRSIZE, "%5u", mbps);
+ }
+
+ return (buf);
+}
+
#define LOCK_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
static int
@@ -241,6 +442,9 @@ dladm_class2str(datalink_class_t class, char *buf)
case DATALINK_CLASS_VNIC:
s = "vnic";
break;
+ case DATALINK_CLASS_ETHERSTUB:
+ s = "etherstub";
+ break;
default:
s = "unknown";
break;
@@ -491,3 +695,123 @@ dladm_valid_linkname(const char *link)
return (B_TRUE);
}
+
+/*
+ * Convert priority string to a value.
+ */
+dladm_status_t
+dladm_str2pri(char *token, mac_priority_level_t *pri)
+{
+ if (strlen(token) == strlen("low") &&
+ strncasecmp(token, "low", strlen("low")) == 0) {
+ *pri = MPL_LOW;
+ } else if (strlen(token) == strlen("medium") &&
+ strncasecmp(token, "medium", strlen("medium")) == 0) {
+ *pri = MPL_MEDIUM;
+ } else if (strlen(token) == strlen("high") &&
+ strncasecmp(token, "high", strlen("high")) == 0) {
+ *pri = MPL_HIGH;
+ } else {
+ return (DLADM_STATUS_BADVAL);
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Convert priority value to a string.
+ */
+const char *
+dladm_pri2str(mac_priority_level_t pri, char *buf)
+{
+ const char *s;
+
+ switch (pri) {
+ case MPL_LOW:
+ s = "low";
+ break;
+ case MPL_MEDIUM:
+ s = "medium";
+ break;
+ case MPL_HIGH:
+ s = "high";
+ break;
+ default:
+ s = "--";
+ break;
+ }
+ (void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
+ return (buf);
+}
+
+void
+dladm_free_args(dladm_arg_list_t *list)
+{
+ if (list != NULL) {
+ free(list->al_buf);
+ free(list);
+ }
+}
+
+dladm_status_t
+dladm_parse_args(char *str, dladm_arg_list_t **listp, boolean_t novalues)
+{
+ dladm_arg_list_t *list;
+ dladm_arg_info_t *aip;
+ char *buf, *curr;
+ int len, i;
+
+ list = malloc(sizeof (dladm_arg_list_t));
+ if (list == NULL)
+ return (dladm_errno2status(errno));
+
+ list->al_count = 0;
+ list->al_buf = buf = strdup(str);
+ if (buf == NULL)
+ return (dladm_errno2status(errno));
+
+ curr = buf;
+ len = strlen(buf);
+ aip = NULL;
+ for (i = 0; i < len; i++) {
+ char c = buf[i];
+ boolean_t match = (c == '=' || c == ',');
+
+ if (!match && i != len - 1)
+ continue;
+
+ if (match) {
+ buf[i] = '\0';
+ if (*curr == '\0')
+ goto fail;
+ }
+
+ if (aip != NULL && c != '=') {
+ if (aip->ai_count > DLADM_MAX_ARG_VALS)
+ goto fail;
+
+ if (novalues)
+ goto fail;
+
+ aip->ai_val[aip->ai_count] = curr;
+ aip->ai_count++;
+ } else {
+ if (list->al_count > DLADM_MAX_ARG_VALS)
+ goto fail;
+
+ aip = &list->al_info[list->al_count];
+ aip->ai_name = curr;
+ aip->ai_count = 0;
+ list->al_count++;
+ if (c == ',')
+ aip = NULL;
+ }
+ curr = buf + i + 1;
+ }
+
+ *listp = list;
+ return (DLADM_STATUS_OK);
+
+fail:
+ dladm_free_args(list);
+ return (DLADM_STATUS_FAILED);
+}
diff --git a/usr/src/lib/libdladm/common/libdladm.h b/usr/src/lib/libdladm/common/libdladm.h
index df69a54615..a76245d478 100644
--- a/usr/src/lib/libdladm/common/libdladm.h
+++ b/usr/src/lib/libdladm/common/libdladm.h
@@ -26,7 +26,7 @@
#ifndef _LIBDLADM_H
#define _LIBDLADM_H
-#include <sys/dls.h>
+#include <sys/dls_mgmt.h>
#include <sys/dlpi.h>
/*
@@ -60,16 +60,28 @@ extern "C" {
*
* - DLADM_OPT_PREFIX:
* The function requests to generate a link name using the specified prefix.
+ *
+ * - DLADM_OPT_VLAN:
+ * Signifies VLAN creation code path
+ *
+ * - DLADM_OPT_HWRINGS:
+ * Requires a hardware group of rings when creating a vnic.
*/
#define DLADM_OPT_ACTIVE 0x00000001
#define DLADM_OPT_PERSIST 0x00000002
#define DLADM_OPT_CREATE 0x00000004
#define DLADM_OPT_FORCE 0x00000008
#define DLADM_OPT_PREFIX 0x00000010
+#define DLADM_OPT_ANCHOR 0x00000020
+#define DLADM_OPT_VLAN 0x00000040
+#define DLADM_OPT_HWRINGS 0x00000080
#define DLADM_WALK_TERMINATE 0
#define DLADM_WALK_CONTINUE -1
+#define DLADM_MAX_ARG_CNT 32
+#define DLADM_MAX_ARG_VALS 32
+
typedef enum {
DLADM_STATUS_OK = 0,
DLADM_STATUS_BADARG,
@@ -99,7 +111,44 @@ typedef enum {
DLADM_STATUS_VIDINVAL,
DLADM_STATUS_NONOTIF,
DLADM_STATUS_TRYAGAIN,
- DLADM_STATUS_NOTDEFINED
+ DLADM_STATUS_BADTIMEVAL,
+ DLADM_STATUS_INVALIDMACADDR,
+ DLADM_STATUS_INVALIDMACADDRNIC,
+ DLADM_STATUS_INVALIDMACADDRINUSE,
+ DLADM_STATUS_MACFACTORYSLOTINVALID,
+ DLADM_STATUS_MACFACTORYSLOTUSED,
+ DLADM_STATUS_MACFACTORYSLOTALLUSED,
+ DLADM_STATUS_MACFACTORYNOTSUP,
+ DLADM_STATUS_INVALIDMACPREFIX,
+ DLADM_STATUS_INVALIDMACPREFIXLEN,
+ DLADM_STATUS_CPUMAX,
+ DLADM_STATUS_CPUERR,
+ DLADM_STATUS_CPUNOTONLINE,
+ DLADM_STATUS_DB_NOTFOUND,
+ DLADM_STATUS_DB_PARSE_ERR,
+ DLADM_STATUS_PROP_PARSE_ERR,
+ DLADM_STATUS_ATTR_PARSE_ERR,
+ DLADM_STATUS_FLOW_DB_ERR,
+ DLADM_STATUS_FLOW_DB_OPEN_ERR,
+ DLADM_STATUS_FLOW_DB_PARSE_ERR,
+ DLADM_STATUS_FLOWPROP_DB_PARSE_ERR,
+ DLADM_STATUS_FLOW_ADD_ERR,
+ DLADM_STATUS_FLOW_WALK_ERR,
+ DLADM_STATUS_FLOW_IDENTICAL,
+ DLADM_STATUS_FLOW_INCOMPATIBLE,
+ DLADM_STATUS_FLOW_EXISTS,
+ DLADM_STATUS_PERSIST_FLOW_EXISTS,
+ DLADM_STATUS_INVALID_IP,
+ DLADM_STATUS_INVALID_PREFIXLEN,
+ DLADM_STATUS_INVALID_PROTOCOL,
+ DLADM_STATUS_INVALID_PORT,
+ DLADM_STATUS_INVALID_DSF,
+ DLADM_STATUS_INVALID_DSFMASK,
+ DLADM_STATUS_INVALID_MACMARGIN,
+ DLADM_STATUS_NOTDEFINED,
+ DLADM_STATUS_BADPROP,
+ DLADM_STATUS_MINMAXBW,
+ DLADM_STATUS_NO_HWRINGS
} dladm_status_t;
typedef enum {
@@ -111,11 +160,63 @@ typedef enum {
typedef int dladm_conf_t;
#define DLADM_INVALID_CONF 0
+typedef struct dladm_arg_info {
+ const char *ai_name;
+ char *ai_val[DLADM_MAX_ARG_VALS];
+ uint_t ai_count;
+} dladm_arg_info_t;
+
+typedef struct dladm_arg_list {
+ dladm_arg_info_t al_info[DLADM_MAX_ARG_CNT];
+ uint_t al_count;
+ char *al_buf;
+} dladm_arg_list_t;
+
+typedef enum {
+ DLADM_LOGTYPE_LINK = 1,
+ DLADM_LOGTYPE_FLOW
+} dladm_logtype_t;
+
+typedef struct dladm_usage {
+ char du_name[MAXLINKNAMELEN];
+ uint64_t du_duration;
+ uint64_t du_stime;
+ uint64_t du_etime;
+ uint64_t du_ipackets;
+ uint64_t du_rbytes;
+ uint64_t du_opackets;
+ uint64_t du_obytes;
+ uint64_t du_bandwidth;
+ boolean_t du_last;
+} dladm_usage_t;
+
extern const char *dladm_status2str(dladm_status_t, char *);
extern dladm_status_t dladm_set_rootdir(const char *);
extern const char *dladm_class2str(datalink_class_t, char *);
extern const char *dladm_media2str(uint32_t, char *);
extern boolean_t dladm_valid_linkname(const char *);
+extern dladm_status_t dladm_str2bw(char *, uint64_t *);
+extern const char *dladm_bw2str(int64_t, char *);
+
+extern dladm_status_t dladm_parse_flow_props(char *, dladm_arg_list_t **,
+ boolean_t);
+extern dladm_status_t dladm_parse_link_props(char *, dladm_arg_list_t **,
+ boolean_t);
+extern void dladm_free_props(dladm_arg_list_t *);
+extern dladm_status_t dladm_parse_flow_attrs(char *, dladm_arg_list_t **,
+ boolean_t);
+extern void dladm_free_attrs(dladm_arg_list_t *);
+
+extern dladm_status_t dladm_start_usagelog(dladm_logtype_t, uint_t);
+extern dladm_status_t dladm_stop_usagelog(dladm_logtype_t);
+extern dladm_status_t dladm_walk_usage_res(int (*)(dladm_usage_t *, void *),
+ int, char *, char *, char *, char *, void *);
+extern dladm_status_t dladm_walk_usage_time(int (*)(dladm_usage_t *, void *),
+ int, char *, char *, char *, void *);
+extern dladm_status_t dladm_usage_summary(int (*)(dladm_usage_t *, void *),
+ int, char *, void *);
+extern dladm_status_t dladm_usage_dates(int (*)(dladm_usage_t *, void *),
+ int, char *, char *, void *);
#ifdef __cplusplus
}
diff --git a/usr/src/lib/libdladm/common/libdladm_impl.h b/usr/src/lib/libdladm/common/libdladm_impl.h
index d4a5a52445..41f09b3a46 100644
--- a/usr/src/lib/libdladm/common/libdladm_impl.h
+++ b/usr/src/lib/libdladm/common/libdladm_impl.h
@@ -36,18 +36,17 @@ extern "C" {
#define MAXLINELEN 1024
#define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
-typedef struct val_desc {
- char *vd_name;
- uintptr_t vd_val;
-} val_desc_t;
-
-#define VALCNT(vals) (sizeof ((vals)) / sizeof (val_desc_t))
-
extern dladm_status_t dladm_errno2status(int);
extern dladm_status_t i_dladm_rw_db(const char *, mode_t,
dladm_status_t (*)(void *, FILE *, FILE *),
void *, boolean_t);
+extern const char *dladm_pri2str(mac_priority_level_t, char *);
+extern dladm_status_t dladm_str2pri(char *, mac_priority_level_t *);
+extern dladm_status_t dladm_parse_args(char *, dladm_arg_list_t **,
+ boolean_t);
+extern void dladm_free_args(dladm_arg_list_t *);
+
/*
* Link attributes persisted by dlmgmtd.
*/
@@ -65,11 +64,64 @@ extern dladm_status_t i_dladm_rw_db(const char *, mode_t,
#define FPORTS "portnames" /* string */
#define FPOLICY "policy" /* uint64_t */
#define FFIXMACADDR "fix_macaddr" /* boolean_t */
-#define FMACADDR "macaddr" /* string */
#define FFORCE "force" /* boolean_t */
#define FLACPMODE "lacp_mode" /* uint64_t */
#define FLACPTIMER "lacp_timer" /* uint64_t */
+/*
+ * Set for VNICs only
+ */
+#define FMADDRTYPE "maddrtype" /* uint64_t */
+#define FMADDRLEN "maddrlen" /* uint64_t */
+#define FMADDRSLOT "maddrslot" /* uint64_t */
+#define FMADDRPREFIXLEN "maddrpreflen" /* uint64_t */
+#define FHWRINGS "hwrings" /* boolean_t */
+
+/*
+ * Common fields
+ */
+#define FMACADDR "macaddr" /* string */
+
+/*
+ * Data structures used for implementing temporary properties
+ */
+
+typedef struct val_desc {
+ char *vd_name;
+ uintptr_t vd_val;
+} val_desc_t;
+
+#define VALCNT(vals) (sizeof ((vals)) / sizeof (val_desc_t))
+
+extern dladm_status_t dladm_link_proplist_extract(dladm_arg_list_t *,
+ mac_resource_props_t *);
+
+extern dladm_status_t dladm_flow_proplist_extract(dladm_arg_list_t *,
+ mac_resource_props_t *);
+
+/*
+ * The prop extract() callback.
+ *
+ * rp_extract extracts the kernel structure from the val_desc_t created
+ * by the pd_check function.
+ */
+typedef dladm_status_t rp_extractf_t(val_desc_t *propval, void *arg,
+ uint_t cnt);
+extern rp_extractf_t do_extract_maxbw, do_extract_priority,
+ do_extract_cpus;
+
+typedef struct resource_prop_s {
+ /*
+ * resource property name
+ */
+ char *rp_name;
+
+ /*
+ * callback to extract kernel structure
+ */
+ rp_extractf_t *rp_extract;
+} resource_prop_t;
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libdladm/common/libdlaggr.c b/usr/src/lib/libdladm/common/libdlaggr.c
index dba84441ea..5a155fcad9 100644
--- a/usr/src/lib/libdladm/common/libdlaggr.c
+++ b/usr/src/lib/libdladm/common/libdlaggr.c
@@ -37,6 +37,7 @@
#include <libintl.h>
#include <net/if_types.h>
#include <net/if_dl.h>
+#include <sys/dld.h>
#include <libdllink.h>
#include <libdlvlan.h>
#include <libdlaggr.h>
@@ -1110,7 +1111,7 @@ dladm_aggr_create(const char *name, uint16_t key, uint32_t nports,
for (i = 0; i < nports; i++) {
if ((dladm_datalink_id2info(ports[i].lp_linkid, NULL,
&class, &media, NULL, 0) != DLADM_STATUS_OK) ||
- (class != DATALINK_CLASS_PHYS) && (media != DL_ETHER)) {
+ !((class == DATALINK_CLASS_PHYS) && (media == DL_ETHER))) {
return (DLADM_STATUS_BADARG);
}
}
diff --git a/usr/src/lib/libdladm/common/libdlflow.c b/usr/src/lib/libdladm/common/libdlflow.c
new file mode 100644
index 0000000000..3ec77705a7
--- /dev/null
+++ b/usr/src/lib/libdladm/common/libdlflow.c
@@ -0,0 +1,903 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ethernet.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <libintl.h>
+#include <netdb.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <libdlflow.h>
+#include <libdlflow_impl.h>
+#include <libdladm_impl.h>
+
+/* minimum buffer size for DLDIOCWALKFLOW */
+#define MIN_INFO_SIZE (4 * 1024)
+
+#define DLADM_FLOW_DB "/etc/dladm/flowadm.conf"
+#define DLADM_FLOW_DB_TMP "/etc/dladm/flowadm.conf.new"
+#define DLADM_FLOW_DB_LOCK "/tmp/flowadm.conf.lock"
+
+#define DLADM_FLOW_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
+#define DLADM_FLOW_DB_OWNER UID_DLADM
+#define DLADM_FLOW_DB_GROUP GID_SYS
+
+#define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
+#define MAXLINELEN 1024
+#define MAXPATHLEN 1024
+
+#define V4_PART_OF_V6(v6) ((v6)._S6_un._S6_u32[3])
+
+/* database file parameters */
+static const char *BW_LIMIT = "bw_limit";
+static const char *PRIORITY = "priority";
+static const char *LOCAL_IP_ADDR = "local_ip";
+static const char *REMOTE_IP_ADDR = "remote_ip";
+static const char *TRANSPORT = "transport";
+static const char *LOCAL_PORT = "local_port";
+static const char *DSFIELD = "dsfield";
+
+/*
+ * Open and lock the flowadm configuration file lock. The lock is
+ * acquired as a reader (F_RDLCK) or writer (F_WRLCK).
+ */
+static int
+i_dladm_flow_lock_db(short type)
+{
+ int lock_fd;
+ struct flock lock;
+
+ if ((lock_fd = open(DLADM_FLOW_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC,
+ DLADM_FLOW_DB_PERMS)) < 0)
+ return (-1);
+
+ lock.l_type = type;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+
+ if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
+ (void) close(lock_fd);
+ (void) unlink(DLADM_FLOW_DB_LOCK);
+ return (-1);
+ }
+ return (lock_fd);
+}
+
+/*
+ * Unlock and close the specified file.
+ */
+static void
+i_dladm_flow_unlock_db(int fd)
+{
+ struct flock lock;
+
+ if (fd < 0)
+ return;
+
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+
+ (void) fcntl(fd, F_SETLKW, &lock);
+ (void) close(fd);
+ (void) unlink(DLADM_FLOW_DB_LOCK);
+}
+
+/*
+ * Parse one line of the link flowadm DB
+ * Returns -1 on failure, 0 on success.
+ */
+dladm_status_t
+dladm_flow_parse_db(char *line, dld_flowinfo_t *attr)
+{
+ char *token;
+ char *value, *name = NULL;
+ char *endp = NULL;
+ char *lasts = NULL;
+ dladm_status_t status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
+
+ bzero(attr, sizeof (*attr));
+
+ /* flow name */
+ if ((token = strtok_r(line, " \t", &lasts)) == NULL)
+ goto done;
+
+ if (strlcpy(attr->fi_flowname, token, MAXNAMELEN) >= MAXNAMELEN)
+ goto done;
+
+ /* resource control and flow descriptor parameters */
+ while ((token = strtok_r(NULL, " \t", &lasts)) != NULL) {
+ if ((name = strdup(token)) == NULL)
+ goto done;
+
+ (void) strtok(name, "=");
+ value = strtok(NULL, "=");
+ if (value == NULL)
+ goto done;
+
+ if (strcmp(name, "linkid") == 0) {
+ if ((attr->fi_linkid =
+ (uint32_t)strtol(value, &endp, 10)) ==
+ DATALINK_INVALID_LINKID)
+ goto done;
+
+ } else if (strcmp(name, BW_LIMIT) == 0) {
+ attr->fi_resource_props.mrp_mask |=
+ MRP_MAXBW;
+ attr->fi_resource_props.mrp_maxbw =
+ (uint64_t)strtol(value, &endp, 0);
+
+ } else if (strcmp(name, PRIORITY) == 0) {
+ attr->fi_resource_props.mrp_mask |= MRP_PRIORITY;
+ status = dladm_str2pri(value,
+ &attr->fi_resource_props.mrp_priority);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ } else if (strcmp(name, DSFIELD) == 0) {
+ status = do_check_dsfield(value,
+ &attr->fi_flow_desc);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ } else if (strcmp(name, LOCAL_IP_ADDR) == 0) {
+ status = do_check_ip_addr(value, B_TRUE,
+ &attr->fi_flow_desc);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ } else if (strcmp(name, REMOTE_IP_ADDR) == 0) {
+ status = do_check_ip_addr(value, B_FALSE,
+ &attr->fi_flow_desc);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ } else if (strcmp(name, TRANSPORT) == 0) {
+ attr->fi_flow_desc.fd_mask |= FLOW_IP_PROTOCOL;
+ attr->fi_flow_desc.fd_protocol =
+ (uint8_t)strtol(value, &endp, 0);
+
+ } else if (strcmp(name, LOCAL_PORT) == 0) {
+ attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_LOCAL;
+ attr->fi_flow_desc.fd_local_port =
+ (uint16_t)strtol(value, &endp, 10);
+ attr->fi_flow_desc.fd_local_port =
+ htons(attr->fi_flow_desc.fd_local_port);
+ }
+ free(name);
+ name = NULL;
+ }
+ if (attr->fi_linkid != DATALINK_INVALID_LINKID)
+ status = DLADM_STATUS_OK;
+done:
+ free(name);
+ return (status);
+}
+
+#define FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1);
+
+/*
+ * Write the attribute of a group to the specified file. Returns 0 on
+ * success, -1 on failure.
+ */
+static int
+i_dladm_flow_fput_grp(FILE *fp, dld_flowinfo_t *attr)
+{
+
+ FPRINTF_ERR(fprintf(fp, "%s\tlinkid=%d\t",
+ attr->fi_flowname, attr->fi_linkid));
+
+ /* flow policy */
+ if (attr->fi_resource_props.mrp_mask & MRP_MAXBW)
+ FPRINTF_ERR(fprintf(fp, "%s=%" PRIu64 "\t", BW_LIMIT,
+ attr->fi_resource_props.mrp_maxbw));
+
+ if (attr->fi_resource_props.mrp_mask & MRP_PRIORITY)
+ FPRINTF_ERR(fprintf(fp, "%s=%d\t", PRIORITY,
+ attr->fi_resource_props.mrp_priority));
+
+ /* flow descriptor */
+ if (attr->fi_flow_desc.fd_mask & FLOW_IP_DSFIELD)
+ FPRINTF_ERR(fprintf(fp, "%s=%x:%x\t", DSFIELD,
+ attr->fi_flow_desc.fd_dsfield,
+ attr->fi_flow_desc.fd_dsfield_mask));
+
+ if (attr->fi_flow_desc.fd_mask & FLOW_IP_LOCAL) {
+ char abuf[INET6_ADDRSTRLEN], *ap;
+ struct in_addr ipaddr;
+ int prefix_len, prefix_max;
+
+ if (attr->fi_flow_desc.fd_ipversion != 6) {
+ ipaddr.s_addr =
+ attr->fi_flow_desc.
+ fd_local_addr._S6_un._S6_u32[3];
+
+ ap = inet_ntoa(ipaddr);
+ prefix_max = IP_ABITS;
+ } else {
+ (void) inet_ntop(AF_INET6,
+ &attr->fi_flow_desc.fd_local_addr,
+ abuf, INET6_ADDRSTRLEN);
+
+ ap = abuf;
+ prefix_max = IPV6_ABITS;
+ }
+ (void) dladm_mask2prefixlen(
+ &attr->fi_flow_desc.fd_local_netmask, prefix_max,
+ &prefix_len);
+
+ FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", LOCAL_IP_ADDR,
+ ap, prefix_len));
+ }
+ if (attr->fi_flow_desc.fd_mask & FLOW_IP_REMOTE) {
+ char abuf[INET6_ADDRSTRLEN], *ap;
+ struct in_addr ipaddr;
+ int prefix_len, prefix_max;
+
+ if (attr->fi_flow_desc.fd_ipversion != 6) {
+ ipaddr.s_addr =
+ attr->fi_flow_desc.
+ fd_remote_addr._S6_un._S6_u32[3];
+
+ ap = inet_ntoa(ipaddr);
+ prefix_max = IP_ABITS;
+ } else {
+ (void) inet_ntop(AF_INET6,
+ &(attr->fi_flow_desc.fd_remote_addr),
+ abuf, INET6_ADDRSTRLEN);
+
+ ap = abuf;
+ prefix_max = IPV6_ABITS;
+ }
+ (void) dladm_mask2prefixlen(
+ &attr->fi_flow_desc.fd_remote_netmask, prefix_max,
+ &prefix_len);
+
+ FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", REMOTE_IP_ADDR,
+ ap, prefix_len));
+ }
+ if (attr->fi_flow_desc.fd_mask & FLOW_IP_PROTOCOL)
+ FPRINTF_ERR(fprintf(fp, "%s=%d\t", TRANSPORT,
+ attr->fi_flow_desc.fd_protocol));
+
+ if (attr->fi_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL)
+ FPRINTF_ERR(fprintf(fp, "%s=%d\t", LOCAL_PORT,
+ ntohs(attr->fi_flow_desc.fd_local_port)));
+
+ FPRINTF_ERR(fprintf(fp, "\n"));
+
+ return (0);
+
+}
+
+static dladm_status_t
+i_dladm_flow_walk_rw_db(int (*fn)(void *, dld_flowinfo_t *),
+ void *arg,
+ const char *root)
+{
+ FILE *fp, *nfp;
+ int nfd, fn_rc, lock_fd;
+ char line[MAXLINELEN];
+ dld_flowinfo_t attr;
+ char *db_file, *tmp_db_file;
+ char db_file_buf[MAXPATHLEN];
+ char tmp_db_file_buf[MAXPATHLEN];
+ dladm_status_t status = DLADM_STATUS_FLOW_DB_ERR;
+
+ if (root == NULL) {
+ db_file = DLADM_FLOW_DB;
+ tmp_db_file = DLADM_FLOW_DB_TMP;
+ } else {
+ (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
+ DLADM_FLOW_DB);
+ (void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root,
+ DLADM_FLOW_DB_TMP);
+ db_file = db_file_buf;
+ tmp_db_file = tmp_db_file_buf;
+ }
+
+ if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0)
+ return (DLADM_STATUS_FLOW_DB_ERR);
+
+ if ((fp = fopen(db_file, "r")) == NULL) {
+ i_dladm_flow_unlock_db(lock_fd);
+ return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
+ }
+
+ if ((nfd = open(tmp_db_file, O_WRONLY|O_CREAT|O_TRUNC,
+ DLADM_FLOW_DB_PERMS)) == -1) {
+ (void) fclose(fp);
+ i_dladm_flow_unlock_db(lock_fd);
+ return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
+ }
+
+ if ((nfp = fdopen(nfd, "w")) == NULL) {
+ (void) close(nfd);
+ (void) fclose(fp);
+ (void) unlink(tmp_db_file);
+ i_dladm_flow_unlock_db(lock_fd);
+ return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
+ }
+
+ while (fgets(line, MAXLINELEN, fp) != NULL) {
+
+ /* skip comments */
+ if (BLANK_LINE(line)) {
+ if (fputs(line, nfp) == EOF)
+ goto failed;
+ continue;
+ }
+ (void) strtok(line, " \n");
+
+ if ((status = dladm_flow_parse_db(line, &attr)) !=
+ DLADM_STATUS_OK)
+ goto failed;
+
+ fn_rc = fn(arg, &attr);
+
+ switch (fn_rc) {
+ case -1:
+ /* failure, stop walking */
+ goto failed;
+ case 0:
+ /*
+ * Success, write group attributes, which could
+ * have been modified by fn().
+ */
+ if (i_dladm_flow_fput_grp(nfp, &attr) != 0)
+ goto failed;
+ break;
+ case 1:
+ /* skip current group */
+ break;
+ }
+ }
+ if (fchmod(nfd, DLADM_FLOW_DB_PERMS) == -1)
+ goto failed;
+
+ if (fchown(nfd, DLADM_FLOW_DB_OWNER, DLADM_FLOW_DB_GROUP) == -1)
+ goto failed;
+
+ if (fflush(nfp) == EOF)
+ goto failed;
+
+ (void) fclose(fp);
+ (void) fclose(nfp);
+
+ if (rename(tmp_db_file, db_file) == -1) {
+ (void) unlink(tmp_db_file);
+ i_dladm_flow_unlock_db(lock_fd);
+ return (DLADM_STATUS_FLOW_DB_ERR);
+ }
+ i_dladm_flow_unlock_db(lock_fd);
+ return (DLADM_STATUS_OK);
+
+failed:
+ (void) fclose(fp);
+ (void) fclose(nfp);
+ (void) unlink(tmp_db_file);
+ i_dladm_flow_unlock_db(lock_fd);
+
+ return (status);
+}
+
+/*
+ * Remove existing flow from DB.
+ */
+
+typedef struct remove_db_state {
+ dld_flowinfo_t rs_newattr;
+ dld_flowinfo_t rs_oldattr;
+ boolean_t rs_found;
+} remove_db_state_t;
+
+static int
+i_dladm_flow_remove_db_fn(void *arg, dld_flowinfo_t *grp)
+{
+ remove_db_state_t *state = (remove_db_state_t *)arg;
+ dld_flowinfo_t *attr = &state->rs_newattr;
+
+ if ((strcmp(grp->fi_flowname, attr->fi_flowname)) != 0)
+ return (0);
+ else {
+ bcopy(grp, &state->rs_oldattr,
+ sizeof (dld_flowinfo_t));
+ state->rs_found = B_TRUE;
+ return (1);
+ }
+}
+
+/* ARGSUSED */
+static int
+i_dladm_flow_remove_db(remove_db_state_t *state, const char *root)
+{
+ if (i_dladm_flow_walk_rw_db(i_dladm_flow_remove_db_fn, state, root)
+ != 0)
+ return (-1);
+
+ if (!state->rs_found) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Create a flow in the DB.
+ */
+
+typedef struct modify_db_state {
+ dld_flowinfo_t ms_newattr;
+ dld_flowinfo_t ms_oldattr;
+ boolean_t ms_found;
+} modify_db_state_t;
+
+static dladm_status_t
+i_dladm_flow_create_db(dld_flowinfo_t *attr, const char *root)
+{
+ FILE *fp;
+ char line[MAXLINELEN];
+ char *db_file;
+ char db_file_buf[MAXPATHLEN];
+ int lock_fd;
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ if (root == NULL) {
+ db_file = DLADM_FLOW_DB;
+ } else {
+ (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
+ DLADM_FLOW_DB);
+ db_file = db_file_buf;
+ }
+
+ if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0)
+ return (DLADM_STATUS_FLOW_DB_ERR);
+
+ if ((fp = fopen(db_file, "r+")) == NULL &&
+ (fp = fopen(db_file, "w")) == NULL) {
+ i_dladm_flow_unlock_db(lock_fd);
+ return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
+ }
+
+ /* look for existing group with same flowname */
+ while (fgets(line, MAXLINELEN, fp) != NULL) {
+ char *holder, *lasts;
+
+ /* skip comments */
+ if (BLANK_LINE(line))
+ continue;
+
+ /* ignore corrupted lines */
+ holder = strtok_r(line, " \t", &lasts);
+ if (holder == NULL)
+ continue;
+
+ /* flow id */
+ if (strcmp(holder, attr->fi_flowname) == 0) {
+ /* group with flow id already exists */
+ status = DLADM_STATUS_PERSIST_FLOW_EXISTS;
+ goto failed;
+ }
+ }
+ /*
+ * If we get here, we've verified that no existing group with
+ * the same flow id already exists. Its now time to add the new
+ * group to the DB.
+ */
+ if (i_dladm_flow_fput_grp(fp, attr) != 0)
+ status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
+
+failed:
+ (void) fclose(fp);
+ i_dladm_flow_unlock_db(lock_fd);
+ return (status);
+}
+
+static dladm_status_t
+i_dladm_flow_add(char *flowname, datalink_id_t linkid, flow_desc_t *flowdesc,
+ mac_resource_props_t *mrp)
+{
+ dld_ioc_addflow_t attr;
+ int fd;
+
+ /* create flow */
+ bzero(&attr, sizeof (attr));
+ bcopy(flowdesc, &attr.af_flow_desc, sizeof (flow_desc_t));
+ if (mrp != NULL) {
+ bcopy(mrp, &attr.af_resource_props,
+ sizeof (mac_resource_props_t));
+ }
+
+ (void) strlcpy(attr.af_name, flowname, sizeof (attr.af_name));
+ attr.af_linkid = linkid;
+
+ fd = open(DLD_CONTROL_DEV, O_RDWR);
+ if (fd < 0)
+ return (dladm_errno2status(errno));
+
+ if (ioctl(fd, DLDIOC_ADDFLOW, &attr) < 0) {
+ (void) close(fd);
+ return (dladm_errno2status(errno));
+ }
+
+ (void) close(fd);
+
+ return (DLADM_STATUS_OK);
+}
+
+static dladm_status_t
+i_dladm_flow_remove(char *flowname)
+{
+ dld_ioc_removeflow_t attr;
+ int fd;
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ (void) strlcpy(attr.rf_name, flowname,
+ sizeof (attr.rf_name));
+
+ fd = open(DLD_CONTROL_DEV, O_RDWR);
+ if (fd < 0)
+ return (dladm_errno2status(errno));
+
+ if (ioctl(fd, DLDIOC_REMOVEFLOW, &attr) < 0)
+ status = dladm_errno2status(errno);
+
+ (void) close(fd);
+
+ return (status);
+}
+
+
+/* ARGSUSED */
+dladm_status_t
+dladm_flow_add(datalink_id_t linkid, dladm_arg_list_t *attrlist,
+ dladm_arg_list_t *proplist, char *flowname, boolean_t tempop,
+ const char *root)
+{
+ dld_flowinfo_t db_attr;
+ flow_desc_t flowdesc;
+ mac_resource_props_t mrp;
+ dladm_status_t status;
+
+ /* Extract flow attributes from attrlist */
+ bzero(&flowdesc, sizeof (flow_desc_t));
+ if (attrlist != NULL && (status = dladm_flow_attrlist_extract(attrlist,
+ &flowdesc)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+
+ /* Extract resource_ctl and cpu_list from proplist */
+ bzero(&mrp, sizeof (mac_resource_props_t));
+ if (proplist != NULL && (status = dladm_flow_proplist_extract(proplist,
+ &mrp)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+
+ /* Add flow in kernel */
+ status = i_dladm_flow_add(flowname, linkid, &flowdesc, &mrp);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ /* Add flow to DB */
+ if (!tempop) {
+ bzero(&db_attr, sizeof (db_attr));
+ bcopy(&flowdesc, &db_attr.fi_flow_desc, sizeof (flow_desc_t));
+ (void) strlcpy(db_attr.fi_flowname, flowname,
+ sizeof (db_attr.fi_flowname));
+ db_attr.fi_linkid = linkid;
+
+ if ((status = i_dladm_flow_create_db(&db_attr, root)) !=
+ DLADM_STATUS_OK) {
+ (void) i_dladm_flow_remove(flowname);
+ return (status);
+ }
+ /* set flow properties */
+ if (proplist != NULL) {
+ status = i_dladm_set_flow_proplist_db(flowname,
+ proplist);
+ if (status != DLADM_STATUS_OK) {
+ (void) i_dladm_flow_remove(flowname);
+ return (status);
+ }
+ }
+ }
+ return (status);
+}
+
+/*
+ * Remove a flow.
+ */
+/* ARGSUSED */
+dladm_status_t
+dladm_flow_remove(char *flowname, boolean_t tempop,
+ const char *root)
+{
+ remove_db_state_t state;
+ dladm_status_t status = DLADM_STATUS_OK;
+ dladm_status_t s = DLADM_STATUS_OK;
+
+ /* remove flow */
+ status = i_dladm_flow_remove(flowname);
+ if ((status != DLADM_STATUS_OK) &&
+ (tempop || status != DLADM_STATUS_NOTFOUND))
+ goto done;
+
+ /* remove flow from DB */
+ if (!tempop) {
+ bzero(&state, sizeof (state));
+ (void) strlcpy(state.rs_newattr.fi_flowname, flowname,
+ sizeof (state.rs_newattr.fi_flowname));
+ state.rs_found = B_FALSE;
+
+ /* flow DB */
+ if (i_dladm_flow_remove_db(&state, root) < 0) {
+ s = dladm_errno2status(errno);
+ goto done;
+ }
+
+ /* flow prop DB */
+ s = dladm_set_flowprop(flowname, NULL, NULL, 0,
+ DLADM_OPT_PERSIST, NULL);
+ }
+
+done:
+ if (!tempop) {
+ if (s == DLADM_STATUS_OK) {
+ if (status == DLADM_STATUS_NOTFOUND)
+ status = s;
+ } else {
+ if (s != DLADM_STATUS_NOTFOUND)
+ status = s;
+ }
+ }
+ return (status);
+}
+
+/*
+ * Get an existing flow in the DB.
+ */
+
+typedef struct get_db_state {
+ int (*gs_fn)(dladm_flow_attr_t *, void *);
+ void *gs_arg;
+ datalink_id_t gs_linkid;
+} get_db_state_t;
+
+/*
+ * For each flow which matches the linkid, copy all flow information
+ * to a new dladm_flow_attr_t structure and call the provided
+ * function. This is used to display perisistent flows from
+ * the database.
+ */
+
+static int
+i_dladm_flow_get_db_fn(void *arg, dld_flowinfo_t *grp)
+{
+ get_db_state_t *state = (get_db_state_t *)arg;
+ dladm_flow_attr_t attr;
+
+ if (grp->fi_linkid == state->gs_linkid) {
+ attr.fa_linkid = state->gs_linkid;
+ bcopy(grp->fi_flowname, &attr.fa_flowname,
+ sizeof (attr.fa_flowname));
+ bcopy(&grp->fi_flow_desc, &attr.fa_flow_desc,
+ sizeof (attr.fa_flow_desc));
+ bcopy(&grp->fi_resource_props, &attr.fa_resource_props,
+ sizeof (attr.fa_resource_props));
+ (void) state->gs_fn(&attr, state->gs_arg);
+ }
+ return (0);
+}
+
+/*
+ * Walk through the flows defined on the system and for each flow
+ * invoke <fn>(<arg>, <flow>);
+ * Currently used for show-flow.
+ */
+/* ARGSUSED */
+dladm_status_t
+dladm_walk_flow(int (*fn)(dladm_flow_attr_t *, void *),
+ datalink_id_t linkid, void *arg, boolean_t persist)
+{
+ dld_flowinfo_t *flow;
+ int i, bufsize, fd;
+ dld_ioc_walkflow_t *ioc = NULL;
+ dladm_flow_attr_t attr;
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ if (fn == NULL)
+ return (DLADM_STATUS_BADARG);
+
+ if (persist) {
+ get_db_state_t state;
+
+ bzero(&state, sizeof (state));
+
+ state.gs_linkid = linkid;
+ state.gs_fn = fn;
+ state.gs_arg = arg;
+ status = i_dladm_flow_walk_rw_db(i_dladm_flow_get_db_fn,
+ &state, NULL);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ } else {
+ if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
+ return (dladm_errno2status(errno));
+
+ bufsize = MIN_INFO_SIZE;
+ if ((ioc = calloc(1, bufsize)) == NULL) {
+ status = dladm_errno2status(errno);
+ (void) close(fd);
+ return (status);
+ }
+
+ ioc->wf_linkid = linkid;
+ ioc->wf_len = bufsize - sizeof (*ioc);
+
+ while (ioctl(fd, DLDIOC_WALKFLOW, ioc) < 0) {
+ if (errno == ENOSPC) {
+ bufsize *= 2;
+ ioc = realloc(ioc, bufsize);
+ if (ioc != NULL) {
+ ioc->wf_linkid = linkid;
+ ioc->wf_len = bufsize - sizeof (*ioc);
+ continue;
+ }
+ }
+ goto bail;
+ }
+
+ flow = (dld_flowinfo_t *)(void *)(ioc + 1);
+ for (i = 0; i < ioc->wf_nflows; i++, flow++) {
+ bzero(&attr, sizeof (attr));
+
+ attr.fa_linkid = flow->fi_linkid;
+ bcopy(&flow->fi_flowname, &attr.fa_flowname,
+ sizeof (attr.fa_flowname));
+ bcopy(&flow->fi_flow_desc, &attr.fa_flow_desc,
+ sizeof (attr.fa_flow_desc));
+ bcopy(&flow->fi_resource_props, &attr.fa_resource_props,
+ sizeof (attr.fa_resource_props));
+
+ if (fn(&attr, arg) == DLADM_WALK_TERMINATE)
+ break;
+ }
+ }
+
+bail:
+ free(ioc);
+ (void) close(fd);
+ return (status);
+}
+
+dladm_status_t
+dladm_flow_init(void)
+{
+ flow_desc_t flowdesc;
+ datalink_id_t linkid;
+ dladm_status_t s, status = DLADM_STATUS_OK;
+ char name[MAXNAMELEN];
+ char line[MAXLINELEN];
+ dld_flowinfo_t attr;
+ FILE *fp;
+
+ if ((fp = fopen(DLADM_FLOW_DB, "r")) == NULL)
+ return (DLADM_STATUS_DB_NOTFOUND);
+
+ while (fgets(line, MAXLINELEN, fp) != NULL) {
+ /* skip comments */
+ if (BLANK_LINE(line))
+ continue;
+
+ (void) strtok(line, " \n");
+
+ s = dladm_flow_parse_db(line, &attr);
+ if (s != DLADM_STATUS_OK) {
+ status = s;
+ continue;
+ }
+ bzero(&flowdesc, sizeof (flowdesc));
+ bcopy(&attr.fi_flow_desc, &flowdesc, sizeof (flow_desc_t));
+ (void) strlcpy(name, attr.fi_flowname,
+ sizeof (attr.fi_flowname));
+ linkid = attr.fi_linkid;
+
+ s = i_dladm_flow_add(name, linkid, &flowdesc, NULL);
+ if (s != DLADM_STATUS_OK)
+ status = s;
+ }
+ s = i_dladm_init_flowprop_db();
+ if (s != DLADM_STATUS_OK)
+ status = s;
+
+ (void) fclose(fp);
+ return (status);
+}
+
+dladm_status_t
+dladm_prefixlen2mask(int prefixlen, int maxlen, uchar_t *mask)
+{
+ if (prefixlen < 0 || prefixlen > maxlen)
+ return (DLADM_STATUS_BADARG);
+
+ while (prefixlen > 0) {
+ if (prefixlen >= 8) {
+ *mask++ = 0xFF;
+ prefixlen -= 8;
+ continue;
+ }
+ *mask |= 1 << (8 - prefixlen);
+ prefixlen--;
+ }
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+dladm_mask2prefixlen(in6_addr_t *mask, int plen, int *prefixlen)
+{
+ int bits;
+ int i, end;
+
+ switch (plen) {
+ case IP_ABITS:
+ end = 3;
+ break;
+ case IPV6_ABITS:
+ end = 0;
+ break;
+ default:
+ return (DLADM_STATUS_BADARG);
+ }
+
+ for (i = 3; i >= end; i--) {
+ if (mask->_S6_un._S6_u32[i] == 0) {
+ plen -= 32;
+ continue;
+ }
+ bits = ffs(ntohl(mask->_S6_un._S6_u32[i])) - 1;
+ if (bits == 0)
+ break;
+ plen -= bits;
+ }
+ *prefixlen = plen;
+ return (DLADM_STATUS_OK);
+}
diff --git a/usr/src/lib/libdladm/common/libdlflow.h b/usr/src/lib/libdladm/common/libdlflow.h
new file mode 100644
index 0000000000..d35631ba4b
--- /dev/null
+++ b/usr/src/lib/libdladm/common/libdlflow.h
@@ -0,0 +1,93 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBDLFLOW_H
+#define _LIBDLFLOW_H
+
+/*
+ * This file includes strcutures, macros and routines used by general
+ * flow administration
+ */
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/mac_flow.h>
+#include <sys/dld.h>
+#include <sys/param.h>
+#include <sys/mac.h>
+#include <libdladm.h>
+#include <libdladm_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct dladm_flow_attr {
+ datalink_id_t fa_linkid;
+ char fa_flowname[MAXNAMELEN];
+ flow_desc_t fa_flow_desc;
+ mac_resource_props_t fa_resource_props;
+ uint64_t fa_mask;
+ int fa_nattr;
+} dladm_flow_attr_t;
+
+extern dladm_status_t dladm_flow_add(datalink_id_t, dladm_arg_list_t *,
+ dladm_arg_list_t *, char *, boolean_t,
+ const char *);
+extern dladm_status_t dladm_flow_remove(char *, boolean_t, const char *);
+extern dladm_status_t dladm_flow_init(void);
+
+extern dladm_status_t dladm_flow_parse_db(char *, dld_flowinfo_t *);
+extern dladm_status_t dladm_walk_flow(int (*)(dladm_flow_attr_t *,
+ void *), datalink_id_t, void *, boolean_t);
+extern dladm_status_t dladm_flow_info(const char *, dladm_flow_attr_t *);
+
+extern dladm_status_t dladm_set_flowprop(const char *, const char *,
+ char **, uint_t, uint_t, char **);
+extern dladm_status_t dladm_get_flowprop(const char *, uint32_t,
+ const char *, char **, uint_t *);
+extern dladm_status_t dladm_walk_flowprop(int (*)(void *, const char *),
+ const char *, void *);
+
+extern void dladm_flow_attr_mask(uint64_t, dladm_flow_attr_t *);
+extern dladm_status_t dladm_flow_attr_check(dladm_arg_list_t *);
+extern dladm_status_t dladm_prefixlen2mask(int, int, uchar_t *);
+extern dladm_status_t dladm_mask2prefixlen(in6_addr_t *, int, int *);
+extern char *dladm_proto2str(uint8_t);
+extern uint8_t dladm_str2proto(const char *);
+
+extern void dladm_flow_attr_ip2str(dladm_flow_attr_t *,
+ char *, size_t);
+extern void dladm_flow_attr_proto2str(dladm_flow_attr_t *,
+ char *, size_t);
+extern void dladm_flow_attr_port2str(dladm_flow_attr_t *,
+ char *, size_t);
+extern void dladm_flow_attr_dsfield2str(dladm_flow_attr_t *,
+ char *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBDLFLOW_H */
diff --git a/usr/src/lib/libdladm/common/libdlflow_impl.h b/usr/src/lib/libdladm/common/libdlflow_impl.h
new file mode 100644
index 0000000000..09b6d55bc1
--- /dev/null
+++ b/usr/src/lib/libdladm/common/libdlflow_impl.h
@@ -0,0 +1,138 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBDLFLOW_IMPL_H
+#define _LIBDLFLOW_IMPL_H
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mac.h>
+#include <libdladm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct fprop_desc;
+struct fattr_desc;
+
+typedef dladm_status_t fpd_getf_t(const char *, char **, uint_t *);
+typedef dladm_status_t fpd_setf_t(const char *, val_desc_t *, uint_t);
+typedef dladm_status_t fpd_checkf_t(struct fprop_desc *, char **,
+ uint_t, val_desc_t **);
+
+typedef struct fprop_desc {
+ char *pd_name;
+ val_desc_t pd_defval;
+ val_desc_t *pd_modval;
+ uint_t pd_nmodval;
+ boolean_t pd_temponly;
+ fpd_setf_t *pd_set;
+ fpd_getf_t *pd_getmod;
+ fpd_getf_t *pd_get;
+ fpd_checkf_t *pd_check;
+} fprop_desc_t;
+
+typedef struct prop_table {
+ fprop_desc_t *pt_table;
+ uint_t pt_size;
+} prop_table_t;
+
+typedef enum {
+ DLADM_PROP_VAL_CURRENT = 1,
+ DLADM_PROP_VAL_DEFAULT,
+ DLADM_PROP_VAL_MODIFIABLE,
+ DLADM_PROP_VAL_PERSISTENT
+} prop_type_t;
+
+typedef dladm_status_t fad_checkf_t(char *, flow_desc_t *);
+
+extern dladm_status_t do_check_ip_addr(char *, boolean_t, flow_desc_t *);
+extern dladm_status_t do_check_dsfield(char *, flow_desc_t *);
+
+typedef struct fattr_desc {
+ const char *ad_name;
+ fad_checkf_t *ad_check;
+} fattr_desc_t;
+
+extern dladm_status_t i_dladm_get_prop_temp(const char *, prop_type_t,
+ const char *, char **, uint_t *, prop_table_t *);
+extern dladm_status_t i_dladm_set_prop_temp(const char *, const char *,
+ char **, uint_t, uint_t, char **, prop_table_t *);
+extern boolean_t i_dladm_is_prop_temponly(const char *prop_name,
+ char **, prop_table_t *);
+/*
+ * Data structures used for implementing persistent properties
+ */
+typedef struct prop_val {
+ const char *lv_name;
+ struct prop_val *lv_nextval;
+} prop_val_t;
+
+typedef struct prop_db_info {
+ const char *li_name;
+ struct prop_db_info *li_nextprop;
+ struct prop_val *li_val;
+} prop_db_info_t;
+
+typedef struct prop_db_state prop_db_state_t;
+
+typedef boolean_t (*prop_db_op_t)(prop_db_state_t *,
+ char *, prop_db_info_t *, dladm_status_t *);
+
+typedef dladm_status_t (*prop_db_initop_t)(const char *, const char *,
+ char **, uint_t, uint_t, char **);
+
+struct prop_db_state {
+ prop_db_op_t ls_op;
+ const char *ls_name;
+ const char *ls_propname;
+ char **ls_propval;
+ uint_t *ls_valcntp;
+ prop_db_initop_t ls_initop;
+};
+
+extern boolean_t process_prop_set(prop_db_state_t *lsp, char *buf,
+ prop_db_info_t *listp, dladm_status_t *statusp);
+extern boolean_t process_prop_get(prop_db_state_t *lsp, char *buf,
+ prop_db_info_t *listp, dladm_status_t *statusp);
+extern boolean_t process_prop_init(prop_db_state_t *lsp, char *buf,
+ prop_db_info_t *listp, dladm_status_t *statusp);
+extern dladm_status_t process_prop_db(void *arg, FILE *fp, FILE *nfp);
+
+extern dladm_status_t i_dladm_init_flowprop_db(void);
+extern dladm_status_t i_dladm_set_flow_proplist_db(char *,
+ dladm_arg_list_t *);
+extern dladm_status_t i_dladm_flow_check_restriction(datalink_id_t,
+ flow_desc_t *, mac_resource_props_t *, boolean_t);
+
+extern dladm_status_t dladm_flow_attrlist_extract(dladm_arg_list_t *,
+ flow_desc_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBDLFLOW_IMPL_H */
diff --git a/usr/src/lib/libdladm/common/libdllink.c b/usr/src/lib/libdladm/common/libdllink.c
index 8deed6fe76..5698409442 100644
--- a/usr/src/lib/libdladm/common/libdllink.c
+++ b/usr/src/lib/libdladm/common/libdllink.c
@@ -62,6 +62,50 @@ i_dladm_info(int fd, const datalink_id_t linkid, dladm_attr_t *dap)
return (DLADM_STATUS_OK);
}
+static dladm_status_t
+dladm_usagelog(dladm_logtype_t type, dld_ioc_usagelog_t *log_info)
+{
+ int fd;
+
+ fd = open(DLD_CONTROL_DEV, O_RDWR);
+ if (fd < 0)
+ return (DLADM_STATUS_IOERR);
+
+ if (type == DLADM_LOGTYPE_FLOW)
+ log_info->ul_type = MAC_LOGTYPE_FLOW;
+ else
+ log_info->ul_type = MAC_LOGTYPE_LINK;
+
+ if (ioctl(fd, DLDIOC_USAGELOG, log_info) < 0) {
+ (void) close(fd);
+ return (DLADM_STATUS_IOERR);
+ }
+ (void) close(fd);
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+dladm_start_usagelog(dladm_logtype_t type, uint_t interval)
+{
+ dld_ioc_usagelog_t log_info;
+
+ log_info.ul_onoff = B_TRUE;
+ log_info.ul_interval = interval;
+
+ return (dladm_usagelog(type, &log_info));
+}
+
+dladm_status_t
+dladm_stop_usagelog(dladm_logtype_t type)
+{
+ dld_ioc_usagelog_t log_info;
+
+ log_info.ul_onoff = B_FALSE;
+ log_info.ul_interval = 0;
+
+ return (dladm_usagelog(type, &log_info));
+}
+
struct i_dladm_walk_arg {
dladm_walkcb_t *fn;
void *arg;
@@ -96,6 +140,112 @@ dladm_walk(dladm_walkcb_t *fn, void *arg, datalink_class_t class,
class, dmedia, flags));
}
+#define MAXGRPPERLINK 64
+
+int
+dladm_walk_hwgrp(datalink_id_t linkid, void *arg,
+ boolean_t (*fn)(void *, dladm_hwgrp_attr_t *))
+{
+ int fd, bufsize, ret;
+ int nhwgrp = MAXGRPPERLINK;
+ dld_ioc_hwgrpget_t *iomp = NULL;
+
+ if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
+ return (-1);
+
+ bufsize = sizeof (dld_ioc_hwgrpget_t) +
+ nhwgrp * sizeof (dld_hwgrpinfo_t);
+
+ if ((iomp = (dld_ioc_hwgrpget_t *)calloc(1, bufsize)) == NULL)
+ return (-1);
+
+ iomp->dih_size = nhwgrp * sizeof (dld_hwgrpinfo_t);
+ iomp->dih_linkid = linkid;
+
+ ret = ioctl(fd, DLDIOC_GETHWGRP, iomp);
+ if (ret == 0) {
+ int i;
+ dld_hwgrpinfo_t *dhip;
+ dladm_hwgrp_attr_t attr;
+
+ dhip = (dld_hwgrpinfo_t *)(iomp + 1);
+ for (i = 0; i < iomp->dih_n_groups; i++) {
+ bzero(&attr, sizeof (attr));
+
+ (void) strlcpy(attr.hg_link_name,
+ dhip->dhi_link_name, sizeof (attr.hg_link_name));
+ attr.hg_grp_num = dhip->dhi_grp_num;
+ attr.hg_grp_type = dhip->dhi_grp_type;
+ attr.hg_n_rings = dhip->dhi_n_rings;
+ attr.hg_n_clnts = dhip->dhi_n_clnts;
+ (void) strlcpy(attr.hg_client_names,
+ dhip->dhi_clnts, sizeof (attr.hg_client_names));
+
+ if (!(*fn)(arg, &attr))
+ break;
+ dhip++;
+ }
+ }
+ free(iomp);
+ (void) close(fd);
+ return (ret);
+}
+
+/*
+ * Invoke the specified callback for each MAC address entry defined on
+ * the specified device.
+ */
+int
+dladm_walk_macaddr(datalink_id_t linkid, void *arg,
+ boolean_t (*fn)(void *, dladm_macaddr_attr_t *))
+{
+ int fd, bufsize, ret;
+ int nmacaddr = 1024;
+ dld_ioc_macaddrget_t *iomp = NULL;
+
+ if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
+ return (-1);
+
+ bufsize = sizeof (dld_ioc_macaddrget_t) +
+ nmacaddr * sizeof (dld_macaddrinfo_t);
+
+ if ((iomp = (dld_ioc_macaddrget_t *)calloc(1, bufsize)) == NULL)
+ return (-1);
+
+ iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t);
+ iomp->dig_linkid = linkid;
+
+ ret = ioctl(fd, DLDIOC_MACADDRGET, iomp);
+ if (ret == 0) {
+ int i;
+ dld_macaddrinfo_t *dmip;
+ dladm_macaddr_attr_t attr;
+
+ dmip = (dld_macaddrinfo_t *)(iomp + 1);
+ for (i = 0; i < iomp->dig_count; i++) {
+ bzero(&attr, sizeof (attr));
+
+ attr.ma_slot = dmip->dmi_slot;
+ attr.ma_flags = 0;
+ if (dmip->dmi_flags & DLDIOCMACADDR_USED)
+ attr.ma_flags |= DLADM_MACADDR_USED;
+ bcopy(dmip->dmi_addr, attr.ma_addr,
+ dmip->dmi_addrlen);
+ attr.ma_addrlen = dmip->dmi_addrlen;
+ (void) strlcpy(attr.ma_client_name,
+ dmip->dmi_client_name, MAXNAMELEN);
+ attr.ma_client_linkid = dmip->dma_client_linkid;
+
+ if (!(*fn)(arg, &attr))
+ break;
+ dmip++;
+ }
+ }
+ free(iomp);
+ (void) close(fd);
+ return (ret);
+}
+
/*
* These routines are used by administration tools such as dladm(1M) to
* iterate through the list of MAC interfaces
@@ -253,84 +403,22 @@ dladm_linkduplex2str(link_duplex_t duplex, char *buf)
/*
* Set zoneid of a given link. Note that this function takes a link name
* argument instead of a linkid, because a data-link (and its linkid) could
- * be created implicitly as the result of this function. For example, a VLAN
- * could be created if a VLAN PPA hack name is assigned to an exclusive
- * non-global zone.
+ * be created implicitly as the result of this function.
*/
dladm_status_t
dladm_setzid(const char *dlname, char *zone_name)
{
datalink_id_t linkid;
- char *val;
- char **prop_val;
- char link[MAXLINKNAMELEN];
- uint_t ppa;
- char dev[DLPI_LINKNAME_MAX];
- int valsize;
dladm_status_t status = DLADM_STATUS_OK;
- char *prop_name = "zone";
- boolean_t needfree = B_FALSE;
- char delim = ':';
/* If the link does not exist, it is a ppa-hacked vlan. */
status = dladm_name2info(dlname, &linkid, NULL, NULL, NULL);
- switch (status) {
- case DLADM_STATUS_NOTFOUND:
- if (strlen(dlname) > MAXLINKNAMELEN)
- return (DLADM_STATUS_BADVAL);
-
- if (strlen(zone_name) > ZONENAME_MAX)
- return (DLADM_STATUS_BADVAL);
-
- status = dladm_parselink(dlname, dev, &ppa);
- if (status != DLADM_STATUS_OK)
- return (status);
-
- ppa = (uint_t)DLS_PPA2INST(ppa);
- (void) snprintf(link, sizeof (link), "%s%d", dev, ppa);
-
- status = dladm_name2info(link, &linkid, NULL, NULL, NULL);
- if (status != DLADM_STATUS_OK)
- return (status);
-
- /*
- * Since the link does not exist as yet, we've to pass the
- * link name too as part of data, so that the kernel can
- * create the link. Hence, we're packing the zone_name and
- * the link name into val.
- */
- valsize = ZONENAME_MAX + MAXLINKNAMELEN + 1;
- val = malloc(valsize);
- if (val == NULL)
- return (DLADM_STATUS_NOMEM);
- needfree = B_TRUE;
-
- (void) snprintf(val, valsize, "%s%c%s", zone_name,
- delim, dlname);
-
- break;
- case DLADM_STATUS_OK:
- /*
- * The link exists, so only the zone_name is being passed as
- * val. We could also pass zone_name + linkname like in the
- * previous case just to maintain consistency, but other calls
- * like set_linkprop() in dladm.c [which is called when we run
- * 'dladm set-linkprop -p zone <linkname>' at the command line]
- * pass in the value entered at the command line [which is zone
- * name] as val.
- */
- val = zone_name;
- break;
- default:
- return (DLADM_STATUS_FAILED);
- }
+ if (status != DLADM_STATUS_OK)
+ return (status);
- prop_val = &val;
- status = dladm_set_linkprop(linkid, prop_name, prop_val, 1,
+ status = dladm_set_linkprop(linkid, "zone", &zone_name, 1,
DLADM_OPT_ACTIVE);
- if (needfree)
- free(val);
return (status);
}
@@ -958,86 +1046,6 @@ done:
}
dladm_status_t
-dladm_get_single_mac_stat(datalink_id_t linkid, const char *name, uint8_t type,
- void *val)
-{
- char module[DLPI_LINKNAME_MAX];
- uint_t instance;
- char link[DLPI_LINKNAME_MAX];
- dladm_status_t status;
- uint32_t flags, media;
- kstat_ctl_t *kcp;
- kstat_t *ksp;
- dladm_phys_attr_t dpap;
-
- if ((status = dladm_datalink_id2info(linkid, &flags, NULL, &media,
- link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
- return (status);
-
- if (media != DL_ETHER)
- return (DLADM_STATUS_LINKINVAL);
-
- status = dladm_phys_info(linkid, &dpap, DLADM_OPT_PERSIST);
-
- if (status != DLADM_STATUS_OK)
- return (status);
-
- status = dladm_parselink(dpap.dp_dev, module, &instance);
-
- if (status != DLADM_STATUS_OK)
- return (status);
-
- if ((kcp = kstat_open()) == NULL)
- return (dladm_errno2status(errno));
-
- /*
- * The kstat query could fail if the underlying MAC
- * driver was already detached.
- */
- if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
- (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL)
- goto bail;
-
- if (kstat_read(kcp, ksp, NULL) == -1)
- goto bail;
-
- if (dladm_kstat_value(ksp, name, type, val) < 0)
- goto bail;
-
- (void) kstat_close(kcp);
- return (DLADM_STATUS_OK);
-bail:
- (void) kstat_close(kcp);
- return (dladm_errno2status(errno));
-
-}
-
-int
-dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
-{
- kstat_named_t *knp;
-
- if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
- return (-1);
-
- if (knp->data_type != type)
- return (-1);
-
- switch (type) {
- case KSTAT_DATA_UINT64:
- *(uint64_t *)buf = knp->value.ui64;
- break;
- case KSTAT_DATA_UINT32:
- *(uint32_t *)buf = knp->value.ui32;
- break;
- default:
- return (-1);
- }
-
- return (0);
-}
-
-dladm_status_t
dladm_parselink(const char *dev, char *provider, uint_t *ppa)
{
ifspec_t ifsp;
diff --git a/usr/src/lib/libdladm/common/libdllink.h b/usr/src/lib/libdladm/common/libdllink.h
index ea51087a83..29d078470c 100644
--- a/usr/src/lib/libdladm/common/libdllink.h
+++ b/usr/src/lib/libdladm/common/libdllink.h
@@ -31,17 +31,19 @@
* link administration (i.e. not limited to one specific type of link).
*/
+#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <libdladm.h>
-#include <kstat.h>
+#include <libdladm_impl.h>
+#include <sys/mac_flow.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct dladm_attr {
- uint_t da_max_sdu;
+ uint_t da_max_sdu;
} dladm_attr_t;
typedef struct dladm_phys_attr {
@@ -86,6 +88,32 @@ typedef int dladm_secobj_class_t;
typedef int (dladm_walkcb_t)(const char *, void *);
+/* possible flags for ma_flags below */
+#define DLADM_MACADDR_USED 0x1
+
+typedef enum {
+ DLADM_HWGRP_TYPE_RX = 0x1,
+ DLADM_HWGRP_TYPE_TX
+} dladm_hwgrp_type_t;
+
+typedef struct dladm_hwgrp_attr {
+ char hg_link_name[MAXLINKNAMELEN];
+ uint_t hg_grp_num;
+ dladm_hwgrp_type_t hg_grp_type;
+ uint_t hg_n_rings;
+ uint_t hg_n_clnts;
+ char hg_client_names[MAXCLIENTNAMELEN];
+} dladm_hwgrp_attr_t;
+
+typedef struct dladm_macaddr_attr {
+ uint_t ma_slot;
+ uint_t ma_flags;
+ uchar_t ma_addr[MAXMACADDRLEN];
+ uint_t ma_addrlen;
+ char ma_client_name[MAXNAMELEN];
+ datalink_id_t ma_client_linkid;
+} dladm_macaddr_attr_t;
+
extern dladm_status_t dladm_walk(dladm_walkcb_t *, void *, datalink_class_t,
datalink_media_t, uint32_t);
extern dladm_status_t dladm_mac_walk(dladm_walkcb_t *, void *);
@@ -148,12 +176,19 @@ extern dladm_status_t dladm_phys_delete(datalink_id_t);
extern dladm_status_t dladm_phys_info(datalink_id_t, dladm_phys_attr_t *,
uint32_t);
-extern dladm_status_t dladm_get_single_mac_stat(datalink_id_t, const char *,
- uint8_t, void *);
-extern int dladm_kstat_value(kstat_t *, const char *, uint8_t,
- void *);
extern dladm_status_t dladm_parselink(const char *, char *, uint_t *);
+extern int dladm_walk_macaddr(datalink_id_t, void *,
+ boolean_t (*)(void *, dladm_macaddr_attr_t *));
+extern int dladm_walk_hwgrp(datalink_id_t, void *,
+ boolean_t (*)(void *, dladm_hwgrp_attr_t *));
+
+extern dladm_status_t dladm_link_get_proplist(datalink_id_t,
+ dladm_arg_list_t **);
+
+extern dladm_status_t i_dladm_set_link_proplist_db(char *,
+ dladm_arg_list_t *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libdladm/common/libdlstat.c b/usr/src/lib/libdladm/common/libdlstat.c
new file mode 100644
index 0000000000..1990d27c67
--- /dev/null
+++ b/usr/src/lib/libdladm/common/libdlstat.c
@@ -0,0 +1,684 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <err.h>
+#include <errno.h>
+#include <kstat.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/dld.h>
+
+#include <libdllink.h>
+#include <libdlflow.h>
+#include <libdlstat.h>
+
+/*
+ * x86 <sys/regs> ERR conflicts with <curses.h> ERR.
+ * Include curses.h last.
+ */
+#if defined(ERR)
+#undef ERR
+#endif
+#include <curses.h>
+
+struct flowlist {
+ char flowname[MAXNAMELEN];
+ datalink_id_t linkid;
+ uint_t ifspeed;
+ boolean_t first;
+ boolean_t display;
+ pktsum_t prevstats;
+ pktsum_t diffstats;
+};
+
+static int maxx, maxy, redraw = 0;
+static volatile uint_t handle_resize = 0, handle_break = 0;
+
+pktsum_t totalstats;
+struct flowlist *stattable = NULL;
+static int statentry = -1, maxstatentries = 0;
+
+#define STATGROWSIZE 16
+
+
+/*
+ * Search for flowlist entry in stattable which matches
+ * the flowname and linkide. If no match is found, use
+ * next available slot. If no slots are available,
+ * reallocate table with more slots.
+ *
+ * Return: *flowlist of matching flow
+ * NULL if realloc fails
+ */
+
+static struct flowlist *
+findstat(const char *flowname, datalink_id_t linkid)
+{
+ int match = 0;
+ struct flowlist *flist;
+
+ /* Look for match in the stattable */
+ for (match = 0, flist = stattable;
+ match <= statentry;
+ match++, flist++) {
+
+ if (flist == NULL)
+ break;
+ /* match the flowname */
+ if (flowname != NULL) {
+ if (strncmp(flowname, flist->flowname, MAXNAMELEN)
+ == NULL)
+ return (flist);
+ /* match the linkid */
+ } else {
+ if (linkid == flist->linkid)
+ return (flist);
+ }
+ }
+
+ /*
+ * No match found in the table. Store statistics in the next slot.
+ * If necessary, make room for this entry.
+ */
+ statentry++;
+ if ((maxstatentries == 0) || (maxstatentries == statentry)) {
+ maxstatentries += STATGROWSIZE;
+ stattable = realloc(stattable,
+ maxstatentries * sizeof (struct flowlist));
+ if (stattable == NULL) {
+ perror("realloc");
+ return (struct flowlist *)(NULL);
+ }
+ }
+ flist = &stattable[statentry];
+ bzero(flist, sizeof (struct flowlist));
+ flist->first = B_TRUE;
+
+ if (flowname != NULL)
+ (void) strncpy(flist->flowname, flowname, MAXNAMELEN);
+ flist->linkid = linkid;
+ return (flist);
+}
+
+static void
+print_flow_stats(struct flowlist *flist)
+{
+ struct flowlist *fcurr;
+ double ikbs, okbs;
+ double ipks, opks;
+ double dlt;
+ int fcount;
+ static boolean_t first = B_TRUE;
+
+ if (first) {
+ first = B_FALSE;
+ (void) printw("please wait...\n");
+ return;
+ }
+
+ for (fcount = 0, fcurr = flist;
+ fcount <= statentry;
+ fcount++, fcurr++) {
+ if (fcurr->flowname && fcurr->display) {
+ char linkname[MAXNAMELEN];
+
+ (void) dladm_datalink_id2info(fcurr->linkid, NULL, NULL,
+ NULL, linkname, sizeof (linkname));
+ dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC;
+ ikbs = fcurr->diffstats.rbytes * 8 / dlt / 1024;
+ okbs = fcurr->diffstats.obytes * 8 / dlt / 1024;
+ ipks = fcurr->diffstats.ipackets / dlt;
+ opks = fcurr->diffstats.opackets / dlt;
+ (void) printw("%-15.15s", fcurr->flowname);
+ (void) printw("%-10.10s", linkname);
+ (void) printw("%9.2f %9.2f %9.2f %9.2f ",
+ ikbs, okbs, ipks, opks);
+ (void) printw("\n");
+ }
+ }
+}
+
+/*ARGSUSED*/
+static int
+flow_kstats(dladm_flow_attr_t *attr, void *arg)
+{
+ kstat_ctl_t *kcp = (kstat_ctl_t *)arg;
+ kstat_t *ksp;
+ struct flowlist *flist;
+ pktsum_t currstats, *prevstats, *diffstats;
+
+ flist = findstat(attr->fa_flowname, attr->fa_linkid);
+ if (flist != NULL) {
+ prevstats = &flist->prevstats;
+ diffstats = &flist->diffstats;
+ } else {
+ return (DLADM_STATUS_FAILED);
+ }
+
+ /* lookup kstat entry */
+ ksp = dladm_kstat_lookup(kcp, NULL, -1, attr->fa_flowname, "flow");
+
+ if (ksp == NULL)
+ return (DLADM_WALK_TERMINATE);
+ else
+ flist->display = B_TRUE;
+
+ dladm_get_stats(kcp, ksp, &currstats);
+ if (flist->ifspeed == 0)
+ (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64,
+ &flist->ifspeed);
+
+ if (flist->first)
+ flist->first = B_FALSE;
+ else {
+ dladm_stats_diff(diffstats, &currstats, prevstats);
+ dladm_stats_total(&totalstats, diffstats, &totalstats);
+ }
+
+ bcopy(&currstats, prevstats, sizeof (pktsum_t));
+ return (DLADM_WALK_CONTINUE);
+}
+
+static void
+print_link_stats(struct flowlist *flist)
+{
+ struct flowlist *fcurr;
+ double ikbs, okbs;
+ double ipks, opks;
+ double util;
+ double dlt;
+ int fcount;
+ static boolean_t first = B_TRUE;
+
+ if (first) {
+ first = B_FALSE;
+ (void) printw("please wait...\n");
+ return;
+ }
+
+ for (fcount = 0, fcurr = flist;
+ fcount <= statentry;
+ fcount++, fcurr++) {
+ if ((fcurr->linkid != DATALINK_INVALID_LINKID) &&
+ fcurr->display) {
+ char linkname[MAXNAMELEN];
+
+ (void) dladm_datalink_id2info(fcurr->linkid, NULL, NULL,
+ NULL, linkname, sizeof (linkname));
+ dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC;
+ ikbs = (double)fcurr->diffstats.rbytes * 8 / dlt / 1024;
+ okbs = (double)fcurr->diffstats.obytes * 8 / dlt / 1024;
+ ipks = (double)fcurr->diffstats.ipackets / dlt;
+ opks = (double)fcurr->diffstats.opackets / dlt;
+ (void) printw("%-10.10s", linkname);
+ (void) printw("%9.2f %9.2f %9.2f %9.2f ",
+ ikbs, okbs, ipks, opks);
+ if (fcurr->ifspeed != 0)
+ util = ((ikbs + okbs) * 1024) *
+ 100/ fcurr->ifspeed;
+ else
+ util = (double)0;
+ (void) attron(A_BOLD);
+ (void) printw(" %6.2f", util);
+ (void) attroff(A_BOLD);
+ (void) printw("\n");
+ }
+ }
+}
+
+/*
+ * This function is called through the dladm_walk_datalink_id() walker and
+ * calls the dladm_walk_flow() walker.
+ */
+
+/*ARGSUSED*/
+static int
+link_flowstats(datalink_id_t linkid, void *arg)
+{
+ return (dladm_walk_flow(flow_kstats, linkid, arg, B_FALSE));
+}
+
+/*ARGSUSED*/
+static int
+link_kstats(datalink_id_t linkid, void *arg)
+{
+ kstat_ctl_t *kcp = (kstat_ctl_t *)arg;
+ struct flowlist *flist;
+ pktsum_t currstats, *prevstats, *diffstats;
+ kstat_t *ksp;
+ char linkname[MAXNAMELEN];
+
+ /* find the flist entry */
+ flist = findstat(NULL, linkid);
+ if (flist != NULL) {
+ prevstats = &flist->prevstats;
+ diffstats = &flist->diffstats;
+ } else {
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ /* lookup kstat entry */
+ (void) dladm_datalink_id2info(linkid, NULL, NULL, NULL, linkname,
+ sizeof (linkname));
+
+ if (linkname == NULL) {
+ warn("no linkname for linkid");
+ return (DLADM_WALK_TERMINATE);
+ }
+
+ ksp = dladm_kstat_lookup(kcp, NULL, -1, linkname, "net");
+
+ if (ksp == NULL)
+ return (DLADM_WALK_TERMINATE);
+ else
+ flist->display = B_TRUE;
+
+ /* read packet and byte stats */
+ dladm_get_stats(kcp, ksp, &currstats);
+
+ if (flist->ifspeed == 0)
+ (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64,
+ &flist->ifspeed);
+
+ if (flist->first == B_TRUE)
+ flist->first = B_FALSE;
+ else
+ dladm_stats_diff(diffstats, &currstats, prevstats);
+
+ bcopy(&currstats, prevstats, sizeof (*prevstats));
+
+ return (DLADM_WALK_CONTINUE);
+}
+
+/*ARGSUSED*/
+static void
+sig_break(int s)
+{
+ handle_break = 1;
+}
+
+/*ARGSUSED*/
+static void
+sig_resize(int s)
+{
+ handle_resize = 1;
+}
+
+static void
+curses_init()
+{
+ maxx = maxx; /* lint */
+ maxy = maxy; /* lint */
+
+ /* Install signal handlers */
+ (void) signal(SIGINT, sig_break);
+ (void) signal(SIGQUIT, sig_break);
+ (void) signal(SIGTERM, sig_break);
+ (void) signal(SIGWINCH, sig_resize);
+
+ /* Initialize ncurses */
+ (void) initscr();
+ (void) cbreak();
+ (void) noecho();
+ (void) curs_set(0);
+ timeout(0);
+ getmaxyx(stdscr, maxy, maxx);
+}
+
+static void
+curses_fin()
+{
+ (void) printw("\n");
+ (void) curs_set(1);
+ (void) nocbreak();
+ (void) endwin();
+
+ free(stattable);
+}
+
+static void
+stat_report(kstat_ctl_t *kcp, datalink_id_t linkid, const char *flowname,
+ int opt)
+{
+
+ double dlt, ikbs, okbs, ipks, opks;
+
+ struct flowlist *fstable = stattable;
+
+ if ((opt != LINK_REPORT) && (opt != FLOW_REPORT))
+ return;
+
+ /* Handle window resizes */
+ if (handle_resize) {
+ (void) endwin();
+ (void) initscr();
+ (void) cbreak();
+ (void) noecho();
+ (void) curs_set(0);
+ timeout(0);
+ getmaxyx(stdscr, maxy, maxx);
+ redraw = 1;
+ handle_resize = 0;
+ }
+
+ /* Print title */
+ (void) erase();
+ (void) attron(A_BOLD);
+ (void) move(0, 0);
+ if (opt == FLOW_REPORT)
+ (void) printw("%-15.15s", "Flow");
+ (void) printw("%-10.10s", "Link");
+ (void) printw("%9.9s %9.9s %9.9s %9.9s ",
+ "iKb/s", "oKb/s", "iPk/s", "oPk/s");
+ if (opt == LINK_REPORT)
+ (void) printw(" %6.6s", "%Util");
+ (void) printw("\n");
+ (void) attroff(A_BOLD);
+
+ (void) move(2, 0);
+
+ /* Print stats for each link or flow */
+ bzero(&totalstats, sizeof (totalstats));
+ if (opt == LINK_REPORT) {
+ /* Display all links */
+ if (linkid == DATALINK_ALL_LINKID) {
+ (void) dladm_walk_datalink_id(link_kstats,
+ (void *)kcp, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ /* Display 1 link */
+ } else {
+ (void) link_kstats(linkid, kcp);
+ }
+ print_link_stats(fstable);
+
+ } else if (opt == FLOW_REPORT) {
+ /* Display 1 flow */
+ if (flowname != NULL) {
+ dladm_flow_attr_t fattr;
+ if (dladm_flow_info(flowname, &fattr) !=
+ DLADM_STATUS_OK)
+ return;
+ (void) flow_kstats(&fattr, kcp);
+ /* Display all flows on all links */
+ } else if (linkid == DATALINK_ALL_LINKID) {
+ (void) dladm_walk_datalink_id(link_flowstats,
+ (void *)kcp, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ /* Display all flows on a link */
+ } else if (linkid != DATALINK_INVALID_LINKID) {
+ (void) dladm_walk_flow(flow_kstats, linkid, kcp,
+ B_FALSE);
+ }
+ print_flow_stats(fstable);
+
+ /* Print totals */
+ (void) attron(A_BOLD);
+ dlt = (double)totalstats.snaptime / (double)NANOSEC;
+ ikbs = totalstats.rbytes / dlt / 1024;
+ okbs = totalstats.obytes / dlt / 1024;
+ ipks = totalstats.ipackets / dlt;
+ opks = totalstats.opackets / dlt;
+ (void) printw("\n%-25.25s", "Totals");
+ (void) printw("%9.2f %9.2f %9.2f %9.2f ",
+ ikbs, okbs, ipks, opks);
+ (void) attroff(A_BOLD);
+ }
+
+ if (redraw)
+ (void) clearok(stdscr, 1);
+
+ if (refresh() == ERR)
+ return;
+
+ if (redraw) {
+ (void) clearok(stdscr, 0);
+ redraw = 0;
+ }
+}
+
+/* Exported functions */
+
+/*
+ * Continuously display link or flow statstics using a libcurses
+ * based display.
+ */
+
+void
+dladm_continuous(datalink_id_t linkid, const char *flowname, int interval,
+ int opt)
+{
+ kstat_ctl_t *kcp;
+
+ if ((kcp = kstat_open()) == NULL) {
+ warn("kstat open operation failed");
+ return;
+ }
+
+ curses_init();
+
+ for (;;) {
+
+ if (handle_break)
+ break;
+
+ stat_report(kcp, linkid, flowname, opt);
+
+ (void) sleep(max(1, interval));
+ }
+
+ (void) curses_fin();
+ (void) kstat_close(kcp);
+}
+
+/*
+ * dladm_kstat_lookup() is a modified version of kstat_lookup which
+ * adds the class as a selector.
+ */
+
+kstat_t *
+dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance,
+ const char *name, const char *class)
+{
+ kstat_t *ksp = NULL;
+
+ for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
+ if ((module == NULL || strcmp(ksp->ks_module, module) == 0) &&
+ (instance == -1 || ksp->ks_instance == instance) &&
+ (name == NULL || strcmp(ksp->ks_name, name) == 0) &&
+ (class == NULL || strcmp(ksp->ks_class, class) == 0))
+ return (ksp);
+ }
+
+ errno = ENOENT;
+ return (NULL);
+}
+
+/*
+ * dladm_get_stats() populates the supplied pktsum_t structure with
+ * the input and output packet and byte kstats from the kstat_t
+ * found with dladm_kstat_lookup.
+ */
+void
+dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats)
+{
+
+ if (kstat_read(kcp, ksp, NULL) == -1)
+ return;
+
+ stats->snaptime = gethrtime();
+
+ if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
+ &stats->ipackets) < 0) {
+ if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64,
+ &stats->ipackets) < 0)
+ return;
+ }
+
+ if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
+ &stats->opackets) < 0) {
+ if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64,
+ &stats->opackets) < 0)
+ return;
+ }
+
+ if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
+ &stats->rbytes) < 0) {
+ if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64,
+ &stats->rbytes) < 0)
+ return;
+ }
+
+ if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
+ &stats->obytes) < 0) {
+ if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64,
+ &stats->obytes) < 0)
+ return;
+ }
+
+ if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
+ &stats->ierrors) < 0) {
+ if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64,
+ &stats->ierrors) < 0)
+ return;
+ }
+
+ if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
+ &stats->oerrors) < 0) {
+ if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64,
+ &stats->oerrors) < 0)
+ return;
+ }
+}
+
+int
+dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
+{
+ kstat_named_t *knp;
+
+ if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
+ return (-1);
+
+ if (knp->data_type != type)
+ return (-1);
+
+ switch (type) {
+ case KSTAT_DATA_UINT64:
+ *(uint64_t *)buf = knp->value.ui64;
+ break;
+ case KSTAT_DATA_UINT32:
+ *(uint32_t *)buf = knp->value.ui32;
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+dladm_status_t
+dladm_get_single_mac_stat(datalink_id_t linkid, const char *name, uint8_t type,
+ void *val)
+{
+ kstat_ctl_t *kcp;
+ char module[DLPI_LINKNAME_MAX];
+ uint_t instance;
+ char link[DLPI_LINKNAME_MAX];
+ dladm_status_t status;
+ uint32_t flags, media;
+ kstat_t *ksp;
+ dladm_phys_attr_t dpap;
+
+ if ((kcp = kstat_open()) == NULL) {
+ warn("kstat_open operation failed");
+ return (-1);
+ }
+
+ if ((status = dladm_datalink_id2info(linkid, &flags, NULL, &media,
+ link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
+ return (status);
+
+ if (media != DL_ETHER)
+ return (DLADM_STATUS_LINKINVAL);
+
+ status = dladm_phys_info(linkid, &dpap, DLADM_OPT_PERSIST);
+
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ status = dladm_parselink(dpap.dp_dev, module, &instance);
+
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ /*
+ * The kstat query could fail if the underlying MAC
+ * driver was already detached.
+ */
+ if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
+ (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL)
+ goto bail;
+
+ if (kstat_read(kcp, ksp, NULL) == -1)
+ goto bail;
+
+ if (dladm_kstat_value(ksp, name, type, val) < 0)
+ goto bail;
+
+ (void) kstat_close(kcp);
+ return (DLADM_STATUS_OK);
+
+bail:
+ (void) kstat_close(kcp);
+ return (dladm_errno2status(errno));
+}
+
+/* Compute sum of 2 pktsums (s1 = s2 + s3) */
+void
+dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
+{
+ s1->rbytes = s2->rbytes + s3->rbytes;
+ s1->ipackets = s2->ipackets + s3->ipackets;
+ s1->ierrors = s2->ierrors + s3->ierrors;
+ s1->obytes = s2->obytes + s3->obytes;
+ s1->opackets = s2->opackets + s3->opackets;
+ s1->oerrors = s2->oerrors + s3->oerrors;
+ s1->snaptime = s2->snaptime;
+}
+
+/* Compute differences between 2 pktsums (s1 = s2 - s3) */
+void
+dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
+{
+ s1->rbytes = s2->rbytes - s3->rbytes;
+ s1->ipackets = s2->ipackets - s3->ipackets;
+ s1->ierrors = s2->ierrors - s3->ierrors;
+ s1->obytes = s2->obytes - s3->obytes;
+ s1->opackets = s2->opackets - s3->opackets;
+ s1->oerrors = s2->oerrors - s3->oerrors;
+ s1->snaptime = s2->snaptime - s3->snaptime;
+}
diff --git a/usr/src/lib/libdladm/common/libdlstat.h b/usr/src/lib/libdladm/common/libdlstat.h
new file mode 100644
index 0000000000..a142275268
--- /dev/null
+++ b/usr/src/lib/libdladm/common/libdlstat.h
@@ -0,0 +1,71 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBDLSTAT_H
+#define _LIBDLSTAT_H
+
+/*
+ * This file includes structures, macros and common routines shared by all
+ * data-link administration, and routines which are used to retrieve and
+ * display statistics.
+ */
+
+#include <kstat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LINK_REPORT 1
+#define FLOW_REPORT 2
+
+typedef struct pktsum_s {
+ hrtime_t snaptime;
+ uint64_t ipackets;
+ uint64_t opackets;
+ uint64_t rbytes;
+ uint64_t obytes;
+ uint64_t ierrors;
+ uint64_t oerrors;
+} pktsum_t;
+
+extern void dladm_continuous(datalink_id_t, const char *, int, int);
+
+extern kstat_t *dladm_kstat_lookup(kstat_ctl_t *, const char *, int,
+ const char *, const char *);
+extern void dladm_get_stats(kstat_ctl_t *, kstat_t *, pktsum_t *);
+extern int dladm_kstat_value(kstat_t *, const char *, uint8_t,
+ void *);
+extern dladm_status_t dladm_get_single_mac_stat(datalink_id_t, const char *,
+ uint8_t, void *);
+
+extern void dladm_stats_total(pktsum_t *, pktsum_t *, pktsum_t *);
+extern void dladm_stats_diff(pktsum_t *, pktsum_t *, pktsum_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBDLSTAT_H */
diff --git a/usr/src/lib/libdladm/common/libdlvlan.c b/usr/src/lib/libdladm/common/libdlvlan.c
index f6d855db72..1dc04bf4eb 100644
--- a/usr/src/lib/libdladm/common/libdlvlan.c
+++ b/usr/src/lib/libdladm/common/libdlvlan.c
@@ -23,16 +23,8 @@
* Use is subject to license terms.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <assert.h>
-#include <sys/dld.h>
-#include <libdladm_impl.h>
-#include <libdllink.h>
#include <libdlvlan.h>
+#include <libdlvnic.h>
/*
* VLAN Administration Library.
@@ -44,106 +36,19 @@
/*
* Returns the current attributes of the specified VLAN.
*/
-static dladm_status_t
-i_dladm_vlan_info_active(datalink_id_t vlanid, dladm_vlan_attr_t *dvap)
-{
- int fd;
- dld_ioc_vlan_attr_t div;
- dladm_status_t status = DLADM_STATUS_OK;
-
- if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
- return (dladm_errno2status(errno));
-
- div.div_vlanid = vlanid;
-
- if (ioctl(fd, DLDIOC_VLAN_ATTR, &div) < 0)
- status = dladm_errno2status(errno);
-
- dvap->dv_vid = div.div_vid;
- dvap->dv_linkid = div.div_linkid;
- dvap->dv_force = div.div_force;
- dvap->dv_implicit = div.div_implicit;
-done:
- (void) close(fd);
- return (status);
-}
-
-/*
- * Returns the persistent attributes of the specified VLAN.
- */
-static dladm_status_t
-i_dladm_vlan_info_persist(datalink_id_t vlanid, dladm_vlan_attr_t *dvap)
-{
- dladm_conf_t conf = DLADM_INVALID_CONF;
- dladm_status_t status;
- uint64_t u64;
-
- if ((status = dladm_read_conf(vlanid, &conf)) != DLADM_STATUS_OK)
- return (status);
-
- status = dladm_get_conf_field(conf, FLINKOVER, &u64, sizeof (u64));
- if (status != DLADM_STATUS_OK)
- goto done;
- dvap->dv_linkid = (datalink_id_t)u64;
-
- status = dladm_get_conf_field(conf, FFORCE, &dvap->dv_force,
- sizeof (boolean_t));
- if (status != DLADM_STATUS_OK)
- goto done;
-
- dvap->dv_implicit = B_FALSE;
-
- status = dladm_get_conf_field(conf, FVLANID, &u64, sizeof (u64));
- if (status != DLADM_STATUS_OK)
- goto done;
- dvap->dv_vid = (uint16_t)u64;
-
-done:
- dladm_destroy_conf(conf);
- return (status);
-}
-
dladm_status_t
dladm_vlan_info(datalink_id_t vlanid, dladm_vlan_attr_t *dvap, uint32_t flags)
{
- assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
- if (flags == DLADM_OPT_ACTIVE)
- return (i_dladm_vlan_info_active(vlanid, dvap));
- else
- return (i_dladm_vlan_info_persist(vlanid, dvap));
-}
-
-static dladm_status_t
-dladm_persist_vlan_conf(const char *vlan, datalink_id_t vlanid,
- boolean_t force, datalink_id_t linkid, uint16_t vid)
-{
- dladm_conf_t conf = DLADM_INVALID_CONF;
- dladm_status_t status;
- uint64_t u64;
+ dladm_status_t status;
+ dladm_vnic_attr_t attr, *vnic = &attr;
- if ((status = dladm_create_conf(vlan, vlanid, DATALINK_CLASS_VLAN,
- DL_ETHER, &conf)) != DLADM_STATUS_OK) {
+ if ((status = dladm_vnic_info(vlanid, vnic, flags)) !=
+ DLADM_STATUS_OK)
return (status);
- }
- u64 = linkid;
- status = dladm_set_conf_field(conf, FLINKOVER, DLADM_TYPE_UINT64, &u64);
- if (status != DLADM_STATUS_OK)
- goto done;
-
- status = dladm_set_conf_field(conf, FFORCE, DLADM_TYPE_BOOLEAN, &force);
- if (status != DLADM_STATUS_OK)
- goto done;
-
- u64 = vid;
- status = dladm_set_conf_field(conf, FVLANID, DLADM_TYPE_UINT64, &u64);
- if (status != DLADM_STATUS_OK)
- goto done;
-
- status = dladm_write_conf(conf);
-
-done:
- dladm_destroy_conf(conf);
+ dvap->dv_vid = vnic->va_vid;
+ dvap->dv_linkid = vnic->va_link_id;
+ dvap->dv_force = vnic->va_force;
return (status);
}
@@ -152,63 +57,11 @@ done:
*/
dladm_status_t
dladm_vlan_create(const char *vlan, datalink_id_t linkid, uint16_t vid,
- uint32_t flags)
+ dladm_arg_list_t *proplist, uint32_t flags, datalink_id_t *vlan_id_out)
{
- dld_ioc_create_vlan_t dic;
- int fd;
- datalink_id_t vlanid = DATALINK_INVALID_LINKID;
- uint_t media;
- datalink_class_t class;
- dladm_status_t status;
-
- if (vid < 1 || vid > 4094)
- return (DLADM_STATUS_VIDINVAL);
-
- if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
- return (dladm_errno2status(errno));
-
- status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
- if (status != DLADM_STATUS_OK || media != DL_ETHER ||
- class == DATALINK_CLASS_VLAN) {
- return (DLADM_STATUS_BADARG);
- }
-
- status = dladm_create_datalink_id(vlan, DATALINK_CLASS_VLAN, DL_ETHER,
- flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST), &vlanid);
- if (status != DLADM_STATUS_OK)
- goto fail;
-
- if (flags & DLADM_OPT_PERSIST) {
- status = dladm_persist_vlan_conf(vlan, vlanid,
- (flags & DLADM_OPT_FORCE) != 0, linkid, vid);
- if (status != DLADM_STATUS_OK)
- goto fail;
- }
-
- if (flags & DLADM_OPT_ACTIVE) {
- dic.dic_vlanid = vlanid;
- dic.dic_linkid = linkid;
- dic.dic_vid = vid;
- dic.dic_force = (flags & DLADM_OPT_FORCE) != 0;
-
- if (ioctl(fd, DLDIOC_CREATE_VLAN, &dic) < 0) {
- status = dladm_errno2status(errno);
- if (flags & DLADM_OPT_PERSIST)
- (void) dladm_remove_conf(vlanid);
- goto fail;
- }
- }
-
- (void) close(fd);
- return (DLADM_STATUS_OK);
-
-fail:
- if (vlanid != DATALINK_INVALID_LINKID) {
- (void) dladm_destroy_datalink_id(vlanid,
- flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST));
- }
- (void) close(fd);
- return (status);
+ return (dladm_vnic_create(vlan, linkid, VNIC_MAC_ADDR_TYPE_PRIMARY,
+ NULL, 0, NULL, 0, vid, vlan_id_out, proplist,
+ flags | DLADM_OPT_VLAN));
}
/*
@@ -217,124 +70,11 @@ fail:
dladm_status_t
dladm_vlan_delete(datalink_id_t vlanid, uint32_t flags)
{
- dld_ioc_delete_vlan_t did;
- int fd;
- datalink_class_t class;
- dladm_status_t status = DLADM_STATUS_OK;
-
- if ((dladm_datalink_id2info(vlanid, NULL, &class, NULL, NULL, 0) !=
- DLADM_STATUS_OK) || (class != DATALINK_CLASS_VLAN)) {
- return (DLADM_STATUS_BADARG);
- }
-
- if (flags & DLADM_OPT_ACTIVE) {
- if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
- return (dladm_errno2status(errno));
-
- did.did_linkid = vlanid;
- if ((ioctl(fd, DLDIOC_DELETE_VLAN, &did) < 0) &&
- ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) {
- (void) close(fd);
- return (dladm_errno2status(errno));
- }
- (void) close(fd);
-
- /*
- * Delete active linkprop before this active link is deleted.
- */
- (void) dladm_set_linkprop(vlanid, NULL, NULL, 0,
- DLADM_OPT_ACTIVE);
- }
-
- (void) dladm_destroy_datalink_id(vlanid,
- flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST));
-
- if (flags & DLADM_OPT_PERSIST)
- (void) dladm_remove_conf(vlanid);
-
- return (status);
-}
-
-/*
- * Callback used by dladm_vlan_up()
- */
-static int
-i_dladm_vlan_up(datalink_id_t vlanid, void *arg)
-{
- dladm_vlan_attr_t dva;
- dld_ioc_create_vlan_t dic;
- dladm_status_t *statusp = arg;
- uint32_t flags;
- int fd;
- dladm_status_t status;
-
- status = dladm_vlan_info(vlanid, &dva, DLADM_OPT_PERSIST);
- if (status != DLADM_STATUS_OK)
- goto done;
-
- /*
- * Validate (and delete) the link associated with this VLAN, see if
- * the specific hardware has been removed during system shutdown.
- */
- if ((status = dladm_datalink_id2info(dva.dv_linkid, &flags, NULL,
- NULL, NULL, 0)) != DLADM_STATUS_OK) {
- goto done;
- }
-
- if (!(flags & DLADM_OPT_ACTIVE)) {
- status = DLADM_STATUS_BADARG;
- goto done;
- }
-
- dic.dic_linkid = dva.dv_linkid;
- dic.dic_force = dva.dv_force;
- dic.dic_vid = dva.dv_vid;
-
- if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
- status = dladm_errno2status(errno);
- goto done;
- }
-
- dic.dic_vlanid = vlanid;
- if (ioctl(fd, DLDIOC_CREATE_VLAN, &dic) < 0) {
- status = dladm_errno2status(errno);
- goto done;
- }
-
- if ((status = dladm_up_datalink_id(vlanid)) != DLADM_STATUS_OK) {
- dld_ioc_delete_vlan_t did;
-
- did.did_linkid = vlanid;
- (void) ioctl(fd, DLDIOC_DELETE_VLAN, &did);
- } else {
- /*
- * Reset the active linkprop of this specific link.
- */
- (void) dladm_init_linkprop(vlanid, B_FALSE);
- }
-
- (void) close(fd);
-done:
- *statusp = status;
- return (DLADM_WALK_CONTINUE);
+ return (dladm_vnic_delete(vlanid, flags | DLADM_OPT_VLAN));
}
-/*
- * Bring up one VLAN, or all persistent VLANs. In the latter case, the
- * walk may terminate early if bringup of a VLAN fails.
- */
dladm_status_t
dladm_vlan_up(datalink_id_t linkid)
{
- dladm_status_t status;
-
- if (linkid == DATALINK_ALL_LINKID) {
- (void) dladm_walk_datalink_id(i_dladm_vlan_up, &status,
- DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
- DLADM_OPT_PERSIST);
- return (DLADM_STATUS_OK);
- } else {
- (void) i_dladm_vlan_up(linkid, &status);
- return (status);
- }
+ return (dladm_vnic_up(linkid, DLADM_OPT_VLAN));
}
diff --git a/usr/src/lib/libdladm/common/libdlvlan.h b/usr/src/lib/libdladm/common/libdlvlan.h
index 7a305443df..91f6ee8671 100644
--- a/usr/src/lib/libdladm/common/libdlvlan.h
+++ b/usr/src/lib/libdladm/common/libdlvlan.h
@@ -26,8 +26,6 @@
#ifndef _LIBDLVLAN_H
#define _LIBDLVLAN_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* This file includes structures, macros and routines used by VLAN link
* administration.
@@ -43,13 +41,13 @@ typedef struct dladm_vlan_attr {
uint16_t dv_vid;
datalink_id_t dv_linkid;
boolean_t dv_force;
- boolean_t dv_implicit;
} dladm_vlan_attr_t;
extern dladm_status_t dladm_vlan_info(datalink_id_t, dladm_vlan_attr_t *,
uint32_t);
extern dladm_status_t dladm_vlan_create(const char *, datalink_id_t,
- uint16_t, uint32_t);
+ uint16_t, dladm_arg_list_t *, uint32_t,
+ datalink_id_t *);
extern dladm_status_t dladm_vlan_delete(datalink_id_t, uint32_t);
extern dladm_status_t dladm_vlan_up(datalink_id_t);
diff --git a/usr/src/lib/libdladm/common/libdlvnic.c b/usr/src/lib/libdladm/common/libdlvnic.c
index ac97372785..dfa58bcac5 100644
--- a/usr/src/lib/libdladm/common/libdlvnic.c
+++ b/usr/src/lib/libdladm/common/libdlvnic.c
@@ -36,6 +36,7 @@
#include <libintl.h>
#include <net/if_types.h>
#include <net/if_dl.h>
+#include <sys/dld.h>
#include <libdladm_impl.h>
#include <libdllink.h>
#include <libdlvnic.h>
@@ -44,137 +45,258 @@
* VNIC administration library.
*/
-/* Limits on buffer size for VNIC_IOC_INFO request */
-#define MIN_INFO_SIZE (4*1024)
-#define MAX_INFO_SIZE (128*1024)
-
-/* configuration database entry */
-typedef struct dladm_vnic_attr_db {
- datalink_id_t vt_vnic_id;
- datalink_id_t vt_link_id;
- vnic_mac_addr_type_t vt_mac_addr_type;
- uint_t vt_mac_len;
- uchar_t vt_mac_addr[MAXMACADDRLEN];
-} dladm_vnic_attr_db_t;
-
-typedef struct dladm_vnic_modify_attr {
- vnic_mac_addr_type_t vm_mac_addr_type;
- int vm_mac_len;
- uchar_t vm_mac_addr[MAXMACADDRLEN];
-} dladm_vnic_modify_attr_t;
+/*
+ * Default random MAC address prefix (locally administered).
+ */
+static char dladm_vnic_def_prefix[] = {0x02, 0x08, 0x20};
+
+static dladm_status_t dladm_vnic_persist_conf(const char *name,
+ dladm_vnic_attr_t *, datalink_class_t);
+static const char *dladm_vnic_macaddr2str(const uchar_t *, char *);
+static dladm_status_t dladm_vnic_str2macaddr(const char *, uchar_t *);
/*
- * Send a create command to the VNIC driver.
+ * Convert a diagnostic returned by the kernel into a dladm_status_t.
*/
static dladm_status_t
-i_dladm_vnic_create_sys(int fd, dladm_vnic_attr_db_t *attr)
+dladm_vnic_diag2status(vnic_ioc_diag_t ioc_diag)
{
- vnic_ioc_create_t ioc;
-
- ioc.vc_vnic_id = attr->vt_vnic_id;
- ioc.vc_link_id = attr->vt_link_id;
- ioc.vc_mac_addr_type = attr->vt_mac_addr_type;
- ioc.vc_mac_len = attr->vt_mac_len;
- bcopy(attr->vt_mac_addr, ioc.vc_mac_addr, attr->vt_mac_len);
-
- if (ioctl(fd, VNIC_IOC_CREATE, &ioc) < 0)
- return (dladm_errno2status(errno));
-
+ switch (ioc_diag) {
+ case VNIC_IOC_DIAG_MACADDR_INVALID:
+ return (DLADM_STATUS_INVALIDMACADDR);
+ case VNIC_IOC_DIAG_MACADDRLEN_INVALID:
+ return (DLADM_STATUS_INVALIDMACADDRLEN);
+ case VNIC_IOC_DIAG_MACADDR_NIC:
+ return (DLADM_STATUS_INVALIDMACADDRNIC);
+ case VNIC_IOC_DIAG_MACADDR_INUSE:
+ return (DLADM_STATUS_INVALIDMACADDRINUSE);
+ case VNIC_IOC_DIAG_MACFACTORYSLOTINVALID:
+ return (DLADM_STATUS_MACFACTORYSLOTINVALID);
+ case VNIC_IOC_DIAG_MACFACTORYSLOTUSED:
+ return (DLADM_STATUS_MACFACTORYSLOTUSED);
+ case VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED:
+ return (DLADM_STATUS_MACFACTORYSLOTALLUSED);
+ case VNIC_IOC_DIAG_MACFACTORYNOTSUP:
+ return (DLADM_STATUS_MACFACTORYNOTSUP);
+ case VNIC_IOC_DIAG_MACPREFIX_INVALID:
+ return (DLADM_STATUS_INVALIDMACPREFIX);
+ case VNIC_IOC_DIAG_MACPREFIXLEN_INVALID:
+ return (DLADM_STATUS_INVALIDMACPREFIXLEN);
+ case VNIC_IOC_DIAG_MACMARGIN_INVALID:
+ return (DLADM_STATUS_INVALID_MACMARGIN);
+ case VNIC_IOC_DIAG_NO_HWRINGS:
+ return (DLADM_STATUS_NO_HWRINGS);
+ }
return (DLADM_STATUS_OK);
}
/*
- * Send a modify command to the VNIC driver.
+ * Send a create command to the VNIC driver.
*/
-static dladm_status_t
-i_dladm_vnic_modify_sys(datalink_id_t vnic_id, uint32_t modify_mask,
- dladm_vnic_modify_attr_t *attr)
+dladm_status_t
+i_dladm_vnic_create_sys(dladm_vnic_attr_t *attr)
{
+ int rc, fd;
+ vnic_ioc_create_t ioc;
dladm_status_t status = DLADM_STATUS_OK;
- int fd;
- vnic_ioc_modify_t ioc;
-
- ioc.vm_vnic_id = vnic_id;
- ioc.vm_modify_mask = 0;
- if (modify_mask & DLADM_VNIC_MODIFY_ADDR)
- ioc.vm_modify_mask |= VNIC_IOC_MODIFY_ADDR;
-
- ioc.vm_mac_addr_type = attr->vm_mac_addr_type;
- ioc.vm_mac_len = attr->vm_mac_len;
- bcopy(attr->vm_mac_addr, ioc.vm_mac_addr, MAXMACADDRLEN);
+ bzero(&ioc, sizeof (ioc));
+ ioc.vc_vnic_id = attr->va_vnic_id;
+ ioc.vc_link_id = attr->va_link_id;
+ ioc.vc_mac_addr_type = attr->va_mac_addr_type;
+ ioc.vc_mac_len = attr->va_mac_len;
+ ioc.vc_mac_slot = attr->va_mac_slot;
+ ioc.vc_mac_prefix_len = attr->va_mac_prefix_len;
+ ioc.vc_vid = attr->va_vid;
+ ioc.vc_flags = attr->va_force ? VNIC_IOC_CREATE_FORCE : 0;
+ ioc.vc_flags |= attr->va_hwrings ? VNIC_IOC_CREATE_REQ_HWRINGS : 0;
+
+ if (attr->va_mac_len > 0 || ioc.vc_mac_prefix_len > 0)
+ bcopy(attr->va_mac_addr, ioc.vc_mac_addr, MAXMACADDRLEN);
+ bcopy(&attr->va_resource_props, &ioc.vc_resource_props,
+ sizeof (mac_resource_props_t));
+ if (attr->va_link_id == DATALINK_INVALID_LINKID)
+ ioc.vc_flags |= VNIC_IOC_CREATE_ANCHOR;
if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
return (dladm_errno2status(errno));
- if (ioctl(fd, VNIC_IOC_MODIFY, &ioc) < 0)
+ rc = ioctl(fd, VNIC_IOC_CREATE, &ioc);
+ if (rc < 0)
status = dladm_errno2status(errno);
(void) close(fd);
+ if (status != DLADM_STATUS_OK) {
+ if (ioc.vc_diag != VNIC_IOC_DIAG_NONE)
+ status = dladm_vnic_diag2status(ioc.vc_diag);
+ }
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ attr->va_mac_addr_type = ioc.vc_mac_addr_type;
+ switch (ioc.vc_mac_addr_type) {
+ case VNIC_MAC_ADDR_TYPE_FACTORY:
+ attr->va_mac_slot = ioc.vc_mac_slot;
+ break;
+ case VNIC_MAC_ADDR_TYPE_RANDOM:
+ bcopy(ioc.vc_mac_addr, attr->va_mac_addr, MAXMACADDRLEN);
+ attr->va_mac_len = ioc.vc_mac_len;
+ break;
+ }
return (status);
}
/*
* Get the configuration information of the given VNIC.
*/
-dladm_status_t
-dladm_vnic_info(datalink_id_t vnic_id, dladm_vnic_attr_sys_t *attrp,
- uint32_t flags)
+static dladm_status_t
+i_dladm_vnic_info_active(datalink_id_t linkid, dladm_vnic_attr_t *attrp)
{
- vnic_ioc_info_t *ioc;
- vnic_ioc_info_vnic_t *vnic;
- int bufsize, fd;
+ vnic_ioc_info_t ioc;
+ vnic_info_t *vnic;
+ int rc, fd;
dladm_status_t status = DLADM_STATUS_OK;
- /* for now, only temporary creations are supported */
- if (flags & DLADM_OPT_PERSIST)
- return (dladm_errno2status(ENOTSUP));
-
if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) == -1)
return (dladm_errno2status(errno));
- bufsize = sizeof (vnic_ioc_info_t) + sizeof (vnic_ioc_info_vnic_t);
- ioc = (vnic_ioc_info_t *)calloc(1, bufsize);
- if (ioc == NULL) {
- (void) close(fd);
- return (dladm_errno2status(ENOMEM));
- }
+ bzero(&ioc, sizeof (ioc));
+ vnic = &ioc.vi_info;
+ vnic->vn_vnic_id = linkid;
- ioc->vi_vnic_id = vnic_id;
- ioc->vi_size = bufsize - sizeof (vnic_ioc_info_t);
- if (ioctl(fd, VNIC_IOC_INFO, ioc) != 0) {
+ rc = ioctl(fd, VNIC_IOC_INFO, &ioc);
+ if (rc != 0) {
status = dladm_errno2status(errno);
goto bail;
}
- vnic = (vnic_ioc_info_vnic_t *)(ioc + 1);
-
attrp->va_vnic_id = vnic->vn_vnic_id;
attrp->va_link_id = vnic->vn_link_id;
attrp->va_mac_addr_type = vnic->vn_mac_addr_type;
- bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, ETHERADDRL);
+ bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, MAXMACADDRLEN);
attrp->va_mac_len = vnic->vn_mac_len;
+ attrp->va_mac_slot = vnic->vn_mac_slot;
+ attrp->va_mac_prefix_len = vnic->vn_mac_prefix_len;
+ attrp->va_vid = vnic->vn_vid;
+ attrp->va_force = vnic->vn_force;
bail:
- free(ioc);
(void) close(fd);
return (status);
}
+static dladm_status_t
+i_dladm_vnic_info_persist(datalink_id_t linkid, dladm_vnic_attr_t *attrp)
+{
+ dladm_conf_t conf;
+ dladm_status_t status;
+ char macstr[ETHERADDRL * 3];
+ uint64_t u64;
+ datalink_class_t class;
+
+ attrp->va_vnic_id = linkid;
+ if ((status = dladm_read_conf(linkid, &conf)) != DLADM_STATUS_OK)
+ return (status);
+
+ status = dladm_get_conf_field(conf, FLINKOVER, &u64, sizeof (u64));
+ attrp->va_link_id = ((status == DLADM_STATUS_OK) ?
+ (datalink_id_t)u64 : DATALINK_INVALID_LINKID);
+
+ status = dladm_get_conf_field(conf, FHWRINGS, &attrp->va_hwrings,
+ sizeof (boolean_t));
+
+ if (status != DLADM_STATUS_OK && status != DLADM_STATUS_NOTFOUND)
+ goto done;
+ if (status == DLADM_STATUS_NOTFOUND)
+ attrp->va_hwrings = B_FALSE;
+
+ if ((status = dladm_datalink_id2info(linkid, NULL, &class,
+ NULL, NULL, 0)) != DLADM_STATUS_OK)
+ goto done;
+
+ if (class == DATALINK_CLASS_VLAN) {
+ if (attrp->va_link_id == DATALINK_INVALID_LINKID) {
+ status = DLADM_STATUS_BADARG;
+ goto done;
+ }
+ attrp->va_mac_addr_type = VNIC_MAC_ADDR_TYPE_PRIMARY;
+ attrp->va_mac_len = 0;
+ } else {
+ status = dladm_get_conf_field(conf, FMADDRTYPE, &u64,
+ sizeof (u64));
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ attrp->va_mac_addr_type = (vnic_mac_addr_type_t)u64;
+
+ status = dladm_get_conf_field(conf, FMADDRLEN, &u64,
+ sizeof (u64));
+ attrp->va_mac_len = ((status == DLADM_STATUS_OK) ?
+ (uint_t)u64 : ETHERADDRL);
+
+ status = dladm_get_conf_field(conf, FMADDRSLOT, &u64,
+ sizeof (u64));
+ attrp->va_mac_slot = ((status == DLADM_STATUS_OK) ?
+ (int)u64 : -1);
+
+ status = dladm_get_conf_field(conf, FMADDRPREFIXLEN, &u64,
+ sizeof (u64));
+ attrp->va_mac_prefix_len = ((status == DLADM_STATUS_OK) ?
+ (uint_t)u64 : sizeof (dladm_vnic_def_prefix));
+
+ status = dladm_get_conf_field(conf, FMACADDR, macstr,
+ sizeof (macstr));
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ status = dladm_vnic_str2macaddr(macstr, attrp->va_mac_addr);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+
+ status = dladm_get_conf_field(conf, FVLANID, &u64, sizeof (u64));
+ attrp->va_vid = ((status == DLADM_STATUS_OK) ? (uint16_t)u64 : 0);
+
+
+ status = DLADM_STATUS_OK;
+done:
+ dladm_destroy_conf(conf);
+ return (status);
+}
+
+dladm_status_t
+dladm_vnic_info(datalink_id_t linkid, dladm_vnic_attr_t *attrp,
+ uint32_t flags)
+{
+ if (flags == DLADM_OPT_ACTIVE)
+ return (i_dladm_vnic_info_active(linkid, attrp));
+ else if (flags == DLADM_OPT_PERSIST)
+ return (i_dladm_vnic_info_persist(linkid, attrp));
+ else
+ return (DLADM_STATUS_BADARG);
+}
+
/*
* Remove a VNIC from the kernel.
*/
-static dladm_status_t
-i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr)
+dladm_status_t
+i_dladm_vnic_delete_sys(datalink_id_t linkid)
{
vnic_ioc_delete_t ioc;
+ dladm_status_t status = DLADM_STATUS_OK;
+ int rc, fd;
- ioc.vd_vnic_id = attr->va_vnic_id;
-
- if (ioctl(fd, VNIC_IOC_DELETE, &ioc) < 0)
+ if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
return (dladm_errno2status(errno));
- return (DLADM_STATUS_OK);
+ ioc.vd_vnic_id = linkid;
+
+ rc = ioctl(fd, VNIC_IOC_DELETE, &ioc);
+ if (rc < 0)
+ status = dladm_errno2status(errno);
+
+ (void) close(fd);
+ return (status);
}
/*
@@ -182,20 +304,32 @@ i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr)
*/
typedef struct dladm_vnic_addr_type_s {
- char *va_str;
- vnic_mac_addr_type_t va_type;
+ const char *va_str;
+ vnic_mac_addr_type_t va_type;
} dladm_vnic_addr_type_t;
static dladm_vnic_addr_type_t addr_types[] = {
{"fixed", VNIC_MAC_ADDR_TYPE_FIXED},
+ {"random", VNIC_MAC_ADDR_TYPE_RANDOM},
+ {"factory", VNIC_MAC_ADDR_TYPE_FACTORY},
+ {"auto", VNIC_MAC_ADDR_TYPE_AUTO},
+ {"fixed", VNIC_MAC_ADDR_TYPE_PRIMARY}
};
#define NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t))
-/*
- * Return DLADM_STATUS_OK if a matching type was found,
- * DLADM_STATUS_BADARG otherwise
- */
+static const char *
+dladm_vnic_macaddrtype2str(vnic_mac_addr_type_t type)
+{
+ int i;
+
+ for (i = 0; i < NADDR_TYPES; i++) {
+ if (type == addr_types[i].va_type)
+ return (addr_types[i].va_str);
+ }
+ return (NULL);
+}
+
dladm_status_t
dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val)
{
@@ -209,136 +343,397 @@ dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val)
return (DLADM_STATUS_OK);
}
}
-
return (DLADM_STATUS_BADARG);
}
+
+
/*
- * Create a new VNIC. Update the configuration file and bring it up.
+ * Create a new VNIC / VLAN. Update the configuration file and bring it up.
*/
dladm_status_t
dladm_vnic_create(const char *vnic, datalink_id_t linkid,
vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len,
- datalink_id_t *vnic_id_out, uint32_t flags)
+ int *mac_slot, uint_t mac_prefix_len, uint16_t vid,
+ datalink_id_t *vnic_id_out, dladm_arg_list_t *proplist, uint32_t flags)
{
- dladm_vnic_attr_db_t attr;
- int i, fd;
+ dladm_vnic_attr_t attr;
datalink_id_t vnic_id;
datalink_class_t class;
- uint32_t media;
- char *name = (char *)vnic;
+ uint32_t media = DL_ETHER;
+ char name[MAXLINKNAMELEN];
+ uchar_t tmp_addr[MAXMACADDRLEN];
dladm_status_t status;
+ boolean_t is_vlan;
+ boolean_t is_etherstub;
+ int i;
/*
* Sanity test arguments.
*/
- if (flags & DLADM_OPT_PERSIST)
- return (dladm_errno2status(ENOTSUP));
+ if ((flags & DLADM_OPT_ACTIVE) == 0)
+ return (DLADM_STATUS_NOTSUP);
+
+ is_vlan = ((flags & DLADM_OPT_VLAN) != 0);
+ if (is_vlan && ((vid < 1 || vid > 4094)))
+ return (DLADM_STATUS_VIDINVAL);
+
+ is_etherstub = (linkid == DATALINK_INVALID_LINKID);
if (mac_len > MAXMACADDRLEN)
return (DLADM_STATUS_INVALIDMACADDRLEN);
- for (i = 0; i < NADDR_TYPES; i++) {
- if (mac_addr_type == addr_types[i].va_type)
- break;
- }
- if (i == NADDR_TYPES)
+ if (!dladm_vnic_macaddrtype2str(mac_addr_type))
return (DLADM_STATUS_INVALIDMACADDRTYPE);
- if ((status = dladm_datalink_id2info(linkid, NULL, &class, &media,
- NULL, 0)) != DLADM_STATUS_OK) {
- return (status);
+ /*
+ * If a random address might be generated, but no prefix
+ * was specified by the caller, use the default MAC address
+ * prefix.
+ */
+ if ((mac_addr_type == VNIC_MAC_ADDR_TYPE_RANDOM ||
+ mac_addr_type == VNIC_MAC_ADDR_TYPE_AUTO) &&
+ mac_prefix_len == 0) {
+ mac_prefix_len = sizeof (dladm_vnic_def_prefix);
+ mac_addr = tmp_addr;
+ bcopy(dladm_vnic_def_prefix, mac_addr, mac_prefix_len);
}
- if (class == DATALINK_CLASS_VNIC)
- return (DLADM_STATUS_BADARG);
+ if ((flags & DLADM_OPT_ANCHOR) == 0) {
+ if ((status = dladm_datalink_id2info(linkid, NULL, &class,
+ &media, NULL, 0)) != DLADM_STATUS_OK)
+ return (status);
+
+ if (class == DATALINK_CLASS_VNIC ||
+ class == DATALINK_CLASS_VLAN)
+ return (DLADM_STATUS_BADARG);
+ } else {
+ /* it's an anchor VNIC */
+ if (linkid != DATALINK_INVALID_LINKID || vid != 0)
+ return (DLADM_STATUS_BADARG);
+ }
if (vnic == NULL) {
flags |= DLADM_OPT_PREFIX;
- name = "vnic";
+ (void) strlcpy(name, "vnic", sizeof (name));
+ } else {
+ (void) strlcpy(name, vnic, sizeof (name));
}
- if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_VNIC,
- media, flags, &vnic_id)) != DLADM_STATUS_OK) {
+ class = is_vlan ? DATALINK_CLASS_VLAN :
+ (is_etherstub ? DATALINK_CLASS_ETHERSTUB : DATALINK_CLASS_VNIC);
+ if ((status = dladm_create_datalink_id(name, class,
+ media, flags, &vnic_id)) != DLADM_STATUS_OK)
return (status);
+
+ if ((flags & DLADM_OPT_PREFIX) != 0) {
+ (void) snprintf(name + 4, sizeof (name), "%llu", vnic_id);
+ flags &= ~DLADM_OPT_PREFIX;
}
bzero(&attr, sizeof (attr));
- attr.vt_vnic_id = vnic_id;
- attr.vt_link_id = linkid;
- attr.vt_mac_addr_type = mac_addr_type;
- attr.vt_mac_len = mac_len;
- bcopy(mac_addr, attr.vt_mac_addr, mac_len);
- if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
- status = dladm_errno2status(errno);
+ /* Extract resource_ctl and cpu_list from proplist */
+ if (proplist != NULL) {
+ status = dladm_link_proplist_extract(proplist,
+ &attr.va_resource_props);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+
+ attr.va_vnic_id = vnic_id;
+ attr.va_link_id = linkid;
+ attr.va_mac_addr_type = mac_addr_type;
+ attr.va_mac_len = mac_len;
+ if (mac_slot != NULL)
+ attr.va_mac_slot = *mac_slot;
+ if (mac_len > 0)
+ bcopy(mac_addr, attr.va_mac_addr, mac_len);
+ else if (mac_prefix_len > 0)
+ bcopy(mac_addr, attr.va_mac_addr, mac_prefix_len);
+ attr.va_mac_prefix_len = mac_prefix_len;
+ attr.va_vid = vid;
+ attr.va_force = (flags & DLADM_OPT_FORCE) != 0;
+ attr.va_hwrings = (flags & DLADM_OPT_HWRINGS) != 0;
+
+ status = i_dladm_vnic_create_sys(&attr);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ /* Save vnic configuration and its properties */
+ if (!(flags & DLADM_OPT_PERSIST))
+ goto done;
+
+ status = dladm_vnic_persist_conf(name, &attr, class);
+ if (status != DLADM_STATUS_OK) {
+ (void) i_dladm_vnic_delete_sys(vnic_id);
goto done;
}
- status = i_dladm_vnic_create_sys(fd, &attr);
- (void) close(fd);
+ if (proplist != NULL) {
+ for (i = 0; i < proplist->al_count; i++) {
+ dladm_arg_info_t *aip = &proplist->al_info[i];
+
+ status = dladm_set_linkprop(vnic_id, aip->ai_name,
+ aip->ai_val, aip->ai_count, DLADM_OPT_PERSIST);
+ if (status != DLADM_STATUS_OK)
+ break;
+ }
+
+ if (status != DLADM_STATUS_OK) {
+ (void) dladm_remove_conf(vnic_id);
+ (void) i_dladm_vnic_delete_sys(vnic_id);
+ }
+ }
done:
if (status != DLADM_STATUS_OK) {
- (void) dladm_destroy_datalink_id(vnic_id,
- flags & ~DLADM_OPT_PREFIX);
+ (void) dladm_destroy_datalink_id(vnic_id, flags);
} else {
- *vnic_id_out = vnic_id;
+ if (vnic_id_out != NULL)
+ *vnic_id_out = vnic_id;
+ if (mac_slot != NULL)
+ *mac_slot = attr.va_mac_slot;
}
-
return (status);
}
/*
- * Modify the properties of a VNIC.
+ * Delete a VNIC / VLAN.
*/
dladm_status_t
-dladm_vnic_modify(datalink_id_t vnic_id, uint32_t modify_mask,
- vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
- uint32_t flags)
+dladm_vnic_delete(datalink_id_t linkid, uint32_t flags)
{
- dladm_vnic_modify_attr_t new_attr;
+ dladm_status_t status;
+ datalink_class_t class;
- /* for now, only temporary creations are supported */
- if (flags & DLADM_OPT_PERSIST)
- return (dladm_errno2status(ENOTSUP));
+ if (flags == 0)
+ return (DLADM_STATUS_BADARG);
- bzero(&new_attr, sizeof (new_attr));
+ if ((dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0) !=
+ DLADM_STATUS_OK))
+ return (DLADM_STATUS_BADARG);
- if (modify_mask & DLADM_VNIC_MODIFY_ADDR) {
- new_attr.vm_mac_addr_type = mac_addr_type;
- new_attr.vm_mac_len = mac_len;
- bcopy(mac_addr, new_attr.vm_mac_addr, MAXMACADDRLEN);
+ if ((flags & DLADM_OPT_VLAN) != 0) {
+ if (class != DATALINK_CLASS_VLAN)
+ return (DLADM_STATUS_BADARG);
+ } else {
+ if (class != DATALINK_CLASS_VNIC &&
+ class != DATALINK_CLASS_ETHERSTUB)
+ return (DLADM_STATUS_BADARG);
}
- /* update the properties of the existing VNIC */
- return (i_dladm_vnic_modify_sys(vnic_id, modify_mask, &new_attr));
+ if ((flags & DLADM_OPT_ACTIVE) != 0) {
+ status = i_dladm_vnic_delete_sys(linkid);
+ if (status == DLADM_STATUS_OK) {
+ (void) dladm_set_linkprop(linkid, NULL, NULL, 0,
+ DLADM_OPT_ACTIVE);
+ (void) dladm_destroy_datalink_id(linkid,
+ DLADM_OPT_ACTIVE);
+ } else if (status != DLADM_STATUS_NOTFOUND ||
+ !(flags & DLADM_OPT_PERSIST)) {
+ return (status);
+ }
+ }
+ if ((flags & DLADM_OPT_PERSIST) != 0) {
+ (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST);
+ (void) dladm_remove_conf(linkid);
+ }
+ return (DLADM_STATUS_OK);
}
-/*
- * Delete a VNIC.
- */
-dladm_status_t
-dladm_vnic_delete(datalink_id_t vnic_id, uint32_t flags)
+static const char *
+dladm_vnic_macaddr2str(const uchar_t *mac, char *buf)
{
- dladm_status_t status;
- dladm_vnic_attr_sys_t sys_attr;
- int fd;
+ static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
- /* for now, only temporary deletes are supported */
- if (flags & DLADM_OPT_PERSIST)
- return (dladm_errno2status(ENOTSUP));
+ if (buf == NULL)
+ return (NULL);
- if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
- return (dladm_errno2status(errno));
+ if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
+ (void) strlcpy(buf, "unknown", DLADM_STRSIZE);
+ else
+ return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
- sys_attr.va_vnic_id = vnic_id;
- status = i_dladm_vnic_delete_sys(fd, &sys_attr);
- (void) close(fd);
+ return (buf);
+}
- if (status != DLADM_STATUS_OK)
+static dladm_status_t
+dladm_vnic_str2macaddr(const char *str, uchar_t *buf)
+{
+ int len = 0;
+ uchar_t *b = _link_aton(str, &len);
+
+ if (b == NULL || len >= MAXMACADDRLEN)
+ return (DLADM_STATUS_BADARG);
+
+ bcopy(b, buf, len);
+ free(b);
+ return (DLADM_STATUS_OK);
+}
+
+
+static dladm_status_t
+dladm_vnic_persist_conf(const char *name, dladm_vnic_attr_t *attrp,
+ datalink_class_t class)
+{
+ dladm_conf_t conf = DLADM_INVALID_CONF;
+ dladm_status_t status;
+ char macstr[ETHERADDRL * 3];
+ uint64_t u64;
+
+ if ((status = dladm_create_conf(name, attrp->va_vnic_id,
+ class, DL_ETHER, &conf)) != DLADM_STATUS_OK)
return (status);
- (void) dladm_destroy_datalink_id(vnic_id, flags);
+ if (attrp->va_link_id != DATALINK_INVALID_LINKID) {
+ u64 = attrp->va_link_id;
+ status = dladm_set_conf_field(conf, FLINKOVER,
+ DLADM_TYPE_UINT64, &u64);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+
+ if (class != DATALINK_CLASS_VLAN) {
+ u64 = attrp->va_mac_addr_type;
+ status = dladm_set_conf_field(conf, FMADDRTYPE,
+ DLADM_TYPE_UINT64, &u64);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ if (attrp->va_mac_len != ETHERADDRL) {
+ u64 = attrp->va_mac_len;
+ status = dladm_set_conf_field(conf, FMADDRLEN,
+ DLADM_TYPE_UINT64, &u64);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+ }
+
+ if (attrp->va_hwrings) {
+ boolean_t hwrings = attrp->va_hwrings;
+ status = dladm_set_conf_field(conf, FHWRINGS,
+ DLADM_TYPE_BOOLEAN, &hwrings);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+
+ if (class != DATALINK_CLASS_VLAN) {
+ if (attrp->va_mac_slot != -1) {
+ u64 = attrp->va_mac_slot;
+ status = dladm_set_conf_field(conf, FMADDRSLOT,
+ DLADM_TYPE_UINT64, &u64);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+
+ if (attrp->va_mac_prefix_len !=
+ sizeof (dladm_vnic_def_prefix)) {
+ u64 = attrp->va_mac_prefix_len;
+ status = dladm_set_conf_field(conf, FMADDRPREFIXLEN,
+ DLADM_TYPE_UINT64, &u64);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+
+ (void) dladm_vnic_macaddr2str(attrp->va_mac_addr, macstr);
+ status = dladm_set_conf_field(conf, FMACADDR, DLADM_TYPE_STR,
+ macstr);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+
+ if (attrp->va_vid != 0) {
+ u64 = attrp->va_vid;
+ status = dladm_set_conf_field(conf, FVLANID,
+ DLADM_TYPE_UINT64, &u64);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+ }
+
+ /*
+ * Commit the link configuration.
+ */
+ status = dladm_write_conf(conf);
+
+done:
+ dladm_destroy_conf(conf);
return (status);
}
+
+typedef struct dladm_vnic_up_arg_s {
+ uint32_t flags;
+ dladm_status_t status;
+} dladm_vnic_up_arg_t;
+
+#define DLADM_VNIC_UP_FIRST_WALK 0x1
+#define DLADM_VNIC_UP_SECOND_WALK 0x2
+
+static int
+i_dladm_vnic_up(datalink_id_t linkid, void *arg)
+{
+ dladm_status_t *statusp = &(((dladm_vnic_up_arg_t *)arg)->status);
+ dladm_vnic_attr_t attr;
+ dladm_status_t status;
+ dladm_arg_list_t *proplist;
+ uint32_t flags = ((dladm_vnic_up_arg_t *)arg)->flags;
+
+ bzero(&attr, sizeof (attr));
+
+ status = dladm_vnic_info(linkid, &attr, DLADM_OPT_PERSIST);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ /*
+ * Create the vnics that request hardware group first
+ * Create the vnics that don't request hardware group in the second walk
+ */
+ if ((flags == DLADM_VNIC_UP_FIRST_WALK && !attr.va_hwrings) ||
+ (flags == DLADM_VNIC_UP_SECOND_WALK && attr.va_hwrings))
+ goto done;
+
+ /* Get all properties for this vnic */
+ status = dladm_link_get_proplist(linkid, &proplist);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ if (proplist != NULL) {
+ status = dladm_link_proplist_extract(proplist,
+ &attr.va_resource_props);
+ }
+
+ status = i_dladm_vnic_create_sys(&attr);
+ if (status != DLADM_STATUS_OK)
+ goto done;
+
+ if ((status = dladm_up_datalink_id(linkid)) != DLADM_STATUS_OK) {
+ (void) i_dladm_vnic_delete_sys(linkid);
+ goto done;
+ }
+done:
+ *statusp = status;
+ return (DLADM_WALK_CONTINUE);
+}
+
+dladm_status_t
+dladm_vnic_up(datalink_id_t linkid, uint32_t flags)
+{
+ dladm_vnic_up_arg_t vnic_arg;
+ datalink_class_t class;
+
+ class = ((flags & DLADM_OPT_VLAN) != 0) ? DATALINK_CLASS_VLAN :
+ (DATALINK_CLASS_VNIC | DATALINK_CLASS_ETHERSTUB);
+
+ if (linkid == DATALINK_ALL_LINKID) {
+ vnic_arg.flags = DLADM_VNIC_UP_FIRST_WALK;
+ (void) dladm_walk_datalink_id(i_dladm_vnic_up, &vnic_arg,
+ class, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
+ vnic_arg.flags = DLADM_VNIC_UP_SECOND_WALK;
+ (void) dladm_walk_datalink_id(i_dladm_vnic_up, &vnic_arg,
+ class, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
+ return (DLADM_STATUS_OK);
+ } else {
+ (void) i_dladm_vnic_up(linkid, &vnic_arg);
+ return (vnic_arg.status);
+ }
+}
diff --git a/usr/src/lib/libdladm/common/libdlvnic.h b/usr/src/lib/libdladm/common/libdlvnic.h
index 79b4b01ba2..77f78130be 100644
--- a/usr/src/lib/libdladm/common/libdlvnic.h
+++ b/usr/src/lib/libdladm/common/libdlvnic.h
@@ -26,39 +26,43 @@
#ifndef _LIBDLVNIC_H
#define _LIBDLVNIC_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <netinet/in.h>
#include <libdladm.h>
+#include <libdladm_impl.h>
+#include <sys/mac_flow.h>
#include <sys/vnic.h>
#ifdef __cplusplus
extern "C" {
#endif
-typedef struct dladm_vnic_attr_sys {
+typedef struct dladm_vnic_attr {
datalink_id_t va_vnic_id;
datalink_id_t va_link_id;
vnic_mac_addr_type_t va_mac_addr_type;
- uchar_t va_mac_addr[ETHERADDRL];
uint_t va_mac_len;
-} dladm_vnic_attr_sys_t;
+ uchar_t va_mac_addr[MAXMACADDRLEN];
+ int va_mac_slot;
+ uint_t va_mac_prefix_len;
+ uint16_t va_vid;
+ boolean_t va_force;
+ boolean_t va_hwrings;
+ mac_resource_props_t va_resource_props;
+} dladm_vnic_attr_t;
-/*
- * Modification flags for dladm_vnic_modify().
- */
-#define DLADM_VNIC_MODIFY_ADDR 0x01
+extern dladm_status_t dladm_vnic_create(const char *, datalink_id_t,
+ vnic_mac_addr_type_t, uchar_t *, int, int *,
+ uint_t, uint16_t, datalink_id_t *,
+ dladm_arg_list_t *, uint32_t);
+
+extern dladm_status_t dladm_vnic_delete(datalink_id_t, uint32_t);
+extern dladm_status_t dladm_vnic_info(datalink_id_t, dladm_vnic_attr_t *,
+ uint32_t);
-extern dladm_status_t dladm_vnic_create(const char *, datalink_id_t,
- vnic_mac_addr_type_t, uchar_t *, int, uint_t *, uint32_t);
-extern dladm_status_t dladm_vnic_modify(datalink_id_t, uint32_t,
- vnic_mac_addr_type_t, uint_t, uchar_t *, uint32_t);
-extern dladm_status_t dladm_vnic_delete(datalink_id_t, uint32_t);
-extern dladm_status_t dladm_vnic_info(datalink_id_t, dladm_vnic_attr_sys_t *,
- uint32_t);
-extern dladm_status_t dladm_vnic_str2macaddrtype(const char *,
- vnic_mac_addr_type_t *);
+extern dladm_status_t dladm_vnic_up(datalink_id_t, uint32_t);
+extern dladm_status_t dladm_vnic_str2macaddrtype(const char *,
+ vnic_mac_addr_type_t *);
#ifdef __cplusplus
}
diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c
index 8a570c70ef..2d58b585f8 100644
--- a/usr/src/lib/libdladm/common/linkprop.c
+++ b/usr/src/lib/libdladm/common/linkprop.c
@@ -41,30 +41,34 @@
#include <libdlwlan_impl.h>
#include <libdlwlan.h>
#include <libdlvlan.h>
+#include <libdlvnic.h>
+#include <libintl.h>
#include <dlfcn.h>
#include <link.h>
#include <inet/wifi_ioctl.h>
#include <libdladm.h>
+#include <libdlstat.h>
#include <sys/param.h>
+#include <sys/debug.h>
+#include <sys/dld.h>
+#include <sys/mac_flow.h>
#include <inttypes.h>
#include <sys/ethernet.h>
#include <net/wpa.h>
#include <sys/sysmacros.h>
-#define PERM_READ_ONLY "r-"
-#define PERM_READ_WRITE "rw"
-
/*
* The linkprop get() callback.
- * - pd: pointer to the struct prop_desc
+ * - pd: pointer to the prop_desc_t
* - propstrp: a property string array to keep the returned property.
* Caller allocated.
* - cntp: number of returned properties.
* Caller also uses it to indicate how many it expects.
*/
struct prop_desc;
+typedef struct prop_desc prop_desc_t;
-typedef dladm_status_t pd_getf_t(struct prop_desc *pd,
+typedef dladm_status_t pd_getf_t(prop_desc_t *pdp,
datalink_id_t, char **propstp, uint_t *cntp,
datalink_media_t, uint_t, uint_t *);
@@ -79,10 +83,9 @@ typedef dladm_status_t pd_getf_t(struct prop_desc *pd,
* of ioctl buffers etc. pd_set() may call another common routine (used
* by all other pd_sets) which invokes the ioctl.
*/
-typedef dladm_status_t pd_setf_t(struct prop_desc *, datalink_id_t,
- val_desc_t *propval, uint_t cnt, uint_t flags,
- datalink_media_t);
-
+typedef dladm_status_t pd_setf_t(prop_desc_t *, datalink_id_t,
+ val_desc_t *propval, uint_t cnt, uint_t flags,
+ datalink_media_t);
/*
* The linkprop check() callback.
@@ -98,9 +101,8 @@ typedef dladm_status_t pd_setf_t(struct prop_desc *, datalink_id_t,
* with either a val_desc_t found on the pd_modval list or something
* generated on the fly.
*/
-typedef dladm_status_t pd_checkf_t(struct prop_desc *pd,
- datalink_id_t, char **propstrp,
- uint_t cnt, val_desc_t *propval,
+typedef dladm_status_t pd_checkf_t(prop_desc_t *pdp, datalink_id_t,
+ char **propstrp, uint_t cnt, val_desc_t *propval,
datalink_media_t);
typedef struct link_attr_s {
@@ -110,39 +112,45 @@ typedef struct link_attr_s {
} link_attr_t;
static dld_ioc_macprop_t *i_dladm_buf_alloc_by_name(size_t, datalink_id_t,
- const char *, uint_t, dladm_status_t *);
+ const char *, uint_t, dladm_status_t *);
static dld_ioc_macprop_t *i_dladm_buf_alloc_by_id(size_t, datalink_id_t,
- mac_prop_id_t, uint_t,
- dladm_status_t *);
+ mac_prop_id_t, uint_t, dladm_status_t *);
+static dld_ioc_macprop_t *i_dladm_get_public_prop(datalink_id_t, char *, uint_t,
+ dladm_status_t *, uint_t *);
+
static dladm_status_t i_dladm_set_prop(datalink_id_t, const char *, char **,
uint_t, uint_t);
static dladm_status_t i_dladm_get_prop(datalink_id_t, const char *, char **,
uint_t *, dladm_prop_type_t, uint_t);
static link_attr_t *dladm_name2prop(const char *);
static link_attr_t *dladm_id2prop(mac_prop_id_t);
-static dld_ioc_macprop_t *i_dladm_get_public_prop(datalink_id_t, char *, uint_t,
- dladm_status_t *);
+
static pd_getf_t do_get_zone, do_get_autopush, do_get_rate_mod,
do_get_rate_prop, do_get_channel_prop,
do_get_powermode_prop, do_get_radio_prop,
i_dladm_duplex_get, i_dladm_status_get,
i_dladm_binary_get, i_dladm_uint32_get,
- i_dladm_flowctl_get;
+ i_dladm_flowctl_get, dld_maxbw_get, dld_cpus_get,
+ dld_priority_get;
+
static pd_setf_t do_set_zone, do_set_rate_prop,
do_set_powermode_prop, do_set_radio_prop,
- i_dladm_set_public_prop;
+ i_dladm_set_public_prop, do_set_res, do_set_cpus;
+
static pd_checkf_t do_check_zone, do_check_autopush, do_check_rate,
- i_dladm_defmtu_check;
+ i_dladm_defmtu_check, do_check_maxbw, do_check_cpus,
+ do_check_priority;
-static dladm_status_t i_dladm_speed_get(struct prop_desc *, datalink_id_t,
- char **, uint_t *, uint_t);
+static dladm_status_t i_dladm_speed_get(prop_desc_t *, datalink_id_t,
+ char **, uint_t *, uint_t, uint_t *);
static dladm_status_t i_dladm_wlan_get_legacy_ioctl(datalink_id_t, void *,
uint_t, uint_t);
static dladm_status_t i_dladm_wlan_set_legacy_ioctl(datalink_id_t, void *,
uint_t, uint_t);
static dladm_status_t i_dladm_macprop(void *, boolean_t);
+static const char *dladm_perm2str(uint_t, char *);
-typedef struct prop_desc {
+struct prop_desc {
/*
* link property name
*/
@@ -202,7 +210,7 @@ typedef struct prop_desc {
* indicate link media type this property applies to.
*/
datalink_media_t pd_dmedia;
-} prop_desc_t;
+};
#define MAC_PROP_BUFSIZE(v) sizeof (dld_ioc_macprop_t) + (v) - 1
@@ -303,7 +311,14 @@ static link_attr_t link_attr[] = {
{ MAC_PROP_WL_MLME, sizeof (wl_mlme_t), "mlme"},
+ { MAC_PROP_MAXBW, sizeof (mac_resource_props_t), "maxbw"},
+
+ { MAC_PROP_PRIO, sizeof (mac_resource_props_t), "priority"},
+
+ { MAC_PROP_BIND_CPU, sizeof (mac_resource_props_t), "cpus"},
+
{ MAC_PROP_PRIVATE, 0, "driver-private"}
+
};
static val_desc_t link_duplex_vals[] = {
@@ -324,8 +339,11 @@ static val_desc_t link_flow_vals[] = {
{ "rx", LINK_FLOWCTRL_RX },
{ "bi", LINK_FLOWCTRL_BI }
};
-
-#define VALCNT(vals) (sizeof ((vals)) / sizeof (val_desc_t))
+static val_desc_t link_priority_vals[] = {
+ { "low", MPL_LOW },
+ { "medium", MPL_MEDIUM },
+ { "high", MPL_HIGH }
+};
static val_desc_t dladm_wlan_radio_vals[] = {
{ "on", DLADM_WLAN_RADIO_ON },
@@ -338,8 +356,10 @@ static val_desc_t dladm_wlan_powermode_vals[] = {
{ "max", DLADM_WLAN_PM_MAX }
};
-static prop_desc_t prop_table[] = {
+#define VALCNT(vals) (sizeof ((vals)) / sizeof (val_desc_t))
+#define RESET_VAL ((uintptr_t)-1)
+static prop_desc_t prop_table[] = {
{ "channel", { NULL, 0 },
NULL, 0, NULL, NULL,
do_get_channel_prop, NULL, 0,
@@ -372,12 +392,12 @@ static prop_desc_t prop_table[] = {
do_get_zone, do_check_zone, PD_TEMPONLY|PD_CHECK_ALLOC,
DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
- { "duplex", { "", 0 },
+ { "duplex", { "", 0 },
link_duplex_vals, VALCNT(link_duplex_vals),
NULL, NULL, i_dladm_duplex_get, NULL,
0, DATALINK_CLASS_PHYS, DL_ETHER },
- { "state", { "up", LINK_STATE_UP },
+ { "state", { "up", LINK_STATE_UP },
link_status_vals, VALCNT(link_status_vals),
NULL, NULL, i_dladm_status_get, NULL,
0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
@@ -455,12 +475,34 @@ static prop_desc_t prop_table[] = {
{ "en_10hdx_cap", { "", 0 },
link_01_vals, VALCNT(link_01_vals),
i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
- 0, DATALINK_CLASS_PHYS, DL_ETHER }
+ 0, DATALINK_CLASS_PHYS, DL_ETHER },
+
+ { "maxbw", { "--", RESET_VAL }, NULL, 0,
+ do_set_res, NULL,
+ dld_maxbw_get, do_check_maxbw, PD_CHECK_ALLOC,
+ DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
+ { "cpus", { "--", RESET_VAL }, NULL, 0,
+ do_set_cpus, NULL,
+ dld_cpus_get, do_check_cpus, 0,
+ DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
+
+ { "priority", { "high", RESET_VAL },
+ link_priority_vals, VALCNT(link_priority_vals), do_set_res, NULL,
+ dld_priority_get, do_check_priority, PD_CHECK_ALLOC,
+ DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
};
#define DLADM_MAX_PROPS (sizeof (prop_table) / sizeof (prop_desc_t))
+static resource_prop_t rsrc_prop_table[] = {
+ {"maxbw", do_extract_maxbw},
+ {"priority", do_extract_priority},
+ {"cpus", do_extract_cpus}
+};
+#define DLADM_MAX_RSRC_PROP (sizeof (rsrc_prop_table) / \
+ sizeof (resource_prop_t))
+
/*
* when retrieving private properties, we pass down a buffer with
* DLADM_PROP_BUF_CHUNK of space for the driver to return the property value.
@@ -477,6 +519,9 @@ static dladm_status_t i_dladm_set_linkprop(datalink_id_t, const char *,
char **, uint_t, uint_t);
static dladm_status_t i_dladm_getset_defval(prop_desc_t *, datalink_id_t,
datalink_media_t, uint_t);
+
+static dladm_status_t link_proplist_check(dladm_arg_list_t *);
+
/*
* Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
* rates to be retrieved. However, we cannot increase it at this
@@ -539,17 +584,13 @@ i_dladm_set_single_prop(datalink_id_t linkid, datalink_class_t class,
if (pdp->pd_set == NULL)
return (DLADM_STATUS_PROPRDONLY);
- if (pdp->pd_flags & PD_CHECK_ALLOC)
- needfree = B_TRUE;
- else
- needfree = B_FALSE;
if (prop_val != NULL) {
vdp = malloc(sizeof (val_desc_t) * val_cnt);
if (vdp == NULL)
return (DLADM_STATUS_NOMEM);
-
if (pdp->pd_check != NULL) {
+ needfree = ((pdp->pd_flags & PD_CHECK_ALLOC) != 0);
status = pdp->pd_check(pdp, linkid, prop_val, val_cnt,
vdp, media);
} else if (pdp->pd_optval != NULL) {
@@ -563,23 +604,25 @@ i_dladm_set_single_prop(datalink_id_t linkid, datalink_class_t class,
cnt = val_cnt;
} else {
+ boolean_t defval = B_FALSE;
+
if (pdp->pd_defval.vd_name == NULL)
return (DLADM_STATUS_NOTSUP);
cnt = 1;
- if ((pdp->pd_flags & PD_CHECK_ALLOC) != 0 ||
- strlen(pdp->pd_defval.vd_name) > 0) {
+ defval = (strlen(pdp->pd_defval.vd_name) > 0);
+ if ((pdp->pd_flags & PD_CHECK_ALLOC) != 0 || defval) {
if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
return (DLADM_STATUS_NOMEM);
- if (pdp->pd_check != NULL) {
+ if (defval) {
+ (void) memcpy(vdp, &pdp->pd_defval,
+ sizeof (val_desc_t));
+ } else if (pdp->pd_check != NULL) {
status = pdp->pd_check(pdp, linkid, prop_val,
cnt, vdp, media);
if (status != DLADM_STATUS_OK)
goto done;
- } else {
- (void) memcpy(vdp, &pdp->pd_defval,
- sizeof (val_desc_t));
}
} else {
status = i_dladm_getset_defval(pdp, linkid,
@@ -618,7 +661,6 @@ i_dladm_set_linkprop(datalink_id_t linkid, const char *prop_name,
if (prop_name != NULL &&
(strcasecmp(prop_name, pdp->pd_name) != 0))
continue;
-
found = B_TRUE;
s = i_dladm_set_single_prop(linkid, class, media, pdp, prop_val,
val_cnt, flags);
@@ -774,16 +816,8 @@ dladm_get_linkprop(datalink_id_t linkid, dladm_prop_type_t type,
}
*prop_val[0] = '\0';
- switch (perm_flags) {
- case MAC_PROP_PERM_READ:
- (void) strncpy(*prop_val, PERM_READ_ONLY,
- DLADM_PROP_VAL_MAX);
- break;
- case MAC_PROP_PERM_RW:
- (void) strncpy(*prop_val, PERM_READ_WRITE,
- DLADM_PROP_VAL_MAX);
- break;
- }
+ if (status == DLADM_STATUS_OK)
+ (void) dladm_perm2str(perm_flags, *prop_val);
break;
case DLADM_PROP_VAL_DEFAULT:
@@ -879,7 +913,16 @@ done:
static int
i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
{
- (void) dladm_init_linkprop(linkid, B_TRUE);
+ datalink_class_t class;
+ dladm_status_t status;
+
+ status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0);
+ if (status != DLADM_STATUS_OK)
+ return (DLADM_WALK_TERMINATE);
+
+ if ((class & (DATALINK_CLASS_VNIC | DATALINK_CLASS_VLAN)) == 0)
+ (void) dladm_init_linkprop(linkid, B_TRUE);
+
return (DLADM_WALK_CONTINUE);
}
@@ -904,24 +947,24 @@ dladm_init_linkprop(datalink_id_t linkid, boolean_t any_media)
/* ARGSUSED */
static dladm_status_t
-do_get_zone(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+do_get_zone(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
- char zone_name[ZONENAME_MAX];
- zoneid_t zid;
- dladm_status_t status;
- char *cp;
+ char zone_name[ZONENAME_MAX];
+ zoneid_t zid;
+ dladm_status_t status;
+ char *cp;
dld_ioc_macprop_t *dip;
if (flags != 0)
return (DLADM_STATUS_NOTSUP);
- dip = i_dladm_get_public_prop(linkid, pd->pd_name, flags, &status);
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
if (status != DLADM_STATUS_OK)
return (status);
- *perm_flags = dip->pr_perm_flags;
cp = dip->pr_val;
(void) memcpy(&zid, cp, sizeof (zid));
free(dip);
@@ -929,14 +972,12 @@ do_get_zone(struct prop_desc *pd, datalink_id_t linkid,
*val_cnt = 1;
if (zid != GLOBAL_ZONEID) {
if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) {
- *perm_flags = 0;
return (dladm_errno2status(errno));
}
(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
} else {
*prop_val[0] = '\0';
- *perm_flags = 0;
}
return (DLADM_STATUS_OK);
@@ -1011,13 +1052,13 @@ cleanup:
/* ARGSUSED */
static dladm_status_t
-do_set_zone(prop_desc_t *pd, datalink_id_t linkid, val_desc_t *vdp,
+do_set_zone(prop_desc_t *pdp, datalink_id_t linkid, val_desc_t *vdp,
uint_t val_cnt, uint_t flags, datalink_media_t media)
{
- dladm_status_t status = DLADM_STATUS_OK;
- zoneid_t zid_old, zid_new;
- char link[MAXLINKNAMELEN];
- char *cp;
+ dladm_status_t status = DLADM_STATUS_OK;
+ zoneid_t zid_old, zid_new;
+ char link[MAXLINKNAMELEN];
+ char *cp;
dld_ioc_macprop_t *dip;
dld_ioc_zid_t *dzp;
@@ -1026,25 +1067,14 @@ do_set_zone(prop_desc_t *pd, datalink_id_t linkid, val_desc_t *vdp,
dzp = (dld_ioc_zid_t *)vdp->vd_val;
- /*
- * If diz_is_ppa_hack is set, then an implicit vlan must be created.
- * There is no old value to compare against, and vdp->vd_val is
- * already populated with the zoneid and linkname in the function
- * do_check_zone().
- */
-
- if (dzp->diz_is_ppa_hack) {
- zid_old = GLOBAL_ZONEID;
- } else {
- dip = i_dladm_get_public_prop(linkid, pd->pd_name,
- flags, &status);
- if (status != DLADM_STATUS_OK)
- return (status);
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, NULL);
+ if (status != DLADM_STATUS_OK)
+ return (status);
- cp = dip->pr_val;
- (void) memcpy(&zid_old, cp, sizeof (zid_old));
- free(dip);
- }
+ cp = dip->pr_val;
+ (void) memcpy(&zid_old, cp, sizeof (zid_old));
+ free(dip);
zid_new = dzp->diz_zid;
(void) strlcpy(link, dzp->diz_link, MAXLINKNAMELEN);
@@ -1066,7 +1096,7 @@ do_set_zone(prop_desc_t *pd, datalink_id_t linkid, val_desc_t *vdp,
* link and prevent a link renaming, so we need to do it
* before other operations.
*/
- status = i_dladm_set_public_prop(pd, linkid, vdp, val_cnt,
+ status = i_dladm_set_public_prop(pdp, linkid, vdp, val_cnt,
flags, media);
if (status != DLADM_STATUS_OK)
return (status);
@@ -1092,16 +1122,9 @@ do_set_zone(prop_desc_t *pd, datalink_id_t linkid, val_desc_t *vdp,
goto rollback2;
}
- if (dzp->diz_is_ppa_hack) {
- if ((status = dladm_name2info(link, &linkid, NULL, NULL,
- NULL)) != DLADM_STATUS_OK) {
- return (status);
- }
- }
-
(void) i_dladm_update_deventry(zid_new, linkid, B_TRUE);
} else {
- status = i_dladm_set_public_prop(pd, linkid, vdp, val_cnt,
+ status = i_dladm_set_public_prop(pdp, linkid, vdp, val_cnt,
flags, media);
if (status != DLADM_STATUS_OK)
goto rollback2;
@@ -1117,7 +1140,7 @@ rollback2:
rollback1:
if (zid_new != GLOBAL_ZONEID) {
dzp->diz_zid = zid_old;
- (void) i_dladm_set_public_prop(pd, linkid, vdp, val_cnt,
+ (void) i_dladm_set_public_prop(pdp, linkid, vdp, val_cnt,
flags, media);
}
@@ -1126,15 +1149,13 @@ rollback1:
/* ARGSUSED */
static dladm_status_t
-do_check_zone(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
+do_check_zone(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
{
char *zone_name;
char linkname[MAXLINKNAMELEN];
zoneid_t zoneid;
- char *cp;
dladm_status_t status = DLADM_STATUS_OK;
- boolean_t is_ppa_hack = B_FALSE;
dld_ioc_zid_t *dzp;
if (val_cnt != 1)
@@ -1144,32 +1165,12 @@ do_check_zone(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
if (dzp == NULL)
return (DLADM_STATUS_NOMEM);
- if (prop_val) {
- /*
- * The prop_val contains zone_name{:linkname}. The linkname is
- * present only when the link is a ppa-hacked vlan.
- */
- cp = strchr(*prop_val, ':');
- if (cp) {
- (void) strlcpy(linkname, cp + 1, MAXLINKNAMELEN);
- *cp = '\0';
- is_ppa_hack = B_TRUE;
- } else {
- status = dladm_datalink_id2info(linkid, NULL, NULL,
- NULL, linkname, MAXLINKNAMELEN);
- if (status != DLADM_STATUS_OK) {
- goto done;
- }
- }
- zone_name = *prop_val;
- } else {
- zone_name = GLOBAL_ZONENAME;
- if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL,
- linkname, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
- goto done;
- }
+ if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL,
+ linkname, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
+ goto done;
}
+ zone_name = (prop_val != NULL) ? *prop_val : GLOBAL_ZONENAME;
if (strlen(linkname) > MAXLINKNAMELEN) {
status = DLADM_STATUS_BADVAL;
goto done;
@@ -1199,7 +1200,6 @@ do_check_zone(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
dzp->diz_zid = zoneid;
(void) strlcpy(dzp->diz_link, linkname, MAXLINKNAMELEN);
- dzp->diz_is_ppa_hack = is_ppa_hack;
vdp->vd_val = (uintptr_t)dzp;
return (DLADM_STATUS_OK);
@@ -1210,9 +1210,359 @@ done:
/* ARGSUSED */
static dladm_status_t
-do_get_autopush(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+dld_maxbw_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
+{
+ dld_ioc_macprop_t *dip;
+ mac_resource_props_t mrp;
+ dladm_status_t status;
+
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
+ if (dip == NULL)
+ return (status);
+
+ bcopy(dip->pr_val, &mrp, sizeof (mac_resource_props_t));
+ free(dip);
+
+ if ((mrp.mrp_mask & MRP_MAXBW) == 0) {
+ (*prop_val)[0] = '\0';
+ } else {
+ (void) dladm_bw2str(mrp.mrp_maxbw, prop_val[0]);
+ }
+ *val_cnt = 1;
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_check_maxbw(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
+ uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
+{
+ uint64_t *maxbw;
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ if (val_cnt != 1)
+ return (DLADM_STATUS_BADVALCNT);
+
+ maxbw = malloc(sizeof (uint64_t));
+ if (maxbw == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ status = dladm_str2bw(*prop_val, maxbw);
+ if (status != DLADM_STATUS_OK) {
+ free(maxbw);
+ return (status);
+ }
+
+ if ((*maxbw < MRP_MAXBW_MINVAL) && (*maxbw != 0)) {
+ free(maxbw);
+ return (DLADM_STATUS_MINMAXBW);
+ }
+
+ vdp->vd_val = (uintptr_t)maxbw;
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+dladm_status_t
+do_extract_maxbw(val_desc_t *vdp, void *arg, uint_t cnt)
+{
+ mac_resource_props_t *mrp = (mac_resource_props_t *)arg;
+
+ bcopy((char *)vdp->vd_val, &mrp->mrp_maxbw, sizeof (uint64_t));
+ mrp->mrp_mask |= MRP_MAXBW;
+
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+dld_cpus_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
+{
+ dld_ioc_macprop_t *dip;
+ mac_resource_props_t mrp;
+ int i;
+ uint32_t ncpus;
+ uchar_t *cp;
+ dladm_status_t status;
+
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
+ if (dip == NULL)
+ return (status);
+
+ cp = (uchar_t *)dip->pr_val;
+ (void) memcpy(&mrp, cp, sizeof (mac_resource_props_t));
+ free(dip);
+
+ ncpus = mrp.mrp_ncpus;
+
+ if (ncpus > *val_cnt)
+ return (DLADM_STATUS_TOOSMALL);
+
+ if (ncpus == 0) {
+ (*prop_val)[0] = '\0';
+ *val_cnt = 1;
+ return (DLADM_STATUS_OK);
+ }
+
+ *val_cnt = ncpus;
+ for (i = 0; i < ncpus; i++) {
+ (void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
+ "%u", mrp.mrp_cpu[i]);
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_set_res(prop_desc_t *pdp, datalink_id_t linkid, val_desc_t *vdp,
+ uint_t val_cnt, uint_t flags, datalink_media_t media)
+{
+ mac_resource_props_t mrp;
+ dladm_status_t status = DLADM_STATUS_OK;
+ dld_ioc_macprop_t *dip;
+
+ bzero(&mrp, sizeof (mac_resource_props_t));
+ dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name,
+ flags, &status);
+
+ if (dip == NULL)
+ return (status);
+
+ if (vdp->vd_val == RESET_VAL) {
+ switch (dip->pr_num) {
+ case MAC_PROP_MAXBW:
+ mrp.mrp_maxbw = MRP_MAXBW_RESETVAL;
+ mrp.mrp_mask = MRP_MAXBW;
+ break;
+ case MAC_PROP_PRIO:
+ mrp.mrp_priority = MPL_RESET;
+ mrp.mrp_mask = MRP_PRIORITY;
+ break;
+ default:
+ free(dip);
+ return (DLADM_STATUS_BADARG);
+ }
+ } else {
+ switch (dip->pr_num) {
+ case MAC_PROP_MAXBW:
+ bcopy((void *)vdp->vd_val, &mrp.mrp_maxbw,
+ sizeof (uint64_t));
+ mrp.mrp_mask = MRP_MAXBW;
+ break;
+ case MAC_PROP_PRIO:
+ bcopy((void *)vdp->vd_val, &mrp.mrp_priority,
+ sizeof (mac_priority_level_t));
+ mrp.mrp_mask = MRP_PRIORITY;
+ break;
+ default:
+ free(dip);
+ return (DLADM_STATUS_BADARG);
+ }
+ }
+
+ (void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
+ status = i_dladm_macprop(dip, B_TRUE);
+ free(dip);
+ return (status);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_set_cpus(prop_desc_t *pdp, datalink_id_t linkid, val_desc_t *vdp,
+ uint_t val_cnt, uint_t flags, datalink_media_t media)
+{
+ mac_resource_props_t mrp;
+ dladm_status_t status;
+ dld_ioc_macprop_t *dip;
+ datalink_class_t class;
+
+ /*
+ * CPU bindings can be set on VNIC and regular physical links.
+ * However VNICs fails the dladm_phys_info test(). So apply
+ * the phys_info test only on physical links.
+ */
+ if ((status = dladm_datalink_id2info(linkid, NULL, &class,
+ NULL, NULL, 0)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+
+ /*
+ * We set intr_cpu to -1. The interrupt will be retargetted,
+ * if possible when the setup is complete in MAC.
+ */
+ bzero(&mrp, sizeof (mac_resource_props_t));
+ mrp.mrp_mask = MRP_CPUS;
+ if (vdp != NULL && vdp->vd_val != RESET_VAL) {
+ mac_resource_props_t *vmrp;
+
+ vmrp = (mac_resource_props_t *)vdp->vd_val;
+ if (vmrp->mrp_ncpus > 0) {
+ bcopy(vmrp, &mrp, sizeof (mac_resource_props_t));
+ mrp.mrp_mask = MRP_CPUS;
+ }
+ mrp.mrp_mask |= MRP_CPUS_USERSPEC;
+ mrp.mrp_fanout_mode = MCM_CPUS;
+ mrp.mrp_intr_cpu = -1;
+ }
+
+ dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name,
+ flags, &status);
+ if (dip == NULL)
+ return (status);
+
+ (void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
+ status = i_dladm_macprop(dip, B_TRUE);
+ free(dip);
+ return (status);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_check_cpus(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
+ uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
+{
+ uint32_t cpuid;
+ int i, j, rc;
+ long nproc = sysconf(_SC_NPROCESSORS_CONF);
+ mac_resource_props_t *mrp;
+
+ mrp = malloc(sizeof (mac_resource_props_t));
+ if (mrp == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ for (i = 0; i < val_cnt; i++) {
+ errno = 0;
+ cpuid = strtol(prop_val[i], (char **)NULL, 10);
+ if (errno != 0 || cpuid >= nproc) {
+ free(mrp);
+ return (DLADM_STATUS_CPUMAX);
+ }
+ rc = p_online(cpuid, P_STATUS);
+ if (rc < 1) {
+ free(mrp);
+ return (DLADM_STATUS_CPUERR);
+ }
+ if (rc != P_ONLINE) {
+ free(mrp);
+ return (DLADM_STATUS_CPUNOTONLINE);
+ }
+ mrp->mrp_cpu[i] = cpuid;
+ }
+ mrp->mrp_ncpus = (uint32_t)val_cnt;
+
+ /* Check for duplicates */
+ for (i = 0; i < val_cnt; i++) {
+ for (j = 0; j < val_cnt; j++) {
+ if (i != j && mrp->mrp_cpu[i] == mrp->mrp_cpu[j]) {
+ free(mrp);
+ return (DLADM_STATUS_BADARG);
+ }
+ }
+ }
+ vdp->vd_val = (uintptr_t)mrp;
+
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+dladm_status_t
+do_extract_cpus(val_desc_t *vdp, void *arg, uint_t cnt)
+{
+ mac_resource_props_t *mrp = (mac_resource_props_t *)arg;
+ mac_resource_props_t *vmrp = (mac_resource_props_t *)vdp->vd_val;
+ int i;
+
+ for (i = 0; i < vmrp->mrp_ncpus; i++) {
+ mrp->mrp_cpu[i] = vmrp->mrp_cpu[i];
+ }
+ mrp->mrp_ncpus = vmrp->mrp_ncpus;
+ mrp->mrp_mask |= (MRP_CPUS|MRP_CPUS_USERSPEC);
+ mrp->mrp_fanout_mode = MCM_CPUS;
+
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+dld_priority_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
+{
+ dld_ioc_macprop_t *dip;
+ mac_resource_props_t mrp;
+ mac_priority_level_t pri;
+ dladm_status_t status;
+
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
+ if (dip == NULL)
+ return (status);
+
+ bcopy(dip->pr_val, &mrp, sizeof (mac_resource_props_t));
+ free(dip);
+
+ pri = ((mrp.mrp_mask & MRP_PRIORITY) == 0) ? MPL_HIGH :
+ mrp.mrp_priority;
+
+ (void) dladm_pri2str(pri, prop_val[0]);
+ *val_cnt = 1;
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_check_priority(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
+ uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
+{
+ mac_priority_level_t *pri;
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ if (val_cnt != 1)
+ return (DLADM_STATUS_BADVALCNT);
+
+ pri = malloc(sizeof (mac_priority_level_t));
+ if (pri == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ status = dladm_str2pri(*prop_val, pri);
+ if (status != DLADM_STATUS_OK) {
+ free(pri);
+ return (status);
+ }
+
+ if (*pri < MPL_LOW || *pri > MPL_HIGH) {
+ free(pri);
+ return (DLADM_STATUS_BADVAL);
+ }
+
+ vdp->vd_val = (uintptr_t)pri;
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+dladm_status_t
+do_extract_priority(val_desc_t *vdp, void *arg, uint_t cnt)
+{
+ mac_resource_props_t *mrp = (mac_resource_props_t *)arg;
+
+ bcopy((char *)vdp->vd_val, &mrp->mrp_priority,
+ sizeof (mac_priority_level_t));
+ mrp->mrp_mask |= MRP_PRIORITY;
+
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+do_get_autopush(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
struct dlautopush dlap;
int i, len;
@@ -1223,10 +1573,11 @@ do_get_autopush(struct prop_desc *pd, datalink_id_t linkid,
return (DLADM_STATUS_NOTDEFINED);
*val_cnt = 1;
- dip = i_dladm_get_public_prop(linkid, pd->pd_name, flags, &status);
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
if (dip == NULL) {
(*prop_val)[0] = '\0';
- goto done;
+ return (DLADM_STATUS_OK);
}
(void) memcpy(&dlap, dip->pr_val, sizeof (dlap));
@@ -1246,8 +1597,6 @@ do_get_autopush(struct prop_desc *pd, datalink_id_t linkid,
len += (strlen(AP_ANCHOR) + 1);
}
}
-
- *perm_flags = dip->pr_perm_flags;
free(dip);
done:
return (DLADM_STATUS_OK);
@@ -1292,7 +1641,7 @@ i_dladm_add_ap_module(const char *module, struct dlautopush *dlap)
*/
/* ARGSUSED */
static dladm_status_t
-do_check_autopush(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
+do_check_autopush(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
{
char *module;
@@ -1331,8 +1680,8 @@ do_check_autopush(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
/* ARGSUSED */
static dladm_status_t
-do_get_rate_common(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, uint_t id)
+do_get_rate_common(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, uint_t id, uint_t *perm_flags)
{
wl_rates_t *wrp;
uint_t i;
@@ -1363,6 +1712,7 @@ do_get_rate_common(struct prop_desc *pd, datalink_id_t linkid,
(float)wrp->wl_rates_rates[i] / 2);
}
*val_cnt = wrp->wl_rates_num;
+ *perm_flags = MAC_PROP_PERM_RW;
done:
free(wrp);
@@ -1370,29 +1720,25 @@ done:
}
static dladm_status_t
-do_get_rate_prop(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+do_get_rate_prop(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
if (media != DL_WIFI) {
- *perm_flags = MAC_PROP_PERM_READ;
- return (i_dladm_speed_get(pd, linkid, prop_val,
- val_cnt, flags));
+ return (i_dladm_speed_get(pdp, linkid, prop_val,
+ val_cnt, flags, perm_flags));
}
- *perm_flags = MAC_PROP_PERM_RW;
- return (do_get_rate_common(pd, linkid, prop_val, val_cnt,
- MAC_PROP_WL_DESIRED_RATES));
+ return (do_get_rate_common(pdp, linkid, prop_val, val_cnt,
+ MAC_PROP_WL_DESIRED_RATES, perm_flags));
}
/* ARGSUSED */
static dladm_status_t
-do_get_rate_mod(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+do_get_rate_mod(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
- *perm_flags = MAC_PROP_PERM_READ;
-
switch (media) {
case DL_ETHER:
/*
@@ -1402,8 +1748,8 @@ do_get_rate_mod(struct prop_desc *pd, datalink_id_t linkid,
return (DLADM_STATUS_NOTSUP);
case DL_WIFI:
- return (do_get_rate_common(pd, linkid, prop_val, val_cnt,
- MAC_PROP_WL_SUPPORTED_RATES));
+ return (do_get_rate_common(pdp, linkid, prop_val, val_cnt,
+ MAC_PROP_WL_SUPPORTED_RATES, perm_flags));
default:
return (DLADM_STATUS_BADARG);
}
@@ -1437,7 +1783,7 @@ do_set_rate(datalink_id_t linkid, dladm_wlan_rates_t *rates)
/* ARGSUSED */
static dladm_status_t
-do_set_rate_prop(prop_desc_t *pd, datalink_id_t linkid,
+do_set_rate_prop(prop_desc_t *pdp, datalink_id_t linkid,
val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
{
dladm_wlan_rates_t rates;
@@ -1463,7 +1809,7 @@ done:
/* ARGSUSED */
static dladm_status_t
-do_check_rate(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
+do_check_rate(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
{
int i;
@@ -1517,16 +1863,15 @@ do_get_phyconf(datalink_id_t linkid, void *buf, int buflen)
/* ARGSUSED */
static dladm_status_t
-do_get_channel_prop(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+do_get_channel_prop(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
uint32_t channel;
char buf[WLDP_BUFSIZE];
dladm_status_t status = DLADM_STATUS_OK;
wl_phy_conf_t wl_phy_conf;
- *perm_flags = MAC_PROP_PERM_READ;
if ((status = do_get_phyconf(linkid, buf, sizeof (buf)))
!= DLADM_STATUS_OK)
goto done;
@@ -1539,7 +1884,7 @@ do_get_channel_prop(struct prop_desc *pd, datalink_id_t linkid,
(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
*val_cnt = 1;
-
+ *perm_flags = MAC_PROP_PERM_READ;
done:
return (status);
}
@@ -1553,9 +1898,9 @@ do_get_powermode(datalink_id_t linkid, void *buf, int buflen)
/* ARGSUSED */
static dladm_status_t
-do_get_powermode_prop(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+do_get_powermode_prop(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
wl_ps_mode_t mode;
const char *s;
@@ -1583,12 +1928,8 @@ do_get_powermode_prop(struct prop_desc *pd, datalink_id_t linkid,
}
(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
*val_cnt = 1;
-
+ *perm_flags = MAC_PROP_PERM_RW;
done:
- if (status == DLADM_STATUS_OK)
- *perm_flags = MAC_PROP_PERM_RW;
- else
- *perm_flags = 0;
return (status);
}
@@ -1618,7 +1959,7 @@ do_set_powermode(datalink_id_t linkid, dladm_wlan_powermode_t *pm)
/* ARGSUSED */
static dladm_status_t
-do_set_powermode_prop(prop_desc_t *pd, datalink_id_t linkid,
+do_set_powermode_prop(prop_desc_t *pdp, datalink_id_t linkid,
val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
{
dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val;
@@ -1641,9 +1982,9 @@ do_get_radio(datalink_id_t linkid, void *buf, int buflen)
/* ARGSUSED */
static dladm_status_t
-do_get_radio_prop(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+do_get_radio_prop(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
wl_radio_t radio;
const char *s;
@@ -1668,12 +2009,8 @@ do_get_radio_prop(struct prop_desc *pd, datalink_id_t linkid,
}
(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
*val_cnt = 1;
-
+ *perm_flags = MAC_PROP_PERM_RW;
done:
- if (status == DLADM_STATUS_OK)
- *perm_flags = MAC_PROP_PERM_RW;
- else
- *perm_flags = 0;
return (status);
}
@@ -1698,7 +2035,7 @@ do_set_radio(datalink_id_t linkid, dladm_wlan_radio_t *radio)
/* ARGSUSED */
static dladm_status_t
-do_set_radio_prop(prop_desc_t *pd, datalink_id_t linkid,
+do_set_radio_prop(prop_desc_t *pdp, datalink_id_t linkid,
val_desc_t *vdp, uint_t val_cnt, uint_t fags, datalink_media_t media)
{
dladm_wlan_radio_t radio = (dladm_wlan_radio_t)vdp->vd_val;
@@ -1860,7 +2197,7 @@ i_dladm_buf_alloc_by_id(size_t valsize, datalink_id_t linkid,
/* ARGSUSED */
static dladm_status_t
-i_dladm_set_public_prop(prop_desc_t *pd, datalink_id_t linkid,
+i_dladm_set_public_prop(prop_desc_t *pdp, datalink_id_t linkid,
val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
{
dld_ioc_macprop_t *dip;
@@ -1870,11 +2207,11 @@ i_dladm_set_public_prop(prop_desc_t *pd, datalink_id_t linkid,
uint32_t u32;
void *val;
- dip = i_dladm_buf_alloc_by_name(0, linkid, pd->pd_name, 0, &status);
+ dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name, 0, &status);
if (dip == NULL)
return (status);
- if (pd->pd_flags & PD_CHECK_ALLOC)
+ if (pdp->pd_flags & PD_CHECK_ALLOC)
val = (void *)vdp->vd_val;
else {
/*
@@ -1931,7 +2268,7 @@ i_dladm_macprop(void *dip, boolean_t set)
static dld_ioc_macprop_t *
i_dladm_get_public_prop(datalink_id_t linkid, char *prop_name, uint_t flags,
- dladm_status_t *status)
+ dladm_status_t *status, uint_t *perm_flags)
{
dld_ioc_macprop_t *dip = NULL;
@@ -1944,12 +2281,15 @@ i_dladm_get_public_prop(datalink_id_t linkid, char *prop_name, uint_t flags,
free(dip);
return (NULL);
}
+ if (perm_flags != NULL)
+ *perm_flags = dip->pr_perm_flags;
+
return (dip);
}
/* ARGSUSED */
static dladm_status_t
-i_dladm_defmtu_check(struct prop_desc *pd, datalink_id_t linkid,
+i_dladm_defmtu_check(prop_desc_t *pdp, datalink_id_t linkid,
char **prop_val, uint_t val_cnt, val_desc_t *v, datalink_media_t media)
{
if (val_cnt != 1)
@@ -1960,9 +2300,9 @@ i_dladm_defmtu_check(struct prop_desc *pd, datalink_id_t linkid,
/* ARGSUSED */
static dladm_status_t
-i_dladm_duplex_get(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+i_dladm_duplex_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
link_duplex_t link_duplex;
dladm_status_t status;
@@ -1988,8 +2328,8 @@ i_dladm_duplex_get(struct prop_desc *pd, datalink_id_t linkid,
/* ARGSUSED */
static dladm_status_t
-i_dladm_speed_get(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, uint_t flags)
+i_dladm_speed_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, uint_t flags, uint_t *perm_flags)
{
uint64_t ifspeed = 0;
dladm_status_t status;
@@ -2006,23 +2346,26 @@ i_dladm_speed_get(struct prop_desc *pd, datalink_id_t linkid,
"%llu", ifspeed / 1000000); /* Mbps */
}
*val_cnt = 1;
+ *perm_flags = MAC_PROP_PERM_READ;
return (DLADM_STATUS_OK);
}
/* ARGSUSED */
static dladm_status_t
-i_dladm_status_get(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+i_dladm_status_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
- link_state_t link_state;
- dladm_status_t status;
- uchar_t *cp;
- dld_ioc_macprop_t *dip;
+ link_state_t link_state;
+ dladm_status_t status;
+ uchar_t *cp;
+ dld_ioc_macprop_t *dip;
- dip = i_dladm_get_public_prop(linkid, pd->pd_name, flags, &status);
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
if (status != DLADM_STATUS_OK)
return (status);
+
cp = (uchar_t *)dip->pr_val;
(void) memcpy(&link_state, cp, sizeof (link_state));
@@ -2038,25 +2381,25 @@ i_dladm_status_get(struct prop_desc *pd, datalink_id_t linkid,
break;
}
*val_cnt = 1;
- *perm_flags = dip->pr_perm_flags;
free(dip);
return (DLADM_STATUS_OK);
}
/* ARGSUSED */
static dladm_status_t
-i_dladm_binary_get(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+i_dladm_binary_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
dld_ioc_macprop_t *dip;
dladm_status_t status;
- dip = i_dladm_get_public_prop(linkid, pd->pd_name, flags, &status);
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
if (dip == NULL)
return (status);
+
(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%x", dip->pr_val[0]);
- *perm_flags = dip->pr_perm_flags;
free(dip);
*val_cnt = 1;
return (DLADM_STATUS_OK);
@@ -2064,22 +2407,23 @@ i_dladm_binary_get(struct prop_desc *pd, datalink_id_t linkid,
/* ARGSUSED */
static dladm_status_t
-i_dladm_uint32_get(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+i_dladm_uint32_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
dld_ioc_macprop_t *dip;
- uint32_t v = 0;
+ uint32_t v = 0;
uchar_t *cp;
dladm_status_t status;
- dip = i_dladm_get_public_prop(linkid, pd->pd_name, flags, &status);
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
if (dip == NULL)
return (status);
+
cp = (uchar_t *)dip->pr_val;
(void) memcpy(&v, cp, sizeof (v));
(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", v);
- *perm_flags = dip->pr_perm_flags;
free(dip);
*val_cnt = 1;
return (DLADM_STATUS_OK);
@@ -2087,18 +2431,20 @@ i_dladm_uint32_get(struct prop_desc *pd, datalink_id_t linkid,
/* ARGSUSED */
static dladm_status_t
-i_dladm_flowctl_get(struct prop_desc *pd, datalink_id_t linkid,
- char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
- uint_t *perm_flags)
+i_dladm_flowctl_get(prop_desc_t *pdp, datalink_id_t linkid,
+ char **prop_val, uint_t *val_cnt, datalink_media_t media,
+ uint_t flags, uint_t *perm_flags)
{
dld_ioc_macprop_t *dip;
link_flowctrl_t v;
dladm_status_t status;
uchar_t *cp;
- dip = i_dladm_get_public_prop(linkid, pd->pd_name, flags, &status);
+ dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
+ &status, perm_flags);
if (dip == NULL)
return (status);
+
cp = (uchar_t *)dip->pr_val;
(void) memcpy(&v, cp, sizeof (v));
switch (v) {
@@ -2115,7 +2461,6 @@ i_dladm_flowctl_get(struct prop_desc *pd, datalink_id_t linkid,
(void) sprintf(*prop_val, "bi");
break;
}
- *perm_flags = dip->pr_perm_flags;
free(dip);
*val_cnt = 1;
return (DLADM_STATUS_OK);
@@ -2220,17 +2565,7 @@ i_dladm_get_prop(datalink_id_t linkid, const char *prop_name,
if ((status = i_dladm_macprop(dip, B_FALSE)) == DLADM_STATUS_OK) {
if (type == DLADM_PROP_VAL_PERM) {
- switch (dip->pr_perm_flags) {
- case MAC_PROP_PERM_READ:
- (void) strncpy(*prop_val,
- PERM_READ_ONLY, DLADM_PROP_VAL_MAX);
- break;
- case MAC_PROP_PERM_RW:
- (void) strncpy(*prop_val,
- PERM_READ_WRITE,
- DLADM_PROP_VAL_MAX);
- break;
- }
+ (void) dladm_perm2str(dip->pr_perm_flags, *prop_val);
} else {
(void) strncpy(*prop_val, dip->pr_val,
DLADM_PROP_VAL_MAX);
@@ -2434,3 +2769,189 @@ i_dladm_wlan_set_legacy_ioctl(datalink_id_t linkid, void *buf, uint_t buflen,
free(gbuf);
return (status);
}
+
+static dladm_status_t
+link_proplist_check(dladm_arg_list_t *proplist)
+{
+ int i, j;
+ boolean_t matched;
+
+ for (i = 0; i < proplist->al_count; i++) {
+ matched = B_FALSE;
+ for (j = 0; j < DLADM_MAX_PROPS; j++) {
+ if (strcmp(proplist->al_info[i].ai_name,
+ prop_table[j].pd_name) == 0)
+ matched = B_TRUE;
+ }
+ if (!matched)
+ return (DLADM_STATUS_BADPROP);
+ }
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+dladm_parse_link_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
+{
+ dladm_status_t status;
+
+ status = dladm_parse_args(str, listp, novalues);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ status = link_proplist_check(*listp);
+ if (status != DLADM_STATUS_OK) {
+ dladm_free_props(*listp);
+ return (status);
+ }
+
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Retrieve the one link property from the database
+ */
+/*ARGSUSED*/
+static int
+i_dladm_get_one_prop(datalink_id_t linkid, const char *prop_name, void *arg)
+{
+ dladm_arg_list_t *proplist = arg;
+ dladm_arg_info_t *aip = NULL;
+
+ aip = &proplist->al_info[proplist->al_count];
+ /*
+ * it is fine to point to prop_name since prop_name points to the
+ * prop_table[n].pd_name.
+ */
+ aip->ai_name = prop_name;
+
+ (void) dladm_get_linkprop(linkid, DLADM_PROP_VAL_PERSISTENT, prop_name,
+ aip->ai_val, &aip->ai_count);
+
+ if (aip->ai_count != 0)
+ proplist->al_count++;
+
+ return (DLADM_WALK_CONTINUE);
+}
+
+
+/*
+ * Retrieve all link properties for a link from the database and
+ * return a property list.
+ */
+dladm_status_t
+dladm_link_get_proplist(datalink_id_t linkid, dladm_arg_list_t **listp)
+{
+ dladm_arg_list_t *list;
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ list = calloc(1, sizeof (dladm_arg_list_t));
+ if (list == NULL)
+ return (dladm_errno2status(errno));
+
+ status = dladm_walk_linkprop(linkid, list, i_dladm_get_one_prop);
+
+ *listp = list;
+ return (status);
+}
+
+/*
+ * Retrieve the named property from a proplist, check the value and
+ * convert to a kernel structure.
+ */
+static dladm_status_t
+i_dladm_link_proplist_extract_one(dladm_arg_list_t *proplist,
+ const char *name, void *val)
+{
+ dladm_status_t status;
+ dladm_arg_info_t *aip = NULL;
+ int i, j;
+
+ /* Find named property in proplist */
+ for (i = 0; i < proplist->al_count; i++) {
+ aip = &proplist->al_info[i];
+ if (strcasecmp(aip->ai_name, name) == 0)
+ break;
+ }
+
+ /* Property not in list */
+ if (i == proplist->al_count)
+ return (DLADM_STATUS_OK);
+
+ for (i = 0; i < DLADM_MAX_PROPS; i++) {
+ prop_desc_t *pdp = &prop_table[i];
+ val_desc_t *vdp;
+
+ vdp = malloc(sizeof (val_desc_t) * aip->ai_count);
+ if (vdp == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ if (strcasecmp(aip->ai_name, pdp->pd_name) != 0)
+ continue;
+
+ if (aip->ai_val == NULL)
+ return (DLADM_STATUS_BADARG);
+
+ /* Check property value */
+ if (pdp->pd_check != NULL) {
+ status = pdp->pd_check(pdp, 0, aip->ai_val,
+ aip->ai_count, vdp, 0);
+ } else {
+ status = DLADM_STATUS_BADARG;
+ }
+
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ for (j = 0; j < DLADM_MAX_RSRC_PROP; j++) {
+ resource_prop_t *rpp = &rsrc_prop_table[j];
+
+ if (strcasecmp(aip->ai_name, rpp->rp_name) != 0)
+ continue;
+
+ /* Extract kernel structure */
+ if (rpp->rp_extract != NULL) {
+ status = rpp->rp_extract(vdp, val,
+ aip->ai_count);
+ } else {
+ status = DLADM_STATUS_BADARG;
+ }
+ break;
+ }
+
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ break;
+ }
+ return (status);
+}
+
+/*
+ * Extract properties from a proplist and convert to mac_resource_props_t.
+ */
+dladm_status_t
+dladm_link_proplist_extract(dladm_arg_list_t *proplist,
+ mac_resource_props_t *mrp)
+{
+ dladm_status_t status = DLADM_STATUS_OK;
+
+ status = i_dladm_link_proplist_extract_one(proplist, "maxbw", mrp);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ status = i_dladm_link_proplist_extract_one(proplist, "priority", mrp);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ status = i_dladm_link_proplist_extract_one(proplist, "cpus", mrp);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ return (status);
+}
+
+static const char *
+dladm_perm2str(uint_t perm, char *buf)
+{
+ (void) snprintf(buf, DLADM_STRSIZE, "%c%c",
+ ((perm & MAC_PROP_PERM_READ) != 0) ? 'r' : '-',
+ ((perm & MAC_PROP_PERM_WRITE) != 0) ? 'w' : '-');
+ return (buf);
+}
diff --git a/usr/src/lib/libdladm/common/llib-ldladm b/usr/src/lib/libdladm/common/llib-ldladm
index a6fc19b517..ae8bb981bf 100644
--- a/usr/src/lib/libdladm/common/llib-ldladm
+++ b/usr/src/lib/libdladm/common/llib-ldladm
@@ -23,8 +23,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*LINTLIBRARY*/
/*PROTOLIB1*/
@@ -34,3 +32,5 @@
#include <libdlvnic.h>
#include <libdlvlan.h>
#include <libdlmgmt.h>
+#include <libdlflow.h>
+#include <libdlstat.h>
diff --git a/usr/src/lib/libdladm/common/mapfile-vers b/usr/src/lib/libdladm/common/mapfile-vers
index 9c61b84883..bd8d6a9eb1 100644
--- a/usr/src/lib/libdladm/common/mapfile-vers
+++ b/usr/src/lib/libdladm/common/mapfile-vers
@@ -35,7 +35,6 @@ SUNWprivate_1.1 {
dladm_valid_linkname;
dladm_mac_walk;
dladm_init_linkprop;
- dladm_get_single_mac_stat;
dladm_get_linkprop;
dladm_set_linkprop;
dladm_walk_linkprop;
@@ -44,6 +43,8 @@ SUNWprivate_1.1 {
dladm_set_secobj;
dladm_unset_secobj;
dladm_walk_secobj;
+ dladm_bw2str;
+ dladm_str2bw;
dladm_secobjclass2str;
dladm_str2secobjclass;
dladm_aggr_up;
@@ -118,12 +119,60 @@ SUNWprivate_1.1 {
dladm_wlan_wpa_set_key;
dladm_wlan_wpa_set_mlme;
dladm_vnic_create;
- dladm_vnic_modify;
dladm_vnic_delete;
dladm_vnic_info;
dladm_vnic_str2macaddrtype;
- dladm_kstat_value;
+ dladm_vnic_up;
+ dladm_walk_macaddr;
+ dladm_walk_hwgrp;
+ dladm_pri2str;
+ dladm_str2pri;
+ dladm_start_usagelog;
+ dladm_stop_usagelog;
+ dladm_walk_usage_res;
+ dladm_walk_usage_time;
+ dladm_usage_summary;
+ dladm_usage_dates;
+
+ dladm_flow_add;
+ dladm_flow_remove;
+ dladm_flow_parse_db;
+ dladm_walk_flow;
+ dladm_flow_init;
+ dladm_flow_info;
+ dladm_prefixlen2mask;
+ dladm_mask2prefixlen;
+ dladm_str2proto;
+ dladm_proto2str;
+
+ dladm_free_attrs;
+ dladm_parse_flow_attrs;
+
+ dladm_flow_attr_ip2str;
+ dladm_flow_attr_proto2str;
+ dladm_flow_attr_port2str;
+ dladm_flow_attr_dsfield2str;
+
+ dladm_free_props;
+ dladm_parse_link_props;
+ dladm_get_linkprop;
+ dladm_set_linkprop;
+ dladm_walk_linkprop;
+ dladm_parse_flow_props;
+ dladm_get_flowprop;
+ dladm_set_flowprop;
+ dladm_walk_flowprop;
+
dladm_parselink;
+
+ dladm_continuous;
+ dladm_kstat_lookup;
+ dladm_get_stats;
+ dladm_kstat_value;
+ dladm_get_single_mac_stat;
+ dladm_stats_total;
+ dladm_stats_diff;
+
local:
*;
};
diff --git a/usr/src/lib/libdladm/common/propfuncs.c b/usr/src/lib/libdladm/common/propfuncs.c
new file mode 100644
index 0000000000..74964511eb
--- /dev/null
+++ b/usr/src/lib/libdladm/common/propfuncs.c
@@ -0,0 +1,699 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dld.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libdladm_impl.h>
+#include <libdlflow_impl.h>
+
+/*
+ * XXX duplicate defines
+ */
+#define DLADM_PROP_VAL_MAX 32
+#define DLADM_MAX_PROPS 32
+
+static void
+free_props(prop_db_info_t *lip)
+{
+ prop_db_info_t *lip_next;
+ prop_val_t *lvp, *lvp_next;
+
+ for (; lip != NULL; lip = lip_next) {
+ lip_next = lip->li_nextprop;
+ for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
+ lvp_next = lvp->lv_nextval;
+ free(lvp);
+ }
+ free(lip);
+ }
+}
+
+/*
+ * Generate an entry in the property database.
+ * Each entry has this format:
+ * <name> <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
+ */
+static void
+generate_prop_line(const char *name, char *buf,
+ prop_db_info_t *listp, dladm_status_t *statusp)
+{
+ char tmpbuf[MAXLINELEN];
+ char *ptr, *lim = tmpbuf + MAXLINELEN;
+ prop_db_info_t *lip = listp;
+ prop_val_t *lvp = NULL;
+
+ /*
+ * Delete line if there are no properties left.
+ */
+ if (lip == NULL ||
+ (lip->li_val == NULL && lip->li_nextprop == NULL)) {
+ buf[0] = '\0';
+ return;
+ }
+ ptr = tmpbuf;
+ ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", name);
+ for (; lip != NULL; lip = lip->li_nextprop) {
+ /*
+ * Skip properties without values.
+ */
+ if (lip->li_val == NULL)
+ continue;
+
+ ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s=", lip->li_name);
+ for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
+ ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s%c",
+ lvp->lv_name,
+ ((lvp->lv_nextval == NULL) ? ';' : ','));
+ }
+ }
+ if (ptr > lim) {
+ *statusp = DLADM_STATUS_TOOSMALL;
+ return;
+ }
+ (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
+}
+
+/*
+ * This function is used to update or create an entry in the persistent db.
+ * process_prop_db() will first scan the db for an entry matching the
+ * specified name. If a match is found, this function is invoked with the
+ * entry's contents (buf) and its linked-list representation (listp). lsp
+ * holds the name and values of the property to be added or updated; this
+ * information will be merged with listp. Subsequently, an updated entry
+ * will be written to buf, which will in turn be written to disk by
+ * process_prop_db(). If no entry matches the specified name, listp
+ * will be NULL; a new entry will be generated in this case and it will
+ * contain only the property information in lsp.
+ */
+boolean_t
+process_prop_set(prop_db_state_t *lsp, char *buf,
+ prop_db_info_t *listp, dladm_status_t *statusp)
+{
+ dladm_status_t status;
+ prop_db_info_t *lastp = NULL, *lip = listp, *nlip = NULL;
+ prop_val_t **lvpp;
+ int i;
+
+ if (lsp->ls_propname == NULL) {
+ buf[0] = '\0';
+ return (B_FALSE);
+ }
+
+ /*
+ * Find the prop we want to change.
+ */
+ for (; lip != NULL; lip = lip->li_nextprop) {
+ if (strcmp(lip->li_name, lsp->ls_propname) == 0)
+ break;
+
+ lastp = lip;
+ }
+
+ if (lip == NULL) {
+ /*
+ * If the prop is not found, append it to the list.
+ */
+ if ((nlip = malloc(sizeof (prop_db_info_t))) == NULL) {
+ status = DLADM_STATUS_NOMEM;
+ goto fail;
+ }
+ /*
+ * nlip will need to be freed later if there is no list to
+ * append to.
+ */
+ if (lastp != NULL)
+ lastp->li_nextprop = nlip;
+ nlip->li_name = lsp->ls_propname;
+ nlip->li_nextprop = NULL;
+ nlip->li_val = NULL;
+ lvpp = &nlip->li_val;
+ } else {
+ prop_val_t *lvp, *lvp_next;
+
+ /*
+ * If the prop is found, delete the existing values from it.
+ */
+ for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
+ lvp_next = lvp->lv_nextval;
+ free(lvp);
+ }
+ lip->li_val = NULL;
+ lvpp = &lip->li_val;
+ }
+
+ /*
+ * Fill our prop with the specified values.
+ */
+ for (i = 0; i < *lsp->ls_valcntp; i++) {
+ if ((*lvpp = malloc(sizeof (prop_val_t))) == NULL) {
+ status = DLADM_STATUS_NOMEM;
+ goto fail;
+ }
+ (*lvpp)->lv_name = lsp->ls_propval[i];
+ (*lvpp)->lv_nextval = NULL;
+ lvpp = &(*lvpp)->lv_nextval;
+ }
+
+ if (listp != NULL) {
+ generate_prop_line(lsp->ls_name, buf, listp, statusp);
+ } else {
+ generate_prop_line(lsp->ls_name, buf, nlip, statusp);
+ free_props(nlip);
+ }
+ return (B_FALSE);
+
+fail:
+ *statusp = status;
+ if (listp == NULL)
+ free_props(nlip);
+
+ return (B_FALSE);
+}
+
+/*
+ * This function is used for retrieving the values for a specific property.
+ * It gets called if an entry matching the specified name exists in the db.
+ * The entry is converted into a linked-list listp. This list is then scanned
+ * for the specified property name; if a matching property exists, its
+ * associated values are copied to the array lsp->ls_propval.
+ */
+/* ARGSUSED */
+boolean_t
+process_prop_get(prop_db_state_t *lsp, char *buf,
+ prop_db_info_t *listp, dladm_status_t *statusp)
+{
+ prop_db_info_t *lip = listp;
+ prop_val_t *lvp;
+ uint_t valcnt = 0;
+
+ /*
+ * Find the prop we want to get.
+ */
+ for (; lip != NULL; lip = lip->li_nextprop) {
+ if (strcmp(lip->li_name, lsp->ls_propname) == 0)
+ break;
+ }
+ if (lip == NULL) {
+ *statusp = DLADM_STATUS_NOTFOUND;
+ return (B_FALSE);
+ }
+
+ for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
+ (void) strncpy(lsp->ls_propval[valcnt], lvp->lv_name,
+ DLADM_PROP_VAL_MAX);
+
+ if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) {
+ *statusp = DLADM_STATUS_TOOSMALL;
+ return (B_FALSE);
+ }
+ }
+ /*
+ * This function is meant to be called at most once for each call
+ * to process_prop_db(). For this reason, it's ok to overwrite
+ * the caller's valcnt array size with the actual number of values
+ * returned.
+ */
+ *lsp->ls_valcntp = valcnt;
+ return (B_FALSE);
+}
+
+/*
+ * This is used for initializing properties.
+ * Unlike the other routines, this gets called for every entry in the
+ * database. lsp->ls_name is not user-specified but instead is set to
+ * the current name being processed.
+ */
+/* ARGSUSED */
+boolean_t
+process_prop_init(prop_db_state_t *lsp, char *buf,
+ prop_db_info_t *listp, dladm_status_t *statusp)
+{
+ dladm_status_t status = DLADM_STATUS_OK;
+ prop_db_info_t *lip = listp;
+ prop_val_t *lvp;
+ uint_t valcnt, i;
+ char **propval;
+
+ for (; lip != NULL; lip = lip->li_nextprop) {
+ /*
+ * Construct the propval array and fill it with
+ * values from listp.
+ */
+ for (lvp = lip->li_val, valcnt = 0;
+ lvp != NULL; lvp = lvp->lv_nextval, valcnt++) {
+ }
+
+ propval = malloc(sizeof (char *) * valcnt);
+ if (propval == NULL) {
+ *statusp = DLADM_STATUS_NOMEM;
+ break;
+ }
+ lvp = lip->li_val;
+ for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval)
+ propval[i] = (char *)lvp->lv_name;
+
+ status = (*lsp->ls_initop)(lsp->ls_name, lip->li_name,
+ propval, valcnt, DLADM_OPT_ACTIVE, NULL);
+
+ /*
+ * We continue with initializing other properties even
+ * after encountering an error. This error will be
+ * propagated to the caller via 'statusp'.
+ */
+ if (status != DLADM_STATUS_OK)
+ *statusp = status;
+
+ free(propval);
+ }
+ return (B_TRUE);
+}
+
+static int
+parse_props(char *buf, prop_db_info_t **lipp)
+{
+ int i, len;
+ char *curr;
+ prop_db_info_t *lip = NULL;
+ prop_db_info_t **tailp = lipp;
+ prop_val_t *lvp = NULL;
+ prop_val_t **vtailp = NULL;
+
+ curr = buf;
+ len = strlen(buf);
+ for (i = 0; i < len; i++) {
+ char c = buf[i];
+ boolean_t match = (c == '=' || c == ',' || c == ';');
+
+ /*
+ * Move to the next character if there is no match and
+ * if we have not reached the last character.
+ */
+ if (!match && i != len - 1)
+ continue;
+
+ if (match) {
+ /*
+ * Nul-terminate the string pointed to by 'curr'.
+ */
+ buf[i] = '\0';
+ if (*curr == '\0')
+ goto fail;
+ }
+
+ if (lip != NULL) {
+ /*
+ * We get here after we have processed the "<prop>="
+ * pattern. The pattern we are now interested in is
+ * "<val0>,<val1>,...,<valn>;". For each value we
+ * find, a prop_val_t will be allocated and
+ * added to the current 'lip'.
+ */
+ if (c == '=')
+ goto fail;
+
+ lvp = malloc(sizeof (*lvp));
+ if (lvp == NULL)
+ goto fail;
+
+ lvp->lv_name = curr;
+ lvp->lv_nextval = NULL;
+ *vtailp = lvp;
+ vtailp = &lvp->lv_nextval;
+
+ if (c == ';') {
+ tailp = &lip->li_nextprop;
+ vtailp = NULL;
+ lip = NULL;
+ }
+ } else {
+ /*
+ * lip == NULL indicates that 'curr' must be refering
+ * to a property name. We allocate a new prop_db_info_t
+ * append it to the list given by the caller.
+ */
+ if (c != '=')
+ goto fail;
+
+ lip = malloc(sizeof (*lip));
+ if (lip == NULL)
+ goto fail;
+
+ lip->li_name = curr;
+ lip->li_val = NULL;
+ lip->li_nextprop = NULL;
+ *tailp = lip;
+ vtailp = &lip->li_val;
+ }
+ curr = buf + i + 1;
+ }
+ /*
+ * The list must be non-empty and the last character must be ';'.
+ */
+ if (*lipp == NULL || lip != NULL)
+ goto fail;
+
+ return (0);
+
+fail:
+ free_props(*lipp);
+ *lipp = NULL;
+ return (-1);
+}
+
+static boolean_t
+process_prop_line(prop_db_state_t *lsp, char *buf,
+ dladm_status_t *statusp)
+{
+ prop_db_info_t *lip = NULL;
+ int i, len, llen;
+ char *str, *lasts;
+ boolean_t cont, noname = B_FALSE;
+
+ /*
+ * Skip leading spaces, blank lines, and comments.
+ */
+ len = strlen(buf);
+ for (i = 0; i < len; i++) {
+ if (!isspace(buf[i]))
+ break;
+ }
+ if (i == len || buf[i] == '#')
+ return (B_TRUE);
+
+ str = buf + i;
+ if (lsp->ls_name != NULL) {
+ /*
+ * Skip names we're not interested in.
+ * Note that strncmp() and isspace() are used here
+ * instead of strtok() and strcmp() because we don't
+ * want to modify buf in case it does not contain the
+ * specified name.
+ */
+ llen = strlen(lsp->ls_name);
+ if (strncmp(str, lsp->ls_name, llen) != 0 ||
+ !isspace(str[llen]))
+ return (B_TRUE);
+ } else {
+ /*
+ * If a name is not specified, find the name
+ * and assign it to lsp->ls_name.
+ */
+ if (strtok_r(str, " \n\t", &lasts) == NULL)
+ goto fail;
+
+ llen = strlen(str);
+ lsp->ls_name = str;
+ noname = B_TRUE;
+ }
+ str += llen + 1;
+ if (str >= buf + len)
+ goto fail;
+
+ /*
+ * Now find the list of properties.
+ */
+ if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
+ goto fail;
+
+ if (parse_props(str, &lip) < 0)
+ goto fail;
+
+ cont = (*lsp->ls_op)(lsp, buf, lip, statusp);
+ free_props(lip);
+ if (noname)
+ lsp->ls_name = NULL;
+ return (cont);
+
+fail:
+ free_props(lip);
+ if (noname)
+ lsp->ls_name = NULL;
+
+ /*
+ * Delete corrupted line.
+ */
+ buf[0] = '\0';
+ return (B_TRUE);
+}
+
+dladm_status_t
+process_prop_db(void *arg, FILE *fp, FILE *nfp)
+{
+ prop_db_state_t *lsp = arg;
+ dladm_status_t status = DLADM_STATUS_OK;
+ char buf[MAXLINELEN];
+ boolean_t cont = B_TRUE;
+
+ /*
+ * This loop processes each line of the configuration file.
+ * buf can potentially be modified by process_prop_line().
+ * If this is a write operation and buf is not truncated, buf will
+ * be written to disk. process_prop_line() will no longer be
+ * called after it returns B_FALSE; at which point the remainder
+ * of the file will continue to be read and, if necessary, written
+ * to disk as well.
+ */
+ while (fgets(buf, MAXLINELEN, fp) != NULL) {
+ if (cont)
+ cont = process_prop_line(lsp, buf, &status);
+
+ if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
+ status = dladm_errno2status(errno);
+ break;
+ }
+ }
+
+ if (status != DLADM_STATUS_OK || !cont)
+ return (status);
+
+ if (lsp->ls_op == process_prop_set) {
+ /*
+ * If the specified name is not found above, we add the
+ * name and its properties to the configuration file.
+ */
+ (void) (*lsp->ls_op)(lsp, buf, NULL, &status);
+ if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF)
+ status = dladm_errno2status(errno);
+ }
+
+ if (lsp->ls_op == process_prop_get)
+ status = DLADM_STATUS_NOTFOUND;
+
+ return (status);
+}
+
+dladm_status_t
+i_dladm_get_prop_temp(const char *name, prop_type_t type,
+ const char *prop_name, char **prop_val, uint_t *val_cntp,
+ prop_table_t *prop_tbl)
+{
+ int i;
+ dladm_status_t status;
+ uint_t cnt;
+ fprop_desc_t *pdp;
+
+ if (name == NULL || prop_name == NULL || prop_val == NULL ||
+ val_cntp == NULL || *val_cntp == 0)
+ return (DLADM_STATUS_BADARG);
+
+ for (i = 0; i < prop_tbl->pt_size; i++)
+ if (strcasecmp(prop_name, prop_tbl->pt_table[i].pd_name) == 0)
+ break;
+
+ if (i == prop_tbl->pt_size)
+ return (DLADM_STATUS_NOTFOUND);
+
+ pdp = &prop_tbl->pt_table[i];
+ status = DLADM_STATUS_OK;
+
+ switch (type) {
+ case DLADM_PROP_VAL_CURRENT:
+ status = pdp->pd_get(name, prop_val, val_cntp);
+ break;
+ case DLADM_PROP_VAL_DEFAULT:
+ if (pdp->pd_defval.vd_name == NULL) {
+ status = DLADM_STATUS_NOTSUP;
+ break;
+ }
+ (void) strcpy(*prop_val, pdp->pd_defval.vd_name);
+ *val_cntp = 1;
+ break;
+
+ case DLADM_PROP_VAL_MODIFIABLE:
+ if (pdp->pd_getmod != NULL) {
+ status = pdp->pd_getmod(name, prop_val, val_cntp);
+ break;
+ }
+ cnt = pdp->pd_nmodval;
+ if (cnt == 0) {
+ status = DLADM_STATUS_NOTSUP;
+ } else if (cnt > *val_cntp) {
+ status = DLADM_STATUS_TOOSMALL;
+ } else {
+ for (i = 0; i < cnt; i++) {
+ (void) strcpy(prop_val[i],
+ pdp->pd_modval[i].vd_name);
+ }
+ *val_cntp = cnt;
+ }
+ break;
+ default:
+ status = DLADM_STATUS_BADARG;
+ break;
+ }
+
+ return (status);
+}
+
+static dladm_status_t
+i_dladm_set_one_prop_temp(const char *name, fprop_desc_t *pdp, char **prop_val,
+ uint_t val_cnt, uint_t flags)
+{
+ dladm_status_t status;
+ val_desc_t *vdp = NULL;
+ uint_t cnt;
+
+ if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0)
+ return (DLADM_STATUS_TEMPONLY);
+
+ if (pdp->pd_set == NULL)
+ return (DLADM_STATUS_PROPRDONLY);
+
+ if (prop_val != NULL) {
+ if (pdp->pd_check != NULL)
+ status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp);
+ else
+ status = DLADM_STATUS_BADARG;
+
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ cnt = val_cnt;
+ } else {
+ if (pdp->pd_defval.vd_name == NULL)
+ return (DLADM_STATUS_NOTSUP);
+
+ if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ (void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
+ cnt = 1;
+ }
+
+ status = pdp->pd_set(name, vdp, cnt);
+
+ free(vdp);
+ return (status);
+}
+
+dladm_status_t
+i_dladm_set_prop_temp(const char *name, const char *prop_name, char **prop_val,
+ uint_t val_cnt, uint_t flags, char **errprop, prop_table_t *prop_tbl)
+{
+ int i;
+ dladm_status_t status = DLADM_STATUS_OK;
+ boolean_t found = B_FALSE;
+
+ for (i = 0; i < prop_tbl->pt_size; i++) {
+ fprop_desc_t *pdp = &prop_tbl->pt_table[i];
+ dladm_status_t s;
+
+ if (prop_name != NULL &&
+ (strcasecmp(prop_name, pdp->pd_name) != 0))
+ continue;
+
+ found = B_TRUE;
+ s = i_dladm_set_one_prop_temp(name, pdp, prop_val, val_cnt,
+ flags);
+
+ if (prop_name != NULL) {
+ status = s;
+ break;
+ } else {
+ if (s != DLADM_STATUS_OK &&
+ s != DLADM_STATUS_NOTSUP) {
+ if (errprop != NULL)
+ *errprop = pdp->pd_name;
+ status = s;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ status = DLADM_STATUS_NOTFOUND;
+
+ return (status);
+}
+
+boolean_t
+i_dladm_is_prop_temponly(const char *prop_name, char **errprop,
+ prop_table_t *prop_tbl)
+{
+ int i;
+
+ if (prop_name == NULL)
+ return (B_FALSE);
+
+ for (i = 0; i < prop_tbl->pt_size; i++) {
+ fprop_desc_t *pdp = &prop_tbl->pt_table[i];
+
+ if (strcasecmp(prop_name, pdp->pd_name) != 0)
+ continue;
+
+ if (errprop != NULL)
+ *errprop = pdp->pd_name;
+
+ if (pdp->pd_temponly)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+void
+dladm_free_props(dladm_arg_list_t *list)
+{
+ dladm_free_args(list);
+}
+
+dladm_status_t
+dladm_parse_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
+{
+ if (dladm_parse_args(str, listp, novalues) != DLADM_STATUS_OK)
+ goto fail;
+
+ return (DLADM_STATUS_OK);
+
+fail:
+ dladm_free_args(*listp);
+ return (DLADM_STATUS_PROP_PARSE_ERR);
+}
diff --git a/usr/src/lib/libdladm/common/usage.c b/usr/src/lib/libdladm/common/usage.c
new file mode 100644
index 0000000000..07ef7bbb22
--- /dev/null
+++ b/usr/src/lib/libdladm/common/usage.c
@@ -0,0 +1,1437 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <exacct.h>
+#include <libdladm.h>
+
+#define TIMEBUFLEN 20
+#define GBIT 1000000000
+#define MBIT 1000000
+#define KBIT 1000
+
+#define NET_RESET_TOT(tbytes, ttime, tibytes, tobytes, step) { \
+ (step) = 1; \
+ (tbytes) = 0; \
+ (ttime) = 0; \
+ (tibytes) = 0; \
+ (tobytes) = 0; \
+ }
+
+/* Flow/Link Descriptor */
+typedef struct net_desc_s {
+ char net_desc_name[LIFNAMSIZ];
+ char net_desc_devname[LIFNAMSIZ];
+ uchar_t net_desc_ehost[ETHERADDRL];
+ uchar_t net_desc_edest[ETHERADDRL];
+ ushort_t net_desc_vlan_tpid;
+ ushort_t net_desc_vlan_tci;
+ ushort_t net_desc_sap;
+ ushort_t net_desc_cpuid;
+ ushort_t net_desc_priority;
+ uint64_t net_desc_bw_limit;
+ in6_addr_t net_desc_saddr;
+ in6_addr_t net_desc_daddr;
+ boolean_t net_desc_isv4;
+ in_port_t net_desc_sport;
+ in_port_t net_desc_dport;
+ uint8_t net_desc_protocol;
+ uint8_t net_desc_dsfield;
+ boolean_t net_desc_newrec;
+} net_desc_t;
+
+/* Time structure: Year, Month, Day, Hour, Min, Sec */
+typedef struct net_time_s {
+ int net_time_yr;
+ int net_time_mon;
+ int net_time_day;
+ int net_time_hr;
+ int net_time_min;
+ int net_time_sec;
+} net_time_t;
+
+/* Flow/Link Stats */
+typedef struct net_stat_s {
+ char net_stat_name[LIFNAMSIZ];
+ uint64_t net_stat_ibytes;
+ uint64_t net_stat_obytes;
+ uint64_t net_stat_ipackets;
+ uint64_t net_stat_opackets;
+ uint64_t net_stat_ierrors;
+ uint64_t net_stat_oerrors;
+ uint64_t net_stat_tibytes;
+ uint64_t net_stat_tobytes;
+ uint64_t net_stat_tipackets;
+ uint64_t net_stat_topackets;
+ uint64_t net_stat_tierrors;
+ uint64_t net_stat_toerrors;
+ uint64_t net_stat_ctime;
+ uint64_t net_stat_tdiff;
+ net_time_t net_stat_time;
+ struct net_stat_s *net_stat_next;
+ net_desc_t *net_stat_desc;
+ boolean_t net_stat_isref;
+} net_stat_t;
+
+/* Used to create the [gnu]plot file */
+typedef struct net_plot_entry_s {
+ char *net_pe_name;
+ uint64_t net_pe_tottime;
+ uint64_t net_pe_totbytes;
+ uint64_t net_pe_totibytes;
+ uint64_t net_pe_totobytes;
+ uint64_t net_pe_lasttime;
+} net_plot_entry_t;
+
+/* Stats entry */
+typedef struct net_entry_s {
+ net_desc_t *net_entry_desc;
+ net_stat_t *net_entry_shead;
+ net_stat_t *net_entry_stail;
+ int net_entry_scount;
+ net_stat_t *net_entry_sref;
+ net_stat_t *net_entry_tstats;
+ uint64_t net_entry_ttime;
+ struct net_entry_s *net_entry_next;
+} net_entry_t;
+
+/* Time sorted list */
+typedef struct net_time_entry_s {
+ net_stat_t *my_time_stat;
+ struct net_time_entry_s *net_time_entry_next;
+ struct net_time_entry_s *net_time_entry_prev;
+} net_time_entry_t;
+
+/* The parsed table */
+typedef struct net_table_s {
+ /* List of stats */
+ net_entry_t *net_table_head;
+ net_entry_t *net_table_tail;
+ int net_entries;
+
+ /*
+ * Optimization I : List sorted by time, i.e:
+ * Time Resource ..
+ * -------------------------------
+ * 11.15.10 bge0
+ * 11.15.10 ce0
+ * 11.15.10 vnic1
+ * 11.15.15 bge0
+ * 11.15.15 ce0
+ * 11.15.15 vnic1
+ */
+ net_time_entry_t *net_time_head;
+ net_time_entry_t *net_time_tail;
+
+ /*
+ * Optimization II : List sorted by resources
+ * Time Resource ..
+ * -------------------------------
+ * 11.15.10 bge0
+ * 11.15.15 bge0
+ * 11.15.10 ce0
+ * 11.15.15 ce0
+ * 11.15.10 vnic1
+ * 11.15.15 vnic1
+ */
+ net_time_entry_t *net_ctime_head;
+ net_time_entry_t *net_ctime_tail;
+
+ /* Common to both the above (sorted) lists. */
+ int net_time_entries;
+} net_table_t;
+
+#define NET_DATE_GREATER 0
+#define NET_DATE_LESSER 1
+#define NET_DATE_EQUAL 2
+
+#define NET_TIME_GREATER 0
+#define NET_TIME_LESSER 1
+#define NET_TIME_EQUAL 2
+
+#ifndef _LP64
+#define FMT_UINT64 "%-15llu"
+#else
+#define FMT_UINT64 "%-15lu"
+#endif
+
+/*
+ * Given a timebuf of the form M/D/Y,H:M:S break it into individual elements.
+ */
+static void
+dissect_time(char *tbuf, net_time_t *nt)
+{
+ char *d;
+ char *t;
+ char *dd;
+ char *h;
+ char *endp;
+
+ if (tbuf == NULL || nt == NULL)
+ return;
+
+ d = strtok(tbuf, ","); /* Date */
+ t = strtok(NULL, ","); /* Time */
+
+ /* Month */
+ dd = strtok(d, "/");
+ if (dd == NULL)
+ return;
+ nt->net_time_mon = strtol(dd, &endp, 10);
+
+ /* Day */
+ dd = strtok(NULL, "/");
+ if (dd == NULL)
+ return;
+ nt->net_time_day = strtol(dd, &endp, 10);
+
+ /* Year */
+ dd = strtok(NULL, "/");
+ if (dd == NULL)
+ return;
+ nt->net_time_yr = strtol(dd, &endp, 10);
+ if (strlen(dd) <= 2)
+ nt->net_time_yr += 2000;
+
+ if (t == NULL)
+ return;
+
+ /* Hour */
+ h = strtok(t, ":");
+ if (h == NULL)
+ return;
+ nt->net_time_hr = strtol(h, &endp, 10);
+
+ /* Min */
+ h = strtok(NULL, ":");
+ if (h == NULL)
+ return;
+ nt->net_time_min = strtol(h, &endp, 10);
+
+ /* Sec */
+ h = strtok(NULL, ":");
+ if (h == NULL)
+ return;
+ nt->net_time_sec = strtol(h, &endp, 10);
+}
+
+/* Get a stat item from an object in the exacct file */
+static void
+add_stat_item(ea_object_t *o, net_stat_t *ns)
+{
+ switch (o->eo_catalog & EXT_TYPE_MASK) {
+ case EXT_STRING:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_NAME) {
+ (void) strncpy(ns->net_stat_name, o->eo_item.ei_string,
+ strlen(o->eo_item.ei_string));
+ }
+ break;
+ case EXT_UINT64:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_CURTIME) {
+ time_t _time;
+ char timebuf[TIMEBUFLEN];
+
+ ns->net_stat_ctime = o->eo_item.ei_uint64;
+ _time = ns->net_stat_ctime;
+ (void) strftime(timebuf, sizeof (timebuf),
+ "%m/%d/%Y,%T\n", localtime(&_time));
+ dissect_time(timebuf, &ns->net_stat_time);
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_IBYTES) {
+ ns->net_stat_ibytes = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_OBYTES) {
+ ns->net_stat_obytes = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_IPKTS) {
+ ns->net_stat_ipackets = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_OPKTS) {
+ ns->net_stat_opackets = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_IERRPKTS) {
+ ns->net_stat_ierrors = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_OERRPKTS) {
+ ns->net_stat_oerrors = o->eo_item.ei_uint64;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* Get a description item from an object in the exacct file */
+static void
+add_desc_item(ea_object_t *o, net_desc_t *nd)
+{
+ switch (o->eo_catalog & EXT_TYPE_MASK) {
+ case EXT_STRING:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_NAME) {
+ (void) strncpy(nd->net_desc_name, o->eo_item.ei_string,
+ strlen(o->eo_item.ei_string));
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_DEVNAME) {
+ (void) strncpy(nd->net_desc_devname,
+ o->eo_item.ei_string, strlen(o->eo_item.ei_string));
+ }
+ break;
+ case EXT_UINT8:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_PROTOCOL) {
+ nd->net_desc_protocol = o->eo_item.ei_uint8;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_DSFIELD) {
+ nd->net_desc_dsfield = o->eo_item.ei_uint8;
+ }
+ break;
+ case EXT_UINT16:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_SPORT) {
+ nd->net_desc_sport = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_DPORT) {
+ nd->net_desc_dport = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_SAP) {
+ nd->net_desc_sap = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_VLAN_TPID) {
+ nd->net_desc_vlan_tpid = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_VLAN_TCI) {
+ nd->net_desc_vlan_tci = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_PRIORITY) {
+ nd->net_desc_priority = o->eo_item.ei_uint16;
+ }
+ break;
+ case EXT_UINT32:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4SADDR ||
+ (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4DADDR) {
+ struct in_addr addr;
+
+ addr.s_addr = htonl(o->eo_item.ei_uint32);
+
+ if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_V4SADDR) {
+ IN6_INADDR_TO_V4MAPPED(&addr,
+ &nd->net_desc_saddr);
+ } else {
+ IN6_INADDR_TO_V4MAPPED(&addr,
+ &nd->net_desc_daddr);
+ }
+ }
+ break;
+ case EXT_UINT64:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_BWLIMIT)
+ nd->net_desc_bw_limit = o->eo_item.ei_uint64;
+ break;
+ case EXT_RAW:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6SADDR ||
+ (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6DADDR) {
+ in6_addr_t addr;
+
+ addr = *(in6_addr_t *)o->eo_item.ei_raw;
+ if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_V6SADDR) {
+ nd->net_desc_saddr = addr;
+ } else {
+ nd->net_desc_daddr = addr;
+ }
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_EHOST) {
+ bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_ehost,
+ ETHERADDRL);
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_EDEST) {
+ bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_edest,
+ ETHERADDRL);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* Add a description item to the table */
+static dladm_status_t
+add_desc_to_tbl(net_table_t *net_table, net_desc_t *nd)
+{
+ net_entry_t *ne;
+
+ if ((ne = calloc(1, sizeof (net_entry_t))) == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ if ((ne->net_entry_tstats = calloc(1, sizeof (net_stat_t))) == NULL) {
+ free(ne);
+ return (DLADM_STATUS_NOMEM);
+ }
+
+ ne->net_entry_desc = nd;
+ ne->net_entry_shead = NULL;
+ ne->net_entry_stail = NULL;
+ ne->net_entry_scount = 0;
+
+ if (net_table->net_table_head == NULL) {
+ net_table->net_table_head = ne;
+ net_table->net_table_tail = ne;
+ } else {
+ net_table->net_table_tail->net_entry_next = ne;
+ net_table->net_table_tail = ne;
+ }
+ net_table->net_entries++;
+ return (DLADM_STATUS_OK);
+}
+
+/* Compare dates and return if t1 is equal, greater or lesser than t2 */
+static int
+compare_date(net_time_t *t1, net_time_t *t2)
+{
+ if (t1->net_time_yr == t2->net_time_yr &&
+ t1->net_time_mon == t2->net_time_mon &&
+ t1->net_time_day == t2->net_time_day) {
+ return (NET_DATE_EQUAL);
+ }
+ if (t1->net_time_yr > t2->net_time_yr ||
+ (t1->net_time_yr == t2->net_time_yr &&
+ t1->net_time_mon > t2->net_time_mon) ||
+ (t1->net_time_yr == t2->net_time_yr &&
+ t1->net_time_mon == t2->net_time_mon &&
+ t1->net_time_day > t2->net_time_day)) {
+ return (NET_DATE_GREATER);
+ }
+ return (NET_DATE_LESSER);
+}
+
+/* Compare times and return if t1 is equal, greater or lesser than t2 */
+static int
+compare_time(net_time_t *t1, net_time_t *t2)
+{
+ int cd;
+
+ cd = compare_date(t1, t2);
+
+ if (cd == NET_DATE_GREATER) {
+ return (NET_TIME_GREATER);
+ } else if (cd == NET_DATE_LESSER) {
+ return (NET_TIME_LESSER);
+ } else {
+ if (t1->net_time_hr == t2->net_time_hr &&
+ t1->net_time_min == t2->net_time_min &&
+ t1->net_time_sec == t2->net_time_sec) {
+ return (NET_TIME_EQUAL);
+ }
+ if (t1->net_time_hr > t2->net_time_hr ||
+ (t1->net_time_hr == t2->net_time_hr &&
+ t1->net_time_min > t2->net_time_min) ||
+ (t1->net_time_hr == t2->net_time_hr &&
+ t1->net_time_min == t2->net_time_min &&
+ t1->net_time_sec > t2->net_time_sec)) {
+ return (NET_TIME_GREATER);
+ }
+ }
+ return (NET_TIME_LESSER);
+}
+
+/*
+ * Given a start and end time and start and end entries check if the
+ * times are within the range, and adjust, if needed.
+ */
+static dladm_status_t
+chk_time_bound(net_time_t *s, net_time_t *e, net_time_t *sns,
+ net_time_t *ens)
+{
+ if (s != NULL && e != NULL) {
+ if (compare_time(s, e) == NET_TIME_GREATER)
+ return (DLADM_STATUS_BADTIMEVAL);
+ }
+ if (s != NULL) {
+ if (compare_time(s, sns) == NET_TIME_LESSER) {
+ s->net_time_yr = sns->net_time_yr;
+ s->net_time_mon = sns->net_time_mon;
+ s->net_time_day = sns->net_time_day;
+ s->net_time_hr = sns->net_time_hr;
+ s->net_time_min = sns->net_time_min;
+ s->net_time_sec = sns->net_time_sec;
+ }
+ }
+ if (e != NULL) {
+ if (compare_time(e, ens) == NET_TIME_GREATER) {
+ e->net_time_yr = ens->net_time_yr;
+ e->net_time_mon = ens->net_time_mon;
+ e->net_time_day = ens->net_time_day;
+ e->net_time_hr = ens->net_time_hr;
+ e->net_time_min = ens->net_time_min;
+ e->net_time_sec = ens->net_time_sec;
+ }
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Given a start and end time (strings), convert them into net_time_t
+ * and also check for the range given the head and tail of the list.
+ * If stime is lower then head or etime is greated than tail, adjust.
+ */
+static dladm_status_t
+get_time_range(net_time_entry_t *head, net_time_entry_t *tail,
+ net_time_t *st, net_time_t *et, char *stime, char *etime)
+{
+ bzero(st, sizeof (net_time_t));
+ bzero(et, sizeof (net_time_t));
+
+ if (stime == NULL && etime == NULL)
+ return (0);
+
+ if (stime != NULL)
+ dissect_time(stime, st);
+ if (etime != NULL)
+ dissect_time(etime, et);
+
+ if (stime != NULL || etime != NULL) {
+ return (chk_time_bound(stime == NULL ? NULL : st,
+ etime == NULL ? NULL : et,
+ &head->my_time_stat->net_stat_time,
+ &tail->my_time_stat->net_stat_time));
+ }
+ return (0);
+}
+
+/*
+ * Walk the list from a given starting point and return when we find
+ * an entry that is greater or equal to st. lasttime will point to the
+ * previous time entry.
+ */
+static void
+get_starting_point(net_time_entry_t *head, net_time_entry_t **start,
+ net_time_t *st, char *stime, uint64_t *lasttime)
+{
+ net_time_entry_t *next = head;
+
+ if (head == NULL) {
+ *start = NULL;
+ return;
+ }
+ if (stime == NULL) {
+ *start = head;
+ *lasttime = head->my_time_stat->net_stat_ctime;
+ return;
+ }
+ *start = NULL;
+ while (next != NULL) {
+ if (compare_time(st,
+ &next->my_time_stat->net_stat_time) != NET_TIME_LESSER) {
+ *lasttime = next->my_time_stat->net_stat_ctime;
+ next = next->net_time_entry_next;
+ continue;
+ }
+ *start = next;
+ break;
+ }
+}
+
+/*
+ * Point entry (pe) functions
+ */
+/* Clear all the counters. Done after the contents are written to the file */
+static void
+clear_pe(net_plot_entry_t *pe, int entries, int *pentries)
+{
+ int count;
+
+ for (count = 0; count < entries; count++) {
+ pe[count].net_pe_totbytes = 0;
+ pe[count].net_pe_totibytes = 0;
+ pe[count].net_pe_totobytes = 0;
+ pe[count].net_pe_tottime = 0;
+ }
+ *pentries = 0;
+}
+
+/* Update an entry in the point entry table */
+static void
+update_pe(net_plot_entry_t *pe, net_stat_t *nns, int nentries,
+ int *pentries, uint64_t lasttime)
+{
+ int count;
+
+ for (count = 0; count < nentries; count++) {
+ if ((strlen(nns->net_stat_name) ==
+ strlen(pe[count].net_pe_name)) &&
+ (strncmp(pe[count].net_pe_name, nns->net_stat_name,
+ strlen(nns->net_stat_name)) == 0)) {
+ break;
+ }
+ }
+ if (count == nentries)
+ return;
+
+ if (pe[count].net_pe_totbytes == 0)
+ pe[count].net_pe_lasttime = lasttime;
+
+ pe[count].net_pe_totbytes += nns->net_stat_ibytes +
+ nns->net_stat_obytes;
+ pe[count].net_pe_tottime += nns->net_stat_tdiff;
+ pe[count].net_pe_totibytes += nns->net_stat_ibytes;
+ pe[count].net_pe_totobytes += nns->net_stat_obytes;
+ (*pentries)++;
+}
+
+/* Flush the contents of the point entry table to the file. */
+static void
+add_pe_to_file(int (*fn)(dladm_usage_t *, void *), net_plot_entry_t *pe,
+ net_stat_t *ns, int entries, void *arg)
+{
+ int count;
+ dladm_usage_t usage;
+ uint64_t tottime;
+
+ bcopy(&ns->net_stat_ctime, &usage.du_etime, sizeof (usage.du_etime));
+ for (count = 0; count < entries; count++) {
+ bcopy(pe[count].net_pe_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&pe[count].net_pe_lasttime, &usage.du_stime,
+ sizeof (usage.du_stime));
+ usage.du_rbytes = pe[count].net_pe_totibytes;
+ usage.du_obytes = pe[count].net_pe_totobytes;
+ tottime = pe[count].net_pe_tottime;
+ usage.du_bandwidth = (tottime > 0) ?
+ ((pe[count].net_pe_totbytes * 8) / tottime) : 0;
+ usage.du_last = (count == entries-1);
+ fn(&usage, arg);
+ }
+}
+
+/*
+ * Net entry functions
+ */
+static net_entry_t *
+get_ne_from_table(net_table_t *net_table, char *name)
+{
+ int count;
+ net_desc_t *nd;
+ net_entry_t *ne = net_table->net_table_head;
+
+ for (count = 0; count < net_table->net_entries; count++) {
+ nd = ne->net_entry_desc;
+ if ((strlen(name) == strlen(nd->net_desc_name)) &&
+ (strncmp(name, nd->net_desc_name, strlen(name)) == 0)) {
+ return (ne);
+ }
+ ne = ne->net_entry_next;
+ }
+ return (NULL);
+}
+
+/* Get the entry for the descriptor, if it exists */
+static net_desc_t *
+get_ndesc(net_table_t *net_table, net_desc_t *nd)
+{
+ int count;
+ net_desc_t *nd1;
+ net_entry_t *ne = net_table->net_table_head;
+
+ for (count = 0; count < net_table->net_entries; count++) {
+ nd1 = ne->net_entry_desc;
+ if (strlen(nd1->net_desc_name) == strlen(nd->net_desc_name) &&
+ strlen(nd1->net_desc_devname) ==
+ strlen(nd->net_desc_devname) &&
+ strncmp(nd1->net_desc_name, nd->net_desc_name,
+ strlen(nd1->net_desc_name)) == 0 &&
+ strncmp(nd1->net_desc_devname, nd->net_desc_devname,
+ strlen(nd1->net_desc_devname)) == 0 &&
+ bcmp(nd1->net_desc_ehost, nd->net_desc_ehost,
+ ETHERADDRL) == 0 &&
+ bcmp(nd1->net_desc_edest, nd->net_desc_edest,
+ ETHERADDRL) == 0 &&
+ nd1->net_desc_vlan_tpid == nd->net_desc_vlan_tpid &&
+ nd1->net_desc_vlan_tci == nd->net_desc_vlan_tci &&
+ nd1->net_desc_sap == nd->net_desc_sap &&
+ nd1->net_desc_cpuid == nd->net_desc_cpuid &&
+ nd1->net_desc_priority == nd->net_desc_priority &&
+ nd1->net_desc_bw_limit == nd->net_desc_bw_limit &&
+ nd1->net_desc_sport == nd->net_desc_sport &&
+ nd1->net_desc_dport == nd->net_desc_dport &&
+ nd1->net_desc_protocol == nd->net_desc_protocol &&
+ nd1->net_desc_dsfield == nd->net_desc_dsfield &&
+ IN6_ARE_ADDR_EQUAL(&nd1->net_desc_saddr,
+ &nd->net_desc_saddr) &&
+ IN6_ARE_ADDR_EQUAL(&nd1->net_desc_daddr,
+ &nd->net_desc_daddr)) {
+ return (nd1);
+ }
+ ne = ne->net_entry_next;
+ }
+ return (NULL);
+}
+
+/*
+ * Update the stat entries. The stats in the file are cumulative, so in order
+ * to have increments, we maintain a reference stat entry, which contains
+ * the stats when the record was first written and a total stat entry, which
+ * maintains the running count. When we want to add a stat entry, if it
+ * the reference stat entry, we don't come here. For subsequent entries,
+ * we get the increment by subtracting the current value from the reference
+ * stat and the total stat.
+ */
+static void
+update_stats(net_stat_t *ns1, net_entry_t *ne, net_stat_t *ref)
+{
+
+ /* get the increment */
+ ns1->net_stat_ibytes -= (ref->net_stat_ibytes + ref->net_stat_tibytes);
+ ns1->net_stat_obytes -= (ref->net_stat_obytes + ref->net_stat_tobytes);
+ ns1->net_stat_ipackets -= (ref->net_stat_ipackets +
+ ref->net_stat_tipackets);
+ ns1->net_stat_opackets -= (ref->net_stat_opackets +
+ ref->net_stat_topackets);
+ ns1->net_stat_ierrors -= (ref->net_stat_ierrors +
+ ref->net_stat_tierrors);
+ ns1->net_stat_oerrors -= (ref->net_stat_oerrors +
+ ref->net_stat_toerrors);
+
+ /* update total bytes */
+ ref->net_stat_tibytes += ns1->net_stat_ibytes;
+ ref->net_stat_tobytes += ns1->net_stat_obytes;
+ ref->net_stat_tipackets += ns1->net_stat_ipackets;
+ ref->net_stat_topackets += ns1->net_stat_opackets;
+ ref->net_stat_tierrors += ns1->net_stat_ierrors;
+ ref->net_stat_toerrors += ns1->net_stat_oerrors;
+
+ ne->net_entry_tstats->net_stat_ibytes += ns1->net_stat_ibytes;
+ ne->net_entry_tstats->net_stat_obytes += ns1->net_stat_obytes;
+ ne->net_entry_tstats->net_stat_ipackets += ns1->net_stat_ipackets;
+ ne->net_entry_tstats->net_stat_opackets += ns1->net_stat_opackets;
+ ne->net_entry_tstats->net_stat_ierrors += ns1->net_stat_ierrors;
+ ne->net_entry_tstats->net_stat_oerrors += ns1->net_stat_oerrors;
+}
+
+/* Add the stat entry into the table */
+static dladm_status_t
+add_stat_to_tbl(net_table_t *net_table, net_stat_t *ns)
+{
+ net_entry_t *ne;
+
+ ne = get_ne_from_table(net_table, ns->net_stat_name);
+ if (ne == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ /* Ptr to flow desc */
+ ns->net_stat_desc = ne->net_entry_desc;
+ if (ns->net_stat_desc->net_desc_newrec) {
+ ns->net_stat_desc->net_desc_newrec = B_FALSE;
+ ns->net_stat_isref = B_TRUE;
+ ne->net_entry_sref = ns;
+ } else if (ns->net_stat_ibytes < ne->net_entry_sref->net_stat_tibytes ||
+ (ns->net_stat_obytes < ne->net_entry_sref->net_stat_tobytes)) {
+ ns->net_stat_isref = B_TRUE;
+ ne->net_entry_sref = ns;
+ } else {
+ ns->net_stat_isref = B_FALSE;
+ update_stats(ns, ne, ne->net_entry_sref);
+ }
+ if (ne->net_entry_shead == NULL) {
+ ne->net_entry_shead = ns;
+ ne->net_entry_stail = ns;
+ } else {
+ if (!ns->net_stat_isref) {
+ ne->net_entry_ttime += (ns->net_stat_ctime -
+ ne->net_entry_stail->net_stat_ctime);
+ ns->net_stat_tdiff = ns->net_stat_ctime -
+ ne->net_entry_stail->net_stat_ctime;
+ }
+ ne->net_entry_stail->net_stat_next = ns;
+ ne->net_entry_stail = ns;
+ }
+
+ ne->net_entry_scount++;
+ return (DLADM_STATUS_OK);
+}
+
+/* Add a flow/link descriptor record to the table */
+static dladm_status_t
+add_desc(net_table_t *net_table, ea_file_t *ef, int nobjs)
+{
+ net_desc_t *nd;
+ net_desc_t *dnd;
+ int count;
+ ea_object_t scratch;
+
+ if ((nd = calloc(1, sizeof (net_desc_t))) == NULL)
+ return (DLADM_STATUS_NOMEM);
+ nd->net_desc_newrec = B_TRUE;
+
+ for (count = 0; count < nobjs; count++) {
+ if (ea_get_object(ef, &scratch) == -1) {
+ free(nd);
+ return (DLADM_STATUS_NOMEM);
+ }
+ add_desc_item(&scratch, nd);
+ }
+ if ((dnd = get_ndesc(net_table, nd)) != NULL) {
+ dnd->net_desc_newrec = B_TRUE;
+ free(nd);
+ return (DLADM_STATUS_OK);
+ }
+ if (add_desc_to_tbl(net_table, nd) != 0) {
+ free(nd);
+ return (DLADM_STATUS_NOMEM);
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/* Make an entry into the time sorted list */
+static void
+addto_time_list(net_table_t *net_table, net_time_entry_t *nt,
+ net_time_entry_t *ntc)
+{
+ net_stat_t *ns = nt->my_time_stat;
+ net_stat_t *ns1;
+ net_time_entry_t *end;
+ net_time_t *t1;
+ int count;
+
+ t1 = &ns->net_stat_time;
+
+ net_table->net_time_entries++;
+
+ if (net_table->net_time_head == NULL) {
+ net_table->net_time_head = nt;
+ net_table->net_time_tail = nt;
+ } else {
+ net_table->net_time_tail->net_time_entry_next = nt;
+ nt->net_time_entry_prev = net_table->net_time_tail;
+ net_table->net_time_tail = nt;
+ }
+
+ if (net_table->net_ctime_head == NULL) {
+ net_table->net_ctime_head = ntc;
+ net_table->net_ctime_tail = ntc;
+ } else {
+ end = net_table->net_ctime_tail;
+ count = 0;
+ while (count < net_table->net_time_entries - 1) {
+ ns1 = end->my_time_stat;
+ /* Just add it to the tail */
+ if (compare_date(t1, &ns1->net_stat_time) ==
+ NET_DATE_GREATER) {
+ break;
+ }
+ if ((strlen(ns1->net_stat_name) ==
+ strlen(ns->net_stat_name)) &&
+ (strncmp(ns1->net_stat_name, ns->net_stat_name,
+ strlen(ns1->net_stat_name)) == 0)) {
+ ntc->net_time_entry_next =
+ end->net_time_entry_next;
+ if (end->net_time_entry_next != NULL) {
+ end->net_time_entry_next->
+ net_time_entry_prev = ntc;
+ } else {
+ net_table->net_ctime_tail = ntc;
+ }
+ end->net_time_entry_next = ntc;
+ ntc->net_time_entry_prev = end;
+ return;
+ }
+ count++;
+ end = end->net_time_entry_prev;
+ }
+ net_table->net_ctime_tail->net_time_entry_next = ntc;
+ ntc->net_time_entry_prev = net_table->net_ctime_tail;
+ net_table->net_ctime_tail = ntc;
+ }
+}
+
+/* Add stat entry into the lists */
+static dladm_status_t
+add_stats(net_table_t *net_table, ea_file_t *ef, int nobjs)
+{
+ net_stat_t *ns;
+ int count;
+ ea_object_t scratch;
+ net_time_entry_t *nt;
+ net_time_entry_t *ntc;
+
+ if ((ns = calloc(1, sizeof (net_stat_t))) == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ if ((nt = calloc(1, sizeof (net_time_entry_t))) == NULL) {
+ free(ns);
+ return (DLADM_STATUS_NOMEM);
+ }
+ if ((ntc = calloc(1, sizeof (net_time_entry_t))) == NULL) {
+ free(ns);
+ free(nt);
+ return (DLADM_STATUS_NOMEM);
+ }
+
+ nt->my_time_stat = ns;
+ ntc->my_time_stat = ns;
+
+ for (count = 0; count < nobjs; count++) {
+ if (ea_get_object(ef, &scratch) == -1) {
+ free(ns);
+ free(nt);
+ free(ntc);
+ return (DLADM_STATUS_NOMEM);
+ }
+ add_stat_item(&scratch, ns);
+ }
+ if (add_stat_to_tbl(net_table, ns) != 0) {
+ free(ns);
+ free(nt);
+ free(ntc);
+ return (DLADM_STATUS_NOMEM);
+ }
+ addto_time_list(net_table, nt, ntc);
+ return (DLADM_STATUS_OK);
+}
+
+/* Free the entire table */
+static void
+free_logtable(net_table_t *net_table)
+{
+ net_entry_t *head;
+ net_entry_t *next;
+ net_stat_t *ns;
+ net_stat_t *ns1;
+ net_time_entry_t *thead;
+ net_time_entry_t *tnext;
+
+ thead = net_table->net_time_head;
+ while (thead != NULL) {
+ thead->my_time_stat = NULL;
+ tnext = thead->net_time_entry_next;
+ thead->net_time_entry_next = NULL;
+ thead->net_time_entry_prev = NULL;
+ free(thead);
+ thead = tnext;
+ }
+ net_table->net_time_head = NULL;
+ net_table->net_time_tail = NULL;
+
+ thead = net_table->net_ctime_head;
+ while (thead != NULL) {
+ thead->my_time_stat = NULL;
+ tnext = thead->net_time_entry_next;
+ thead->net_time_entry_next = NULL;
+ thead->net_time_entry_prev = NULL;
+ free(thead);
+ thead = tnext;
+ }
+ net_table->net_ctime_head = NULL;
+ net_table->net_ctime_tail = NULL;
+
+ net_table->net_time_entries = 0;
+
+ head = net_table->net_table_head;
+ while (head != NULL) {
+ next = head->net_entry_next;
+ head->net_entry_next = NULL;
+ ns = head->net_entry_shead;
+ while (ns != NULL) {
+ ns1 = ns->net_stat_next;
+ free(ns);
+ ns = ns1;
+ }
+ head->net_entry_scount = 0;
+ head->net_entry_sref = NULL;
+ free(head->net_entry_desc);
+ free(head->net_entry_tstats);
+ free(head);
+ head = next;
+ }
+ net_table->net_table_head = NULL;
+ net_table->net_table_tail = NULL;
+ net_table->net_time_entries = 0;
+ free(net_table);
+}
+
+/* Parse the exacct file, and return the parsed table. */
+static void *
+parse_logfile(char *file, int logtype, dladm_status_t *status)
+{
+ ea_file_t ef;
+ ea_object_t scratch;
+ net_table_t *net_table;
+
+ *status = DLADM_STATUS_OK;
+ if ((net_table = calloc(1, sizeof (net_table_t))) == NULL) {
+ *status = DLADM_STATUS_NOMEM;
+ return (NULL);
+ }
+ if (ea_open(&ef, file, NULL, 0, O_RDONLY, 0) == -1) {
+ *status = DLADM_STATUS_BADARG;
+ free(net_table);
+ return (NULL);
+ }
+ bzero(&scratch, sizeof (ea_object_t));
+ while (ea_get_object(&ef, &scratch) != -1) {
+ if (scratch.eo_type != EO_GROUP) {
+ (void) ea_free_item(&scratch, EUP_ALLOC);
+ (void) bzero(&scratch, sizeof (ea_object_t));
+ continue;
+ }
+ /* Read Link Desc/Stat records */
+ if (logtype == DLADM_LOGTYPE_FLOW) {
+ /* Flow Descriptor */
+ if ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC) {
+ (void) add_desc(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ /* Flow Stats */
+ } else if ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS) {
+ (void) add_stats(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ }
+ } else if (logtype == DLADM_LOGTYPE_LINK) {
+ /* Link Descriptor */
+ if ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_LINK_DESC) {
+ (void) add_desc(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ /* Link Stats */
+ } else if ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_LINK_STATS) {
+ (void) add_stats(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ }
+ } else {
+ if (((scratch.eo_catalog & EXD_DATA_MASK) ==
+ EXD_GROUP_NET_LINK_DESC) || ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC)) {
+ (void) add_desc(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ } else if (((scratch.eo_catalog & EXD_DATA_MASK) ==
+ EXD_GROUP_NET_LINK_STATS) || ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS)) {
+ (void) add_stats(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ }
+ }
+ (void) ea_free_item(&scratch, EUP_ALLOC);
+ (void) bzero(&scratch, sizeof (ea_object_t));
+ }
+
+ (void) ea_close(&ef);
+ return ((void *)net_table);
+}
+
+/*
+ * Walk the ctime list. This is used when looking for usage records
+ * based on a "resource" name.
+ */
+dladm_status_t
+dladm_walk_usage_res(int (*fn)(dladm_usage_t *, void *), int logtype,
+ char *logfile, char *resource, char *stime, char *etime, void *arg)
+{
+ net_table_t *net_table;
+ net_time_t st, et;
+ net_time_entry_t *start;
+ net_stat_t *ns = NULL;
+ net_stat_t *nns;
+ uint64_t tot_time = 0;
+ uint64_t last_time;
+ uint64_t tot_bytes = 0;
+ uint64_t tot_ibytes = 0;
+ uint64_t tot_obytes = 0;
+ boolean_t gotstart = B_FALSE;
+ dladm_status_t status;
+ dladm_usage_t usage;
+ int step = 1;
+
+ /* Parse the log file */
+ net_table = parse_logfile(logfile, logtype, &status);
+ if (net_table == NULL)
+ return (status);
+
+ if (net_table->net_entries == 0)
+ return (DLADM_STATUS_OK);
+ start = net_table->net_ctime_head;
+
+ /* Time range */
+ status = get_time_range(net_table->net_ctime_head,
+ net_table->net_ctime_tail, &st, &et, stime, etime);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ while (start != NULL) {
+ nns = start->my_time_stat;
+
+ /* Get to the resource we are interested in */
+ if ((strlen(resource) != strlen(nns->net_stat_name)) ||
+ (strncmp(resource, nns->net_stat_name,
+ strlen(nns->net_stat_name)) != 0)) {
+ start = start->net_time_entry_next;
+ continue;
+ }
+
+ /* Find the first record */
+ if (!gotstart) {
+ get_starting_point(start, &start, &st, stime,
+ &last_time);
+ if (start == NULL)
+ break;
+ nns = start->my_time_stat;
+ gotstart = B_TRUE;
+ }
+
+ /* Write one entry and return if we are out of the range */
+ if (etime != NULL && compare_time(&nns->net_stat_time, &et)
+ == NET_TIME_GREATER) {
+ if (tot_bytes != 0) {
+ bcopy(ns->net_stat_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&last_time, &usage.du_stime,
+ sizeof (usage.du_stime));
+ bcopy(&ns->net_stat_ctime, &usage.du_etime,
+ sizeof (usage.du_etime));
+ usage.du_rbytes = tot_ibytes;
+ usage.du_obytes = tot_obytes;
+ usage.du_bandwidth = tot_bytes*8/tot_time;
+ usage.du_last = B_TRUE;
+ fn(&usage, arg);
+ }
+ return (DLADM_STATUS_OK);
+ }
+
+ /*
+ * If this is a reference entry, just print what we have
+ * and proceed.
+ */
+ if (nns->net_stat_isref) {
+ if (tot_bytes != 0) {
+ bcopy(&nns->net_stat_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&nns->net_stat_ctime, &usage.du_stime,
+ sizeof (usage.du_stime));
+ usage.du_rbytes = tot_ibytes;
+ usage.du_obytes = tot_obytes;
+ usage.du_bandwidth = tot_bytes*8/tot_time;
+ usage.du_last = B_TRUE;
+ fn(&usage, arg);
+ NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
+ tot_obytes, step);
+ }
+ last_time = nns->net_stat_ctime;
+ start = start->net_time_entry_next;
+ continue;
+ }
+
+ ns = nns;
+ if (--step == 0) {
+ tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
+ tot_ibytes += ns->net_stat_ibytes;
+ tot_obytes += ns->net_stat_obytes;
+ tot_time += ns->net_stat_tdiff;
+ bcopy(&ns->net_stat_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&last_time, &usage.du_stime,
+ sizeof (usage.du_stime));
+ bcopy(&ns->net_stat_ctime, &usage.du_etime,
+ sizeof (usage.du_etime));
+ usage.du_rbytes = tot_ibytes;
+ usage.du_obytes = tot_obytes;
+ usage.du_bandwidth = tot_bytes*8/tot_time;
+ usage.du_last = B_TRUE;
+ fn(&usage, arg);
+
+ NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
+ tot_obytes, step);
+ last_time = ns->net_stat_ctime;
+ } else {
+ tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
+ tot_ibytes += ns->net_stat_ibytes;
+ tot_obytes += ns->net_stat_obytes;
+ tot_time += ns->net_stat_tdiff;
+ }
+ start = start->net_time_entry_next;
+ }
+
+ if (tot_bytes != 0) {
+ bcopy(&ns->net_stat_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&last_time, &usage.du_stime,
+ sizeof (usage.du_stime));
+ bcopy(&ns->net_stat_ctime, &usage.du_etime,
+ sizeof (usage.du_etime));
+ usage.du_rbytes = tot_ibytes;
+ usage.du_obytes = tot_obytes;
+ usage.du_bandwidth = tot_bytes*8/tot_time;
+ usage.du_last = B_TRUE;
+ fn(&usage, arg);
+ }
+
+ free_logtable(net_table);
+ return (status);
+}
+
+/*
+ * Walk the time sorted list if a resource is not specified.
+ */
+dladm_status_t
+dladm_walk_usage_time(int (*fn)(dladm_usage_t *, void *), int logtype,
+ char *logfile, char *stime, char *etime, void *arg)
+{
+ net_table_t *net_table;
+ net_time_entry_t *start;
+ net_stat_t *ns = NULL, *nns;
+ net_time_t st, et, *t1;
+ net_desc_t *nd;
+ net_entry_t *ne;
+ net_plot_entry_t *pe;
+ int count;
+ int step = 1;
+ int nentries = 0, pentries = 0;
+ uint64_t last_time;
+ dladm_status_t status;
+
+ /* Parse the log file */
+ net_table = parse_logfile(logfile, logtype, &status);
+ if (net_table == NULL)
+ return (status);
+
+ if (net_table->net_entries == 0)
+ return (DLADM_STATUS_OK);
+ start = net_table->net_time_head;
+
+ /* Find the first and last records and starting point */
+ status = get_time_range(net_table->net_time_head,
+ net_table->net_time_tail, &st, &et, stime, etime);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ get_starting_point(start, &start, &st, stime, &last_time);
+ /*
+ * Could assert to be non-null, since get_time_range()
+ * would have adjusted.
+ */
+ if (start == NULL)
+ return (DLADM_STATUS_BADTIMEVAL);
+
+ /*
+ * Collect entries for all resources in a time slot before
+ * writing to the file.
+ */
+ nentries = net_table->net_entries;
+
+ pe = malloc(sizeof (net_plot_entry_t) * net_table->net_entries + 1);
+ if (pe == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ ne = net_table->net_table_head;
+ for (count = 0; count < nentries; count++) {
+ nd = ne->net_entry_desc;
+ pe[count].net_pe_name = nd->net_desc_name;
+ ne = ne->net_entry_next;
+ }
+
+ clear_pe(pe, nentries, &pentries);
+
+ /* Write header to file */
+ /* add_pe_to_file(fn, pe, ns, nentries, arg); */
+
+ t1 = &start->my_time_stat->net_stat_time;
+
+ while (start != NULL) {
+
+ nns = start->my_time_stat;
+ /*
+ * We have crossed the time boundary, check if we need to
+ * print out now.
+ */
+ if (compare_time(&nns->net_stat_time, t1) ==
+ NET_TIME_GREATER) {
+ /* return if we are out of the range */
+ if (etime != NULL &&
+ compare_time(&nns->net_stat_time, &et) ==
+ NET_TIME_GREATER) {
+ if (pentries > 0) {
+ add_pe_to_file(fn, pe, ns, nentries,
+ arg);
+ clear_pe(pe, nentries, &pentries);
+ }
+ free(pe);
+ return (DLADM_STATUS_OK);
+ }
+ /* update the stats from the ns. */
+ t1 = &nns->net_stat_time;
+ last_time = ns->net_stat_ctime;
+ if (--step == 0) {
+ if (pentries > 0) {
+ add_pe_to_file(fn, pe, ns, nentries,
+ arg);
+ clear_pe(pe, nentries, &pentries);
+ }
+ step = 1;
+ }
+ }
+
+ /*
+ * if this is a reference entry, just print what we have
+ * for this resource and proceed. We will end up writing
+ * the stats for all the entries when we hit a ref element,
+ * which means 'steps' for some might not be accurate, but
+ * that is fine, the alternative is to write only the
+ * resource for which we hit a reference entry.
+ */
+ if (nns->net_stat_isref) {
+ if (pentries > 0) {
+ add_pe_to_file(fn, pe, ns, nentries, arg);
+ clear_pe(pe, nentries, &pentries);
+ }
+ step = 1;
+ } else {
+ update_pe(pe, nns, nentries, &pentries, last_time);
+ }
+ ns = nns;
+ start = start->net_time_entry_next;
+ }
+
+ if (pentries > 0)
+ add_pe_to_file(fn, pe, ns, nentries, arg);
+
+ free(pe);
+ free_logtable(net_table);
+
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+dladm_usage_summary(int (*fn)(dladm_usage_t *, void *), int logtype,
+ char *logfile, void *arg)
+{
+ net_table_t *net_table;
+ net_entry_t *ne;
+ net_desc_t *nd;
+ net_stat_t *ns;
+ int count;
+ dladm_usage_t usage;
+ dladm_status_t status;
+
+ /* Parse the log file */
+ net_table = parse_logfile(logfile, logtype, &status);
+ if (net_table == NULL)
+ return (status);
+
+ if (net_table->net_entries == 0)
+ return (DLADM_STATUS_OK);
+
+ ne = net_table->net_table_head;
+ for (count = 0; count < net_table->net_entries; count++) {
+ ns = ne->net_entry_tstats;
+ nd = ne->net_entry_desc;
+
+ if (ns->net_stat_ibytes + ns->net_stat_obytes == 0)
+ continue;
+ bcopy(&nd->net_desc_name, &usage.du_name,
+ sizeof (usage.du_name));
+ usage.du_duration = ne->net_entry_ttime;
+ usage.du_ipackets = ns->net_stat_ipackets;
+ usage.du_rbytes = ns->net_stat_ibytes;
+ usage.du_opackets = ns->net_stat_opackets;
+ usage.du_obytes = ns->net_stat_obytes;
+ usage.du_bandwidth =
+ (ns->net_stat_ibytes + ns->net_stat_obytes) * 8 /
+ usage.du_duration;
+ usage.du_last = (count == net_table->net_entries-1);
+ fn(&usage, arg);
+
+ ne = ne->net_entry_next;
+ }
+
+ free_logtable(net_table);
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Walk the ctime list and display the dates of the records.
+ */
+dladm_status_t
+dladm_usage_dates(int (*fn)(dladm_usage_t *, void *), int logtype,
+ char *logfile, char *resource, void *arg)
+{
+ net_table_t *net_table;
+ net_time_entry_t *start;
+ net_stat_t *nns;
+ net_time_t st;
+ net_time_t *lasttime = NULL;
+ uint64_t last_time;
+ boolean_t gotstart = B_FALSE;
+ dladm_status_t status;
+ dladm_usage_t usage;
+
+ /* Parse the log file */
+ net_table = parse_logfile(logfile, logtype, &status);
+ if (net_table == NULL)
+ return (status);
+
+ if (net_table->net_entries == 0)
+ return (DLADM_STATUS_OK);
+
+ start = net_table->net_ctime_head;
+
+ while (start != NULL) {
+ nns = start->my_time_stat;
+
+ /* get to the resource we are interested in */
+ if (resource != NULL) {
+ if ((strlen(resource) != strlen(nns->net_stat_name)) ||
+ (strncmp(resource, nns->net_stat_name,
+ strlen(nns->net_stat_name)) != 0)) {
+ start = start->net_time_entry_next;
+ continue;
+ }
+ }
+
+ /* get the starting point in the logfile */
+ if (!gotstart) {
+ get_starting_point(start, &start, &st, NULL,
+ &last_time);
+ if (start == NULL)
+ break;
+ nns = start->my_time_stat;
+ gotstart = B_TRUE;
+ }
+
+ if (lasttime == NULL ||
+ compare_date(&nns->net_stat_time, lasttime) ==
+ NET_DATE_GREATER) {
+ bzero(&usage, sizeof (dladm_usage_t));
+ bcopy(&nns->net_stat_ctime, &usage.du_stime,
+ sizeof (usage.du_stime));
+ fn(&usage, arg);
+ lasttime = &nns->net_stat_time;
+ }
+
+ start = start->net_time_entry_next;
+ continue;
+ }
+
+ free_logtable(net_table);
+ return (status);
+}