diff options
author | Eric Cheng <none@none> | 2008-12-04 18:16:10 -0800 |
---|---|---|
committer | Eric Cheng <none@none> | 2008-12-04 18:16:10 -0800 |
commit | da14cebe459d3275048785f25bd869cb09b5307f (patch) | |
tree | a394d2c61ec4d7591782a4a5db4e3a157c3ca89a /usr/src/lib/libdladm | |
parent | 03361682bf38acf5bcc36ee83a0d6277731eee68 (diff) | |
download | illumos-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')
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); +} |