2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#include <fcntl.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <exacct.h>
2N/A#include <net/if.h>
2N/A#include <sys/ethernet.h>
2N/A#include <libdladm.h>
2N/A
2N/A#define TIMEBUFLEN 20
2N/A#define GBIT 1000000000
2N/A#define MBIT 1000000
2N/A#define KBIT 1000
2N/A
2N/A#define NET_RESET_TOT(tbytes, ttime, tibytes, tobytes, step) { \
2N/A (step) = 1; \
2N/A (tbytes) = 0; \
2N/A (ttime) = 0; \
2N/A (tibytes) = 0; \
2N/A (tobytes) = 0; \
2N/A }
2N/A
2N/A/* Flow/Link Descriptor */
2N/Atypedef struct net_desc_s {
2N/A char net_desc_name[LIFNAMSIZ];
2N/A char net_desc_devname[LIFNAMSIZ];
2N/A uchar_t net_desc_ehost[ETHERADDRL];
2N/A uchar_t net_desc_edest[ETHERADDRL];
2N/A ushort_t net_desc_vlan_tpid;
2N/A ushort_t net_desc_vlan_tci;
2N/A ushort_t net_desc_sap;
2N/A ushort_t net_desc_cpuid;
2N/A ushort_t net_desc_priority;
2N/A uint64_t net_desc_bw_limit;
2N/A in6_addr_t net_desc_saddr;
2N/A in6_addr_t net_desc_daddr;
2N/A boolean_t net_desc_isv4;
2N/A in_port_t net_desc_sport;
2N/A in_port_t net_desc_dport;
2N/A uint8_t net_desc_protocol;
2N/A uint8_t net_desc_dsfield;
2N/A boolean_t net_desc_newrec;
2N/A} net_desc_t;
2N/A
2N/A/* Time structure: Year, Month, Day, Hour, Min, Sec */
2N/Atypedef struct net_time_s {
2N/A int net_time_yr;
2N/A int net_time_mon;
2N/A int net_time_day;
2N/A int net_time_hr;
2N/A int net_time_min;
2N/A int net_time_sec;
2N/A} net_time_t;
2N/A
2N/A/* Flow/Link Stats */
2N/Atypedef struct net_stat_s {
2N/A char net_stat_name[LIFNAMSIZ];
2N/A uint64_t net_stat_ibytes;
2N/A uint64_t net_stat_obytes;
2N/A uint64_t net_stat_ipackets;
2N/A uint64_t net_stat_opackets;
2N/A uint64_t net_stat_ierrors;
2N/A uint64_t net_stat_oerrors;
2N/A uint64_t net_stat_tibytes;
2N/A uint64_t net_stat_tobytes;
2N/A uint64_t net_stat_tipackets;
2N/A uint64_t net_stat_topackets;
2N/A uint64_t net_stat_tierrors;
2N/A uint64_t net_stat_toerrors;
2N/A uint64_t net_stat_ctime;
2N/A uint64_t net_stat_tdiff;
2N/A net_time_t net_stat_time;
2N/A struct net_stat_s *net_stat_next;
2N/A net_desc_t *net_stat_desc;
2N/A boolean_t net_stat_isref;
2N/A} net_stat_t;
2N/A
2N/A/* Used to create the [gnu]plot file */
2N/Atypedef struct net_plot_entry_s {
2N/A char *net_pe_name;
2N/A uint64_t net_pe_tottime;
2N/A uint64_t net_pe_totbytes;
2N/A uint64_t net_pe_totibytes;
2N/A uint64_t net_pe_totobytes;
2N/A uint64_t net_pe_lasttime;
2N/A} net_plot_entry_t;
2N/A
2N/A/* Stats entry */
2N/Atypedef struct net_entry_s {
2N/A net_desc_t *net_entry_desc;
2N/A net_stat_t *net_entry_shead;
2N/A net_stat_t *net_entry_stail;
2N/A int net_entry_scount;
2N/A net_stat_t *net_entry_sref;
2N/A net_stat_t *net_entry_tstats;
2N/A uint64_t net_entry_ttime;
2N/A struct net_entry_s *net_entry_next;
2N/A} net_entry_t;
2N/A
2N/A/* Time sorted list */
2N/Atypedef struct net_time_entry_s {
2N/A net_stat_t *my_time_stat;
2N/A struct net_time_entry_s *net_time_entry_next;
2N/A struct net_time_entry_s *net_time_entry_prev;
2N/A} net_time_entry_t;
2N/A
2N/A/* The parsed table */
2N/Atypedef struct net_table_s {
2N/A /* List of stats */
2N/A net_entry_t *net_table_head;
2N/A net_entry_t *net_table_tail;
2N/A int net_entries;
2N/A
2N/A /*
2N/A * Optimization I : List sorted by time, i.e:
2N/A * Time Resource ..
2N/A * -------------------------------
2N/A * 11.15.10 bge0
2N/A * 11.15.10 ce0
2N/A * 11.15.10 vnic1
2N/A * 11.15.15 bge0
2N/A * 11.15.15 ce0
2N/A * 11.15.15 vnic1
2N/A */
2N/A net_time_entry_t *net_time_head;
2N/A net_time_entry_t *net_time_tail;
2N/A
2N/A /*
2N/A * Optimization II : List sorted by resources
2N/A * Time Resource ..
2N/A * -------------------------------
2N/A * 11.15.10 bge0
2N/A * 11.15.15 bge0
2N/A * 11.15.10 ce0
2N/A * 11.15.15 ce0
2N/A * 11.15.10 vnic1
2N/A * 11.15.15 vnic1
2N/A */
2N/A net_time_entry_t *net_ctime_head;
2N/A net_time_entry_t *net_ctime_tail;
2N/A
2N/A /* Common to both the above (sorted) lists. */
2N/A int net_time_entries;
2N/A} net_table_t;
2N/A
2N/A#define NET_DATE_GREATER 0
2N/A#define NET_DATE_LESSER 1
2N/A#define NET_DATE_EQUAL 2
2N/A
2N/A#define NET_TIME_GREATER 0
2N/A#define NET_TIME_LESSER 1
2N/A#define NET_TIME_EQUAL 2
2N/A
2N/A#ifndef _LP64
2N/A#define FMT_UINT64 "%-15llu"
2N/A#else
2N/A#define FMT_UINT64 "%-15lu"
2N/A#endif
2N/A
2N/A/*
2N/A * Given a timebuf of the form M/D/Y,H:M:S break it into individual elements.
2N/A */
2N/Astatic void
2N/Adissect_time(char *tbuf, net_time_t *nt)
2N/A{
2N/A char *d;
2N/A char *t;
2N/A char *dd;
2N/A char *h;
2N/A char *endp;
2N/A
2N/A if (tbuf == NULL || nt == NULL)
2N/A return;
2N/A
2N/A d = strtok(tbuf, ","); /* Date */
2N/A t = strtok(NULL, ","); /* Time */
2N/A
2N/A /* Month */
2N/A dd = strtok(d, "/");
2N/A if (dd == NULL)
2N/A return;
2N/A nt->net_time_mon = strtol(dd, &endp, 10);
2N/A
2N/A /* Day */
2N/A dd = strtok(NULL, "/");
2N/A if (dd == NULL)
2N/A return;
2N/A nt->net_time_day = strtol(dd, &endp, 10);
2N/A
2N/A /* Year */
2N/A dd = strtok(NULL, "/");
2N/A if (dd == NULL)
2N/A return;
2N/A nt->net_time_yr = strtol(dd, &endp, 10);
2N/A if (strlen(dd) <= 2)
2N/A nt->net_time_yr += 2000;
2N/A
2N/A if (t == NULL)
2N/A return;
2N/A
2N/A /* Hour */
2N/A h = strtok(t, ":");
2N/A if (h == NULL)
2N/A return;
2N/A nt->net_time_hr = strtol(h, &endp, 10);
2N/A
2N/A /* Min */
2N/A h = strtok(NULL, ":");
2N/A if (h == NULL)
2N/A return;
2N/A nt->net_time_min = strtol(h, &endp, 10);
2N/A
2N/A /* Sec */
2N/A h = strtok(NULL, ":");
2N/A if (h == NULL)
2N/A return;
2N/A nt->net_time_sec = strtol(h, &endp, 10);
2N/A}
2N/A
2N/A/* Get a stat item from an object in the exacct file */
2N/Astatic void
2N/Aadd_stat_item(ea_object_t *o, net_stat_t *ns)
2N/A{
2N/A switch (o->eo_catalog & EXT_TYPE_MASK) {
2N/A case EXT_STRING:
2N/A if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_NAME) {
2N/A (void) strncpy(ns->net_stat_name, o->eo_item.ei_string,
2N/A strlen(o->eo_item.ei_string));
2N/A }
2N/A break;
2N/A case EXT_UINT64:
2N/A if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_CURTIME) {
2N/A time_t _time;
2N/A char timebuf[TIMEBUFLEN];
2N/A
2N/A ns->net_stat_ctime = o->eo_item.ei_uint64;
2N/A _time = ns->net_stat_ctime;
2N/A (void) strftime(timebuf, sizeof (timebuf),
2N/A "%m/%d/%Y,%T\n", localtime(&_time));
2N/A dissect_time(timebuf, &ns->net_stat_time);
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_STATS_IBYTES) {
2N/A ns->net_stat_ibytes = o->eo_item.ei_uint64;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_STATS_OBYTES) {
2N/A ns->net_stat_obytes = o->eo_item.ei_uint64;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_STATS_IPKTS) {
2N/A ns->net_stat_ipackets = o->eo_item.ei_uint64;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_STATS_OPKTS) {
2N/A ns->net_stat_opackets = o->eo_item.ei_uint64;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_STATS_IERRPKTS) {
2N/A ns->net_stat_ierrors = o->eo_item.ei_uint64;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_STATS_OERRPKTS) {
2N/A ns->net_stat_oerrors = o->eo_item.ei_uint64;
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A}
2N/A
2N/A/* Get a description item from an object in the exacct file */
2N/Astatic void
2N/Aadd_desc_item(ea_object_t *o, net_desc_t *nd)
2N/A{
2N/A switch (o->eo_catalog & EXT_TYPE_MASK) {
2N/A case EXT_STRING:
2N/A if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_NAME) {
2N/A (void) strncpy(nd->net_desc_name, o->eo_item.ei_string,
2N/A strlen(o->eo_item.ei_string));
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_DEVNAME) {
2N/A (void) strncpy(nd->net_desc_devname,
2N/A o->eo_item.ei_string, strlen(o->eo_item.ei_string));
2N/A }
2N/A break;
2N/A case EXT_UINT8:
2N/A if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_PROTOCOL) {
2N/A nd->net_desc_protocol = o->eo_item.ei_uint8;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_DSFIELD) {
2N/A nd->net_desc_dsfield = o->eo_item.ei_uint8;
2N/A }
2N/A break;
2N/A case EXT_UINT16:
2N/A if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_SPORT) {
2N/A nd->net_desc_sport = o->eo_item.ei_uint16;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_DPORT) {
2N/A nd->net_desc_dport = o->eo_item.ei_uint16;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_SAP) {
2N/A nd->net_desc_sap = o->eo_item.ei_uint16;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_VLAN_TPID) {
2N/A nd->net_desc_vlan_tpid = o->eo_item.ei_uint16;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_VLAN_TCI) {
2N/A nd->net_desc_vlan_tci = o->eo_item.ei_uint16;
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_PRIORITY) {
2N/A nd->net_desc_priority = o->eo_item.ei_uint16;
2N/A }
2N/A break;
2N/A case EXT_UINT32:
2N/A if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4SADDR ||
2N/A (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4DADDR) {
2N/A struct in_addr addr;
2N/A
2N/A addr.s_addr = htonl(o->eo_item.ei_uint32);
2N/A
2N/A if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_V4SADDR) {
2N/A IN6_INADDR_TO_V4MAPPED(&addr,
2N/A &nd->net_desc_saddr);
2N/A } else {
2N/A IN6_INADDR_TO_V4MAPPED(&addr,
2N/A &nd->net_desc_daddr);
2N/A }
2N/A }
2N/A break;
2N/A case EXT_UINT64:
2N/A if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_BWLIMIT)
2N/A nd->net_desc_bw_limit = o->eo_item.ei_uint64;
2N/A break;
2N/A case EXT_RAW:
2N/A if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6SADDR ||
2N/A (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6DADDR) {
2N/A in6_addr_t addr;
2N/A
2N/A addr = *(in6_addr_t *)o->eo_item.ei_raw;
2N/A if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_V6SADDR) {
2N/A nd->net_desc_saddr = addr;
2N/A } else {
2N/A nd->net_desc_daddr = addr;
2N/A }
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_EHOST) {
2N/A bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_ehost,
2N/A ETHERADDRL);
2N/A } else if ((o->eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_NET_DESC_EDEST) {
2N/A bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_edest,
2N/A ETHERADDRL);
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A}
2N/A
2N/A/* Add a description item to the table */
2N/Astatic dladm_status_t
2N/Aadd_desc_to_tbl(net_table_t *net_table, net_desc_t *nd)
2N/A{
2N/A net_entry_t *ne;
2N/A
2N/A if ((ne = calloc(1, sizeof (net_entry_t))) == NULL)
2N/A return (DLADM_STATUS_NOMEM);
2N/A
2N/A if ((ne->net_entry_tstats = calloc(1, sizeof (net_stat_t))) == NULL) {
2N/A free(ne);
2N/A return (DLADM_STATUS_NOMEM);
2N/A }
2N/A
2N/A ne->net_entry_desc = nd;
2N/A ne->net_entry_shead = NULL;
2N/A ne->net_entry_stail = NULL;
2N/A ne->net_entry_scount = 0;
2N/A
2N/A if (net_table->net_table_head == NULL) {
2N/A net_table->net_table_head = ne;
2N/A net_table->net_table_tail = ne;
2N/A } else {
2N/A net_table->net_table_tail->net_entry_next = ne;
2N/A net_table->net_table_tail = ne;
2N/A }
2N/A net_table->net_entries++;
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/A/* Compare dates and return if t1 is equal, greater or lesser than t2 */
2N/Astatic int
2N/Acompare_date(net_time_t *t1, net_time_t *t2)
2N/A{
2N/A if (t1->net_time_yr == t2->net_time_yr &&
2N/A t1->net_time_mon == t2->net_time_mon &&
2N/A t1->net_time_day == t2->net_time_day) {
2N/A return (NET_DATE_EQUAL);
2N/A }
2N/A if (t1->net_time_yr > t2->net_time_yr ||
2N/A (t1->net_time_yr == t2->net_time_yr &&
2N/A t1->net_time_mon > t2->net_time_mon) ||
2N/A (t1->net_time_yr == t2->net_time_yr &&
2N/A t1->net_time_mon == t2->net_time_mon &&
2N/A t1->net_time_day > t2->net_time_day)) {
2N/A return (NET_DATE_GREATER);
2N/A }
2N/A return (NET_DATE_LESSER);
2N/A}
2N/A
2N/A/* Compare times and return if t1 is equal, greater or lesser than t2 */
2N/Astatic int
2N/Acompare_time(net_time_t *t1, net_time_t *t2)
2N/A{
2N/A int cd;
2N/A
2N/A cd = compare_date(t1, t2);
2N/A
2N/A if (cd == NET_DATE_GREATER) {
2N/A return (NET_TIME_GREATER);
2N/A } else if (cd == NET_DATE_LESSER) {
2N/A return (NET_TIME_LESSER);
2N/A } else {
2N/A if (t1->net_time_hr == t2->net_time_hr &&
2N/A t1->net_time_min == t2->net_time_min &&
2N/A t1->net_time_sec == t2->net_time_sec) {
2N/A return (NET_TIME_EQUAL);
2N/A }
2N/A if (t1->net_time_hr > t2->net_time_hr ||
2N/A (t1->net_time_hr == t2->net_time_hr &&
2N/A t1->net_time_min > t2->net_time_min) ||
2N/A (t1->net_time_hr == t2->net_time_hr &&
2N/A t1->net_time_min == t2->net_time_min &&
2N/A t1->net_time_sec > t2->net_time_sec)) {
2N/A return (NET_TIME_GREATER);
2N/A }
2N/A }
2N/A return (NET_TIME_LESSER);
2N/A}
2N/A
2N/A/*
2N/A * Given a start and end time and start and end entries check if the
2N/A * times are within the range, and adjust, if needed.
2N/A */
2N/Astatic dladm_status_t
2N/Achk_time_bound(net_time_t *s, net_time_t *e, net_time_t *sns,
2N/A net_time_t *ens)
2N/A{
2N/A if (s != NULL && e != NULL) {
2N/A if (compare_time(s, e) == NET_TIME_GREATER)
2N/A return (DLADM_STATUS_BADTIMEVAL);
2N/A }
2N/A if (s != NULL) {
2N/A if (compare_time(s, sns) == NET_TIME_LESSER) {
2N/A s->net_time_yr = sns->net_time_yr;
2N/A s->net_time_mon = sns->net_time_mon;
2N/A s->net_time_day = sns->net_time_day;
2N/A s->net_time_hr = sns->net_time_hr;
2N/A s->net_time_min = sns->net_time_min;
2N/A s->net_time_sec = sns->net_time_sec;
2N/A }
2N/A }
2N/A if (e != NULL) {
2N/A if (compare_time(e, ens) == NET_TIME_GREATER) {
2N/A e->net_time_yr = ens->net_time_yr;
2N/A e->net_time_mon = ens->net_time_mon;
2N/A e->net_time_day = ens->net_time_day;
2N/A e->net_time_hr = ens->net_time_hr;
2N/A e->net_time_min = ens->net_time_min;
2N/A e->net_time_sec = ens->net_time_sec;
2N/A }
2N/A }
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/A/*
2N/A * Given a start and end time (strings), convert them into net_time_t
2N/A * and also check for the range given the head and tail of the list.
2N/A * If stime is lower then head or etime is greated than tail, adjust.
2N/A */
2N/Astatic dladm_status_t
2N/Aget_time_range(net_time_entry_t *head, net_time_entry_t *tail,
2N/A net_time_t *st, net_time_t *et, char *stime, char *etime)
2N/A{
2N/A bzero(st, sizeof (net_time_t));
2N/A bzero(et, sizeof (net_time_t));
2N/A
2N/A if (stime == NULL && etime == NULL)
2N/A return (0);
2N/A
2N/A if (stime != NULL)
2N/A dissect_time(stime, st);
2N/A if (etime != NULL)
2N/A dissect_time(etime, et);
2N/A
2N/A if (stime != NULL || etime != NULL) {
2N/A return (chk_time_bound(stime == NULL ? NULL : st,
2N/A etime == NULL ? NULL : et,
2N/A &head->my_time_stat->net_stat_time,
2N/A &tail->my_time_stat->net_stat_time));
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Walk the list from a given starting point and return when we find
2N/A * an entry that is greater or equal to st. lasttime will point to the
2N/A * previous time entry.
2N/A */
2N/Astatic void
2N/Aget_starting_point(net_time_entry_t *head, net_time_entry_t **start,
2N/A net_time_t *st, char *stime, uint64_t *lasttime)
2N/A{
2N/A net_time_entry_t *next = head;
2N/A
2N/A if (head == NULL) {
2N/A *start = NULL;
2N/A return;
2N/A }
2N/A if (stime == NULL) {
2N/A *start = head;
2N/A *lasttime = head->my_time_stat->net_stat_ctime;
2N/A return;
2N/A }
2N/A *start = NULL;
2N/A while (next != NULL) {
2N/A if (compare_time(st,
2N/A &next->my_time_stat->net_stat_time) != NET_TIME_LESSER) {
2N/A *lasttime = next->my_time_stat->net_stat_ctime;
2N/A next = next->net_time_entry_next;
2N/A continue;
2N/A }
2N/A *start = next;
2N/A break;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Point entry (pe) functions
2N/A */
2N/A/* Clear all the counters. Done after the contents are written to the file */
2N/Astatic void
2N/Aclear_pe(net_plot_entry_t *pe, int entries, int *pentries)
2N/A{
2N/A int count;
2N/A
2N/A for (count = 0; count < entries; count++) {
2N/A pe[count].net_pe_totbytes = 0;
2N/A pe[count].net_pe_totibytes = 0;
2N/A pe[count].net_pe_totobytes = 0;
2N/A pe[count].net_pe_tottime = 0;
2N/A }
2N/A *pentries = 0;
2N/A}
2N/A
2N/A/* Update an entry in the point entry table */
2N/Astatic void
2N/Aupdate_pe(net_plot_entry_t *pe, net_stat_t *nns, int nentries,
2N/A int *pentries, uint64_t lasttime)
2N/A{
2N/A int count;
2N/A
2N/A for (count = 0; count < nentries; count++) {
2N/A if (strcmp(pe[count].net_pe_name, nns->net_stat_name) == 0)
2N/A break;
2N/A }
2N/A if (count == nentries)
2N/A return;
2N/A
2N/A if (pe[count].net_pe_totbytes == 0)
2N/A pe[count].net_pe_lasttime = lasttime;
2N/A
2N/A pe[count].net_pe_totbytes += nns->net_stat_ibytes +
2N/A nns->net_stat_obytes;
2N/A pe[count].net_pe_tottime += nns->net_stat_tdiff;
2N/A pe[count].net_pe_totibytes += nns->net_stat_ibytes;
2N/A pe[count].net_pe_totobytes += nns->net_stat_obytes;
2N/A (*pentries)++;
2N/A}
2N/A
2N/A/* Flush the contents of the point entry table to the file. */
2N/Astatic void
2N/Aadd_pe_to_file(int (*fn)(dladm_usage_t *, void *), net_plot_entry_t *pe,
2N/A net_stat_t *ns, int entries, void *arg)
2N/A{
2N/A int count;
2N/A dladm_usage_t usage;
2N/A uint64_t tottime;
2N/A
2N/A bcopy(&ns->net_stat_ctime, &usage.du_etime, sizeof (usage.du_etime));
2N/A for (count = 0; count < entries; count++) {
2N/A bcopy(pe[count].net_pe_name, &usage.du_name,
2N/A sizeof (usage.du_name));
2N/A bcopy(&pe[count].net_pe_lasttime, &usage.du_stime,
2N/A sizeof (usage.du_stime));
2N/A usage.du_rbytes = pe[count].net_pe_totibytes;
2N/A usage.du_obytes = pe[count].net_pe_totobytes;
2N/A tottime = pe[count].net_pe_tottime;
2N/A usage.du_bandwidth = (tottime > 0) ?
2N/A ((pe[count].net_pe_totbytes * 8) / tottime) : 0;
2N/A usage.du_last = (count == entries-1);
2N/A fn(&usage, arg);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Net entry functions
2N/A */
2N/Astatic net_entry_t *
2N/Aget_ne_from_table(net_table_t *net_table, char *name)
2N/A{
2N/A int count;
2N/A net_desc_t *nd;
2N/A net_entry_t *ne = net_table->net_table_head;
2N/A
2N/A for (count = 0; count < net_table->net_entries; count++) {
2N/A nd = ne->net_entry_desc;
2N/A if (strcmp(name, nd->net_desc_name) == 0)
2N/A return (ne);
2N/A ne = ne->net_entry_next;
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/A/* Get the entry for the descriptor, if it exists */
2N/Astatic net_desc_t *
2N/Aget_ndesc(net_table_t *net_table, net_desc_t *nd)
2N/A{
2N/A int count;
2N/A net_desc_t *nd1;
2N/A net_entry_t *ne = net_table->net_table_head;
2N/A
2N/A for (count = 0; count < net_table->net_entries; count++) {
2N/A nd1 = ne->net_entry_desc;
2N/A if (strcmp(nd1->net_desc_name, nd->net_desc_name) == 0 &&
2N/A strcmp(nd1->net_desc_devname, nd->net_desc_devname) == 0 &&
2N/A bcmp(nd1->net_desc_ehost, nd->net_desc_ehost,
2N/A ETHERADDRL) == 0 &&
2N/A bcmp(nd1->net_desc_edest, nd->net_desc_edest,
2N/A ETHERADDRL) == 0 &&
2N/A nd1->net_desc_vlan_tpid == nd->net_desc_vlan_tpid &&
2N/A nd1->net_desc_vlan_tci == nd->net_desc_vlan_tci &&
2N/A nd1->net_desc_sap == nd->net_desc_sap &&
2N/A nd1->net_desc_cpuid == nd->net_desc_cpuid &&
2N/A nd1->net_desc_priority == nd->net_desc_priority &&
2N/A nd1->net_desc_bw_limit == nd->net_desc_bw_limit &&
2N/A nd1->net_desc_sport == nd->net_desc_sport &&
2N/A nd1->net_desc_dport == nd->net_desc_dport &&
2N/A nd1->net_desc_protocol == nd->net_desc_protocol &&
2N/A nd1->net_desc_dsfield == nd->net_desc_dsfield &&
2N/A IN6_ARE_ADDR_EQUAL(&nd1->net_desc_saddr,
2N/A &nd->net_desc_saddr) &&
2N/A IN6_ARE_ADDR_EQUAL(&nd1->net_desc_daddr,
2N/A &nd->net_desc_daddr)) {
2N/A return (nd1);
2N/A }
2N/A ne = ne->net_entry_next;
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Update the stat entries. The stats in the file are cumulative, so in order
2N/A * to have increments, we maintain a reference stat entry, which contains
2N/A * the stats when the record was first written and a total stat entry, which
2N/A * maintains the running count. When we want to add a stat entry, if it
2N/A * the reference stat entry, we don't come here. For subsequent entries,
2N/A * we get the increment by subtracting the current value from the reference
2N/A * stat and the total stat.
2N/A */
2N/Astatic void
2N/Aupdate_stats(net_stat_t *ns1, net_entry_t *ne, net_stat_t *ref)
2N/A{
2N/A
2N/A /* get the increment */
2N/A ns1->net_stat_ibytes -= (ref->net_stat_ibytes + ref->net_stat_tibytes);
2N/A ns1->net_stat_obytes -= (ref->net_stat_obytes + ref->net_stat_tobytes);
2N/A ns1->net_stat_ipackets -= (ref->net_stat_ipackets +
2N/A ref->net_stat_tipackets);
2N/A ns1->net_stat_opackets -= (ref->net_stat_opackets +
2N/A ref->net_stat_topackets);
2N/A ns1->net_stat_ierrors -= (ref->net_stat_ierrors +
2N/A ref->net_stat_tierrors);
2N/A ns1->net_stat_oerrors -= (ref->net_stat_oerrors +
2N/A ref->net_stat_toerrors);
2N/A
2N/A /* update total bytes */
2N/A ref->net_stat_tibytes += ns1->net_stat_ibytes;
2N/A ref->net_stat_tobytes += ns1->net_stat_obytes;
2N/A ref->net_stat_tipackets += ns1->net_stat_ipackets;
2N/A ref->net_stat_topackets += ns1->net_stat_opackets;
2N/A ref->net_stat_tierrors += ns1->net_stat_ierrors;
2N/A ref->net_stat_toerrors += ns1->net_stat_oerrors;
2N/A
2N/A ne->net_entry_tstats->net_stat_ibytes += ns1->net_stat_ibytes;
2N/A ne->net_entry_tstats->net_stat_obytes += ns1->net_stat_obytes;
2N/A ne->net_entry_tstats->net_stat_ipackets += ns1->net_stat_ipackets;
2N/A ne->net_entry_tstats->net_stat_opackets += ns1->net_stat_opackets;
2N/A ne->net_entry_tstats->net_stat_ierrors += ns1->net_stat_ierrors;
2N/A ne->net_entry_tstats->net_stat_oerrors += ns1->net_stat_oerrors;
2N/A}
2N/A
2N/A/* Add the stat entry into the table */
2N/Astatic dladm_status_t
2N/Aadd_stat_to_tbl(net_table_t *net_table, net_stat_t *ns)
2N/A{
2N/A net_entry_t *ne;
2N/A
2N/A ne = get_ne_from_table(net_table, ns->net_stat_name);
2N/A if (ne == NULL)
2N/A return (DLADM_STATUS_NOMEM);
2N/A
2N/A /* Ptr to flow desc */
2N/A ns->net_stat_desc = ne->net_entry_desc;
2N/A if (ns->net_stat_desc->net_desc_newrec) {
2N/A ns->net_stat_desc->net_desc_newrec = B_FALSE;
2N/A ns->net_stat_isref = B_TRUE;
2N/A ne->net_entry_sref = ns;
2N/A } else if (ns->net_stat_ibytes < ne->net_entry_sref->net_stat_tibytes ||
2N/A (ns->net_stat_obytes < ne->net_entry_sref->net_stat_tobytes)) {
2N/A ns->net_stat_isref = B_TRUE;
2N/A ne->net_entry_sref = ns;
2N/A } else {
2N/A ns->net_stat_isref = B_FALSE;
2N/A update_stats(ns, ne, ne->net_entry_sref);
2N/A }
2N/A if (ne->net_entry_shead == NULL) {
2N/A ne->net_entry_shead = ns;
2N/A ne->net_entry_stail = ns;
2N/A } else {
2N/A if (!ns->net_stat_isref) {
2N/A ne->net_entry_ttime += (ns->net_stat_ctime -
2N/A ne->net_entry_stail->net_stat_ctime);
2N/A ns->net_stat_tdiff = ns->net_stat_ctime -
2N/A ne->net_entry_stail->net_stat_ctime;
2N/A }
2N/A ne->net_entry_stail->net_stat_next = ns;
2N/A ne->net_entry_stail = ns;
2N/A }
2N/A
2N/A ne->net_entry_scount++;
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/A/* Add a flow/link descriptor record to the table */
2N/Astatic dladm_status_t
2N/Aadd_desc(net_table_t *net_table, ea_file_t *ef, int nobjs)
2N/A{
2N/A net_desc_t *nd;
2N/A net_desc_t *dnd;
2N/A int count;
2N/A ea_object_t scratch;
2N/A
2N/A if ((nd = calloc(1, sizeof (net_desc_t))) == NULL)
2N/A return (DLADM_STATUS_NOMEM);
2N/A nd->net_desc_newrec = B_TRUE;
2N/A
2N/A for (count = 0; count < nobjs; count++) {
2N/A if (ea_get_object(ef, &scratch) == -1) {
2N/A free(nd);
2N/A return (DLADM_STATUS_NOMEM);
2N/A }
2N/A add_desc_item(&scratch, nd);
2N/A }
2N/A if ((dnd = get_ndesc(net_table, nd)) != NULL) {
2N/A dnd->net_desc_newrec = B_TRUE;
2N/A free(nd);
2N/A return (DLADM_STATUS_OK);
2N/A }
2N/A if (add_desc_to_tbl(net_table, nd) != 0) {
2N/A free(nd);
2N/A return (DLADM_STATUS_NOMEM);
2N/A }
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/A/* Make an entry into the time sorted list */
2N/Astatic void
2N/Aaddto_time_list(net_table_t *net_table, net_time_entry_t *nt,
2N/A net_time_entry_t *ntc)
2N/A{
2N/A net_stat_t *ns = nt->my_time_stat;
2N/A net_stat_t *ns1;
2N/A net_time_entry_t *end;
2N/A net_time_t *t1;
2N/A int count;
2N/A
2N/A t1 = &ns->net_stat_time;
2N/A
2N/A net_table->net_time_entries++;
2N/A
2N/A if (net_table->net_time_head == NULL) {
2N/A net_table->net_time_head = nt;
2N/A net_table->net_time_tail = nt;
2N/A } else {
2N/A net_table->net_time_tail->net_time_entry_next = nt;
2N/A nt->net_time_entry_prev = net_table->net_time_tail;
2N/A net_table->net_time_tail = nt;
2N/A }
2N/A
2N/A if (net_table->net_ctime_head == NULL) {
2N/A net_table->net_ctime_head = ntc;
2N/A net_table->net_ctime_tail = ntc;
2N/A } else {
2N/A end = net_table->net_ctime_tail;
2N/A count = 0;
2N/A while (count < net_table->net_time_entries - 1) {
2N/A ns1 = end->my_time_stat;
2N/A /* Just add it to the tail */
2N/A if (compare_date(t1, &ns1->net_stat_time) ==
2N/A NET_DATE_GREATER) {
2N/A break;
2N/A }
2N/A if (strcmp(ns1->net_stat_name, ns->net_stat_name) ==
2N/A 0) {
2N/A ntc->net_time_entry_next =
2N/A end->net_time_entry_next;
2N/A if (end->net_time_entry_next != NULL) {
2N/A end->net_time_entry_next->
2N/A net_time_entry_prev = ntc;
2N/A } else {
2N/A net_table->net_ctime_tail = ntc;
2N/A }
2N/A end->net_time_entry_next = ntc;
2N/A ntc->net_time_entry_prev = end;
2N/A return;
2N/A }
2N/A count++;
2N/A end = end->net_time_entry_prev;
2N/A }
2N/A net_table->net_ctime_tail->net_time_entry_next = ntc;
2N/A ntc->net_time_entry_prev = net_table->net_ctime_tail;
2N/A net_table->net_ctime_tail = ntc;
2N/A }
2N/A}
2N/A
2N/A/* Add stat entry into the lists */
2N/Astatic dladm_status_t
2N/Aadd_stats(net_table_t *net_table, ea_file_t *ef, int nobjs)
2N/A{
2N/A net_stat_t *ns;
2N/A int count;
2N/A ea_object_t scratch;
2N/A net_time_entry_t *nt;
2N/A net_time_entry_t *ntc;
2N/A
2N/A if ((ns = calloc(1, sizeof (net_stat_t))) == NULL)
2N/A return (DLADM_STATUS_NOMEM);
2N/A
2N/A if ((nt = calloc(1, sizeof (net_time_entry_t))) == NULL) {
2N/A free(ns);
2N/A return (DLADM_STATUS_NOMEM);
2N/A }
2N/A if ((ntc = calloc(1, sizeof (net_time_entry_t))) == NULL) {
2N/A free(ns);
2N/A free(nt);
2N/A return (DLADM_STATUS_NOMEM);
2N/A }
2N/A
2N/A nt->my_time_stat = ns;
2N/A ntc->my_time_stat = ns;
2N/A
2N/A for (count = 0; count < nobjs; count++) {
2N/A if (ea_get_object(ef, &scratch) == -1) {
2N/A free(ns);
2N/A free(nt);
2N/A free(ntc);
2N/A return (DLADM_STATUS_NOMEM);
2N/A }
2N/A add_stat_item(&scratch, ns);
2N/A }
2N/A if (add_stat_to_tbl(net_table, ns) != 0) {
2N/A free(ns);
2N/A free(nt);
2N/A free(ntc);
2N/A return (DLADM_STATUS_NOMEM);
2N/A }
2N/A addto_time_list(net_table, nt, ntc);
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/A/* Free the entire table */
2N/Astatic void
2N/Afree_logtable(net_table_t *net_table)
2N/A{
2N/A net_entry_t *head;
2N/A net_entry_t *next;
2N/A net_stat_t *ns;
2N/A net_stat_t *ns1;
2N/A net_time_entry_t *thead;
2N/A net_time_entry_t *tnext;
2N/A
2N/A thead = net_table->net_time_head;
2N/A while (thead != NULL) {
2N/A thead->my_time_stat = NULL;
2N/A tnext = thead->net_time_entry_next;
2N/A thead->net_time_entry_next = NULL;
2N/A thead->net_time_entry_prev = NULL;
2N/A free(thead);
2N/A thead = tnext;
2N/A }
2N/A net_table->net_time_head = NULL;
2N/A net_table->net_time_tail = NULL;
2N/A
2N/A thead = net_table->net_ctime_head;
2N/A while (thead != NULL) {
2N/A thead->my_time_stat = NULL;
2N/A tnext = thead->net_time_entry_next;
2N/A thead->net_time_entry_next = NULL;
2N/A thead->net_time_entry_prev = NULL;
2N/A free(thead);
2N/A thead = tnext;
2N/A }
2N/A net_table->net_ctime_head = NULL;
2N/A net_table->net_ctime_tail = NULL;
2N/A
2N/A net_table->net_time_entries = 0;
2N/A
2N/A head = net_table->net_table_head;
2N/A while (head != NULL) {
2N/A next = head->net_entry_next;
2N/A head->net_entry_next = NULL;
2N/A ns = head->net_entry_shead;
2N/A while (ns != NULL) {
2N/A ns1 = ns->net_stat_next;
2N/A free(ns);
2N/A ns = ns1;
2N/A }
2N/A head->net_entry_scount = 0;
2N/A head->net_entry_sref = NULL;
2N/A free(head->net_entry_desc);
2N/A free(head->net_entry_tstats);
2N/A free(head);
2N/A head = next;
2N/A }
2N/A net_table->net_table_head = NULL;
2N/A net_table->net_table_tail = NULL;
2N/A net_table->net_time_entries = 0;
2N/A free(net_table);
2N/A}
2N/A
2N/A/* Parse the exacct file, and return the parsed table. */
2N/Astatic void *
2N/Aparse_logfile(char *file, int logtype, dladm_status_t *status)
2N/A{
2N/A ea_file_t ef;
2N/A ea_object_t scratch;
2N/A net_table_t *net_table;
2N/A
2N/A *status = DLADM_STATUS_OK;
2N/A if ((net_table = calloc(1, sizeof (net_table_t))) == NULL) {
2N/A *status = DLADM_STATUS_NOMEM;
2N/A return (NULL);
2N/A }
2N/A if (ea_open(&ef, file, NULL, 0, O_RDONLY, 0) == -1) {
2N/A *status = DLADM_STATUS_BADARG;
2N/A free(net_table);
2N/A return (NULL);
2N/A }
2N/A bzero(&scratch, sizeof (ea_object_t));
2N/A while (ea_get_object(&ef, &scratch) != -1) {
2N/A if (scratch.eo_type != EO_GROUP) {
2N/A (void) ea_free_item(&scratch, EUP_ALLOC);
2N/A (void) bzero(&scratch, sizeof (ea_object_t));
2N/A continue;
2N/A }
2N/A /* Read Link Desc/Stat records */
2N/A if (logtype == DLADM_LOGTYPE_FLOW) {
2N/A /* Flow Descriptor */
2N/A if ((scratch.eo_catalog &
2N/A EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC) {
2N/A (void) add_desc(net_table, &ef,
2N/A scratch.eo_group.eg_nobjs - 1);
2N/A /* Flow Stats */
2N/A } else if ((scratch.eo_catalog &
2N/A EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS) {
2N/A (void) add_stats(net_table, &ef,
2N/A scratch.eo_group.eg_nobjs - 1);
2N/A }
2N/A } else if (logtype == DLADM_LOGTYPE_LINK) {
2N/A /* Link Descriptor */
2N/A if ((scratch.eo_catalog &
2N/A EXD_DATA_MASK) == EXD_GROUP_NET_LINK_DESC) {
2N/A (void) add_desc(net_table, &ef,
2N/A scratch.eo_group.eg_nobjs - 1);
2N/A /* Link Stats */
2N/A } else if ((scratch.eo_catalog &
2N/A EXD_DATA_MASK) == EXD_GROUP_NET_LINK_STATS) {
2N/A (void) add_stats(net_table, &ef,
2N/A scratch.eo_group.eg_nobjs - 1);
2N/A }
2N/A } else {
2N/A if (((scratch.eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_GROUP_NET_LINK_DESC) || ((scratch.eo_catalog &
2N/A EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC)) {
2N/A (void) add_desc(net_table, &ef,
2N/A scratch.eo_group.eg_nobjs - 1);
2N/A } else if (((scratch.eo_catalog & EXD_DATA_MASK) ==
2N/A EXD_GROUP_NET_LINK_STATS) || ((scratch.eo_catalog &
2N/A EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS)) {
2N/A (void) add_stats(net_table, &ef,
2N/A scratch.eo_group.eg_nobjs - 1);
2N/A }
2N/A }
2N/A (void) ea_free_item(&scratch, EUP_ALLOC);
2N/A (void) bzero(&scratch, sizeof (ea_object_t));
2N/A }
2N/A
2N/A (void) ea_close(&ef);
2N/A return ((void *)net_table);
2N/A}
2N/A
2N/A/*
2N/A * Walk the ctime list. This is used when looking for usage records
2N/A * based on a "resource" name.
2N/A */
2N/Adladm_status_t
2N/Adladm_walk_usage_res(int (*fn)(dladm_usage_t *, void *), int logtype,
2N/A char *logfile, char *resource, char *stime, char *etime, void *arg)
2N/A{
2N/A net_table_t *net_table;
2N/A net_time_t st, et;
2N/A net_time_entry_t *start;
2N/A net_stat_t *ns = NULL;
2N/A net_stat_t *nns;
2N/A uint64_t tot_time = 0;
2N/A uint64_t last_time;
2N/A uint64_t tot_bytes = 0;
2N/A uint64_t tot_ibytes = 0;
2N/A uint64_t tot_obytes = 0;
2N/A boolean_t gotstart = B_FALSE;
2N/A dladm_status_t status;
2N/A dladm_usage_t usage;
2N/A int step = 1;
2N/A
2N/A /* Parse the log file */
2N/A net_table = parse_logfile(logfile, logtype, &status);
2N/A if (net_table == NULL)
2N/A return (status);
2N/A
2N/A if (net_table->net_entries == 0)
2N/A return (DLADM_STATUS_OK);
2N/A start = net_table->net_ctime_head;
2N/A
2N/A /* Time range */
2N/A status = get_time_range(net_table->net_ctime_head,
2N/A net_table->net_ctime_tail, &st, &et, stime, etime);
2N/A if (status != DLADM_STATUS_OK)
2N/A return (status);
2N/A
2N/A while (start != NULL) {
2N/A nns = start->my_time_stat;
2N/A
2N/A /* Get to the resource we are interested in */
2N/A if (strcmp(resource, nns->net_stat_name) != 0) {
2N/A start = start->net_time_entry_next;
2N/A continue;
2N/A }
2N/A
2N/A /* Find the first record */
2N/A if (!gotstart) {
2N/A get_starting_point(start, &start, &st, stime,
2N/A &last_time);
2N/A if (start == NULL)
2N/A break;
2N/A nns = start->my_time_stat;
2N/A gotstart = B_TRUE;
2N/A }
2N/A
2N/A /* Write one entry and return if we are out of the range */
2N/A if (etime != NULL && compare_time(&nns->net_stat_time, &et)
2N/A == NET_TIME_GREATER) {
2N/A if (tot_bytes != 0) {
2N/A bcopy(ns->net_stat_name, &usage.du_name,
2N/A sizeof (usage.du_name));
2N/A bcopy(&last_time, &usage.du_stime,
2N/A sizeof (usage.du_stime));
2N/A bcopy(&ns->net_stat_ctime, &usage.du_etime,
2N/A sizeof (usage.du_etime));
2N/A usage.du_rbytes = tot_ibytes;
2N/A usage.du_obytes = tot_obytes;
2N/A usage.du_bandwidth = tot_bytes*8/tot_time;
2N/A usage.du_last = B_TRUE;
2N/A fn(&usage, arg);
2N/A }
2N/A return (DLADM_STATUS_OK);
2N/A }
2N/A
2N/A /*
2N/A * If this is a reference entry, just print what we have
2N/A * and proceed.
2N/A */
2N/A if (nns->net_stat_isref) {
2N/A if (tot_bytes != 0) {
2N/A bcopy(&nns->net_stat_name, &usage.du_name,
2N/A sizeof (usage.du_name));
2N/A bcopy(&nns->net_stat_ctime, &usage.du_stime,
2N/A sizeof (usage.du_stime));
2N/A usage.du_rbytes = tot_ibytes;
2N/A usage.du_obytes = tot_obytes;
2N/A usage.du_bandwidth = tot_bytes*8/tot_time;
2N/A usage.du_last = B_TRUE;
2N/A fn(&usage, arg);
2N/A NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
2N/A tot_obytes, step);
2N/A }
2N/A last_time = nns->net_stat_ctime;
2N/A start = start->net_time_entry_next;
2N/A continue;
2N/A }
2N/A
2N/A ns = nns;
2N/A if (--step == 0) {
2N/A tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
2N/A tot_ibytes += ns->net_stat_ibytes;
2N/A tot_obytes += ns->net_stat_obytes;
2N/A tot_time += ns->net_stat_tdiff;
2N/A bcopy(&ns->net_stat_name, &usage.du_name,
2N/A sizeof (usage.du_name));
2N/A bcopy(&last_time, &usage.du_stime,
2N/A sizeof (usage.du_stime));
2N/A bcopy(&ns->net_stat_ctime, &usage.du_etime,
2N/A sizeof (usage.du_etime));
2N/A usage.du_rbytes = tot_ibytes;
2N/A usage.du_obytes = tot_obytes;
2N/A usage.du_bandwidth = tot_bytes*8/tot_time;
2N/A usage.du_last = B_TRUE;
2N/A fn(&usage, arg);
2N/A
2N/A NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
2N/A tot_obytes, step);
2N/A last_time = ns->net_stat_ctime;
2N/A } else {
2N/A tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
2N/A tot_ibytes += ns->net_stat_ibytes;
2N/A tot_obytes += ns->net_stat_obytes;
2N/A tot_time += ns->net_stat_tdiff;
2N/A }
2N/A start = start->net_time_entry_next;
2N/A }
2N/A
2N/A if (tot_bytes != 0) {
2N/A bcopy(&ns->net_stat_name, &usage.du_name,
2N/A sizeof (usage.du_name));
2N/A bcopy(&last_time, &usage.du_stime,
2N/A sizeof (usage.du_stime));
2N/A bcopy(&ns->net_stat_ctime, &usage.du_etime,
2N/A sizeof (usage.du_etime));
2N/A usage.du_rbytes = tot_ibytes;
2N/A usage.du_obytes = tot_obytes;
2N/A usage.du_bandwidth = tot_bytes*8/tot_time;
2N/A usage.du_last = B_TRUE;
2N/A fn(&usage, arg);
2N/A }
2N/A
2N/A free_logtable(net_table);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Walk the time sorted list if a resource is not specified.
2N/A */
2N/Adladm_status_t
2N/Adladm_walk_usage_time(int (*fn)(dladm_usage_t *, void *), int logtype,
2N/A char *logfile, char *stime, char *etime, void *arg)
2N/A{
2N/A net_table_t *net_table;
2N/A net_time_entry_t *start;
2N/A net_stat_t *ns = NULL, *nns;
2N/A net_time_t st, et, *t1;
2N/A net_desc_t *nd;
2N/A net_entry_t *ne;
2N/A net_plot_entry_t *pe;
2N/A int count;
2N/A int step = 1;
2N/A int nentries = 0, pentries = 0;
2N/A uint64_t last_time;
2N/A dladm_status_t status;
2N/A
2N/A /* Parse the log file */
2N/A net_table = parse_logfile(logfile, logtype, &status);
2N/A if (net_table == NULL)
2N/A return (status);
2N/A
2N/A if (net_table->net_entries == 0)
2N/A return (DLADM_STATUS_OK);
2N/A start = net_table->net_time_head;
2N/A
2N/A /* Find the first and last records and starting point */
2N/A status = get_time_range(net_table->net_time_head,
2N/A net_table->net_time_tail, &st, &et, stime, etime);
2N/A if (status != DLADM_STATUS_OK)
2N/A return (status);
2N/A get_starting_point(start, &start, &st, stime, &last_time);
2N/A /*
2N/A * Could assert to be non-null, since get_time_range()
2N/A * would have adjusted.
2N/A */
2N/A if (start == NULL)
2N/A return (DLADM_STATUS_BADTIMEVAL);
2N/A
2N/A /*
2N/A * Collect entries for all resources in a time slot before
2N/A * writing to the file.
2N/A */
2N/A nentries = net_table->net_entries;
2N/A
2N/A pe = malloc(sizeof (net_plot_entry_t) * net_table->net_entries + 1);
2N/A if (pe == NULL)
2N/A return (DLADM_STATUS_NOMEM);
2N/A
2N/A ne = net_table->net_table_head;
2N/A for (count = 0; count < nentries; count++) {
2N/A nd = ne->net_entry_desc;
2N/A pe[count].net_pe_name = nd->net_desc_name;
2N/A ne = ne->net_entry_next;
2N/A }
2N/A
2N/A clear_pe(pe, nentries, &pentries);
2N/A
2N/A /* Write header to file */
2N/A /* add_pe_to_file(fn, pe, ns, nentries, arg); */
2N/A
2N/A t1 = &start->my_time_stat->net_stat_time;
2N/A
2N/A while (start != NULL) {
2N/A
2N/A nns = start->my_time_stat;
2N/A /*
2N/A * We have crossed the time boundary, check if we need to
2N/A * print out now.
2N/A */
2N/A if (compare_time(&nns->net_stat_time, t1) ==
2N/A NET_TIME_GREATER) {
2N/A /* return if we are out of the range */
2N/A if (etime != NULL &&
2N/A compare_time(&nns->net_stat_time, &et) ==
2N/A NET_TIME_GREATER) {
2N/A if (pentries > 0) {
2N/A add_pe_to_file(fn, pe, ns, nentries,
2N/A arg);
2N/A clear_pe(pe, nentries, &pentries);
2N/A }
2N/A free(pe);
2N/A return (DLADM_STATUS_OK);
2N/A }
2N/A /* update the stats from the ns. */
2N/A t1 = &nns->net_stat_time;
2N/A last_time = ns->net_stat_ctime;
2N/A if (--step == 0) {
2N/A if (pentries > 0) {
2N/A add_pe_to_file(fn, pe, ns, nentries,
2N/A arg);
2N/A clear_pe(pe, nentries, &pentries);
2N/A }
2N/A step = 1;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * if this is a reference entry, just print what we have
2N/A * for this resource and proceed. We will end up writing
2N/A * the stats for all the entries when we hit a ref element,
2N/A * which means 'steps' for some might not be accurate, but
2N/A * that is fine, the alternative is to write only the
2N/A * resource for which we hit a reference entry.
2N/A */
2N/A if (nns->net_stat_isref) {
2N/A if (pentries > 0) {
2N/A add_pe_to_file(fn, pe, ns, nentries, arg);
2N/A clear_pe(pe, nentries, &pentries);
2N/A }
2N/A step = 1;
2N/A } else {
2N/A update_pe(pe, nns, nentries, &pentries, last_time);
2N/A }
2N/A ns = nns;
2N/A start = start->net_time_entry_next;
2N/A }
2N/A
2N/A if (pentries > 0)
2N/A add_pe_to_file(fn, pe, ns, nentries, arg);
2N/A
2N/A free(pe);
2N/A free_logtable(net_table);
2N/A
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_usage_summary(int (*fn)(dladm_usage_t *, void *), int logtype,
2N/A char *logfile, void *arg)
2N/A{
2N/A net_table_t *net_table;
2N/A net_entry_t *ne;
2N/A net_desc_t *nd;
2N/A net_stat_t *ns;
2N/A int count;
2N/A dladm_usage_t usage;
2N/A dladm_status_t status;
2N/A
2N/A /* Parse the log file */
2N/A net_table = parse_logfile(logfile, logtype, &status);
2N/A if (net_table == NULL)
2N/A return (status);
2N/A
2N/A if (net_table->net_entries == 0)
2N/A return (DLADM_STATUS_OK);
2N/A
2N/A ne = net_table->net_table_head;
2N/A for (count = 0; count < net_table->net_entries; count++) {
2N/A ns = ne->net_entry_tstats;
2N/A nd = ne->net_entry_desc;
2N/A
2N/A if (ns->net_stat_ibytes + ns->net_stat_obytes == 0) {
2N/A ne = ne->net_entry_next;
2N/A continue;
2N/A }
2N/A bcopy(&nd->net_desc_name, &usage.du_name,
2N/A sizeof (usage.du_name));
2N/A usage.du_duration = ne->net_entry_ttime;
2N/A usage.du_ipackets = ns->net_stat_ipackets;
2N/A usage.du_rbytes = ns->net_stat_ibytes;
2N/A usage.du_opackets = ns->net_stat_opackets;
2N/A usage.du_obytes = ns->net_stat_obytes;
2N/A usage.du_bandwidth =
2N/A (ns->net_stat_ibytes + ns->net_stat_obytes) * 8 /
2N/A usage.du_duration;
2N/A usage.du_last = (count == net_table->net_entries-1);
2N/A fn(&usage, arg);
2N/A
2N/A ne = ne->net_entry_next;
2N/A }
2N/A
2N/A free_logtable(net_table);
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/A/*
2N/A * Walk the ctime list and display the dates of the records.
2N/A */
2N/Adladm_status_t
2N/Adladm_usage_dates(int (*fn)(dladm_usage_t *, void *), int logtype,
2N/A char *logfile, char *resource, void *arg)
2N/A{
2N/A net_table_t *net_table;
2N/A net_time_entry_t *start;
2N/A net_stat_t *nns;
2N/A net_time_t st;
2N/A net_time_t *lasttime = NULL;
2N/A uint64_t last_time;
2N/A boolean_t gotstart = B_FALSE;
2N/A dladm_status_t status;
2N/A dladm_usage_t usage;
2N/A
2N/A /* Parse the log file */
2N/A net_table = parse_logfile(logfile, logtype, &status);
2N/A if (net_table == NULL)
2N/A return (status);
2N/A
2N/A if (net_table->net_entries == 0)
2N/A return (DLADM_STATUS_OK);
2N/A
2N/A start = net_table->net_ctime_head;
2N/A
2N/A while (start != NULL) {
2N/A nns = start->my_time_stat;
2N/A
2N/A /* get to the resource we are interested in */
2N/A if (resource != NULL) {
2N/A if (strcmp(resource, nns->net_stat_name) != 0) {
2N/A start = start->net_time_entry_next;
2N/A continue;
2N/A }
2N/A }
2N/A
2N/A /* get the starting point in the logfile */
2N/A if (!gotstart) {
2N/A get_starting_point(start, &start, &st, NULL,
2N/A &last_time);
2N/A if (start == NULL)
2N/A break;
2N/A nns = start->my_time_stat;
2N/A gotstart = B_TRUE;
2N/A }
2N/A
2N/A if (lasttime == NULL ||
2N/A compare_date(&nns->net_stat_time, lasttime) ==
2N/A NET_DATE_GREATER) {
2N/A bzero(&usage, sizeof (dladm_usage_t));
2N/A (void) strlcpy(usage.du_name, nns->net_stat_name,
2N/A sizeof (usage.du_name));
2N/A bcopy(&nns->net_stat_ctime, &usage.du_stime,
2N/A sizeof (usage.du_stime));
2N/A fn(&usage, arg);
2N/A lasttime = &nns->net_stat_time;
2N/A }
2N/A
2N/A start = start->net_time_entry_next;
2N/A continue;
2N/A }
2N/A
2N/A free_logtable(net_table);
2N/A return (status);
2N/A}