1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <fcntl.h> 28 #include <stdlib.h> 29 #include <strings.h> 30 #include <exacct.h> 31 #include <net/if.h> 32 #include <sys/ethernet.h> 33 #include <libdladm.h> 34 35 #define TIMEBUFLEN 20 36 #define GBIT 1000000000 37 #define MBIT 1000000 38 #define KBIT 1000 39 40 #define NET_RESET_TOT(tbytes, ttime, tibytes, tobytes, step) { \ 41 (step) = 1; \ 42 (tbytes) = 0; \ 43 (ttime) = 0; \ 44 (tibytes) = 0; \ 45 (tobytes) = 0; \ 46 } 47 48 /* Flow/Link Descriptor */ 49 typedef struct net_desc_s { 50 char net_desc_name[LIFNAMSIZ]; 51 char net_desc_devname[LIFNAMSIZ]; 52 uchar_t net_desc_ehost[ETHERADDRL]; 53 uchar_t net_desc_edest[ETHERADDRL]; 54 ushort_t net_desc_vlan_tpid; 55 ushort_t net_desc_vlan_tci; 56 ushort_t net_desc_sap; 57 ushort_t net_desc_cpuid; 58 ushort_t net_desc_priority; 59 uint64_t net_desc_bw_limit; 60 in6_addr_t net_desc_saddr; 61 in6_addr_t net_desc_daddr; 62 boolean_t net_desc_isv4; 63 in_port_t net_desc_sport; 64 in_port_t net_desc_dport; 65 uint8_t net_desc_protocol; 66 uint8_t net_desc_dsfield; 67 boolean_t net_desc_newrec; 68 } net_desc_t; 69 70 /* Time structure: Year, Month, Day, Hour, Min, Sec */ 71 typedef struct net_time_s { 72 int net_time_yr; 73 int net_time_mon; 74 int net_time_day; 75 int net_time_hr; 76 int net_time_min; 77 int net_time_sec; 78 } net_time_t; 79 80 /* Flow/Link Stats */ 81 typedef struct net_stat_s { 82 char net_stat_name[LIFNAMSIZ]; 83 uint64_t net_stat_ibytes; 84 uint64_t net_stat_obytes; 85 uint64_t net_stat_ipackets; 86 uint64_t net_stat_opackets; 87 uint64_t net_stat_ierrors; 88 uint64_t net_stat_oerrors; 89 uint64_t net_stat_tibytes; 90 uint64_t net_stat_tobytes; 91 uint64_t net_stat_tipackets; 92 uint64_t net_stat_topackets; 93 uint64_t net_stat_tierrors; 94 uint64_t net_stat_toerrors; 95 uint64_t net_stat_ctime; 96 uint64_t net_stat_tdiff; 97 net_time_t net_stat_time; 98 struct net_stat_s *net_stat_next; 99 net_desc_t *net_stat_desc; 100 boolean_t net_stat_isref; 101 } net_stat_t; 102 103 /* Used to create the [gnu]plot file */ 104 typedef struct net_plot_entry_s { 105 char *net_pe_name; 106 uint64_t net_pe_tottime; 107 uint64_t net_pe_totbytes; 108 uint64_t net_pe_totibytes; 109 uint64_t net_pe_totobytes; 110 uint64_t net_pe_lasttime; 111 } net_plot_entry_t; 112 113 /* Stats entry */ 114 typedef struct net_entry_s { 115 net_desc_t *net_entry_desc; 116 net_stat_t *net_entry_shead; 117 net_stat_t *net_entry_stail; 118 int net_entry_scount; 119 net_stat_t *net_entry_sref; 120 net_stat_t *net_entry_tstats; 121 uint64_t net_entry_ttime; 122 struct net_entry_s *net_entry_next; 123 } net_entry_t; 124 125 /* Time sorted list */ 126 typedef struct net_time_entry_s { 127 net_stat_t *my_time_stat; 128 struct net_time_entry_s *net_time_entry_next; 129 struct net_time_entry_s *net_time_entry_prev; 130 } net_time_entry_t; 131 132 /* The parsed table */ 133 typedef struct net_table_s { 134 /* List of stats */ 135 net_entry_t *net_table_head; 136 net_entry_t *net_table_tail; 137 int net_entries; 138 139 /* 140 * Optimization I : List sorted by time, i.e: 141 * Time Resource .. 142 * ------------------------------- 143 * 11.15.10 bge0 144 * 11.15.10 ce0 145 * 11.15.10 vnic1 146 * 11.15.15 bge0 147 * 11.15.15 ce0 148 * 11.15.15 vnic1 149 */ 150 net_time_entry_t *net_time_head; 151 net_time_entry_t *net_time_tail; 152 153 /* 154 * Optimization II : List sorted by resources 155 * Time Resource .. 156 * ------------------------------- 157 * 11.15.10 bge0 158 * 11.15.15 bge0 159 * 11.15.10 ce0 160 * 11.15.15 ce0 161 * 11.15.10 vnic1 162 * 11.15.15 vnic1 163 */ 164 net_time_entry_t *net_ctime_head; 165 net_time_entry_t *net_ctime_tail; 166 167 /* Common to both the above (sorted) lists. */ 168 int net_time_entries; 169 } net_table_t; 170 171 #define NET_DATE_GREATER 0 172 #define NET_DATE_LESSER 1 173 #define NET_DATE_EQUAL 2 174 175 #define NET_TIME_GREATER 0 176 #define NET_TIME_LESSER 1 177 #define NET_TIME_EQUAL 2 178 179 #ifndef _LP64 180 #define FMT_UINT64 "%-15llu" 181 #else 182 #define FMT_UINT64 "%-15lu" 183 #endif 184 185 /* 186 * Given a timebuf of the form M/D/Y,H:M:S break it into individual elements. 187 */ 188 static void 189 dissect_time(char *tbuf, net_time_t *nt) 190 { 191 char *d; 192 char *t; 193 char *dd; 194 char *h; 195 char *endp; 196 197 if (tbuf == NULL || nt == NULL) 198 return; 199 200 d = strtok(tbuf, ","); /* Date */ 201 t = strtok(NULL, ","); /* Time */ 202 203 /* Month */ 204 dd = strtok(d, "/"); 205 if (dd == NULL) 206 return; 207 nt->net_time_mon = strtol(dd, &endp, 10); 208 209 /* Day */ 210 dd = strtok(NULL, "/"); 211 if (dd == NULL) 212 return; 213 nt->net_time_day = strtol(dd, &endp, 10); 214 215 /* Year */ 216 dd = strtok(NULL, "/"); 217 if (dd == NULL) 218 return; 219 nt->net_time_yr = strtol(dd, &endp, 10); 220 if (strlen(dd) <= 2) 221 nt->net_time_yr += 2000; 222 223 if (t == NULL) 224 return; 225 226 /* Hour */ 227 h = strtok(t, ":"); 228 if (h == NULL) 229 return; 230 nt->net_time_hr = strtol(h, &endp, 10); 231 232 /* Min */ 233 h = strtok(NULL, ":"); 234 if (h == NULL) 235 return; 236 nt->net_time_min = strtol(h, &endp, 10); 237 238 /* Sec */ 239 h = strtok(NULL, ":"); 240 if (h == NULL) 241 return; 242 nt->net_time_sec = strtol(h, &endp, 10); 243 } 244 245 /* Get a stat item from an object in the exacct file */ 246 static void 247 add_stat_item(ea_object_t *o, net_stat_t *ns) 248 { 249 switch (o->eo_catalog & EXT_TYPE_MASK) { 250 case EXT_STRING: 251 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_NAME) { 252 (void) strncpy(ns->net_stat_name, o->eo_item.ei_string, 253 strlen(o->eo_item.ei_string)); 254 } 255 break; 256 case EXT_UINT64: 257 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_CURTIME) { 258 time_t _time; 259 char timebuf[TIMEBUFLEN]; 260 261 ns->net_stat_ctime = o->eo_item.ei_uint64; 262 _time = ns->net_stat_ctime; 263 (void) strftime(timebuf, sizeof (timebuf), 264 "%m/%d/%Y,%T\n", localtime(&_time)); 265 dissect_time(timebuf, &ns->net_stat_time); 266 } else if ((o->eo_catalog & EXD_DATA_MASK) == 267 EXD_NET_STATS_IBYTES) { 268 ns->net_stat_ibytes = o->eo_item.ei_uint64; 269 } else if ((o->eo_catalog & EXD_DATA_MASK) == 270 EXD_NET_STATS_OBYTES) { 271 ns->net_stat_obytes = o->eo_item.ei_uint64; 272 } else if ((o->eo_catalog & EXD_DATA_MASK) == 273 EXD_NET_STATS_IPKTS) { 274 ns->net_stat_ipackets = o->eo_item.ei_uint64; 275 } else if ((o->eo_catalog & EXD_DATA_MASK) == 276 EXD_NET_STATS_OPKTS) { 277 ns->net_stat_opackets = o->eo_item.ei_uint64; 278 } else if ((o->eo_catalog & EXD_DATA_MASK) == 279 EXD_NET_STATS_IERRPKTS) { 280 ns->net_stat_ierrors = o->eo_item.ei_uint64; 281 } else if ((o->eo_catalog & EXD_DATA_MASK) == 282 EXD_NET_STATS_OERRPKTS) { 283 ns->net_stat_oerrors = o->eo_item.ei_uint64; 284 } 285 break; 286 default: 287 break; 288 } 289 } 290 291 /* Get a description item from an object in the exacct file */ 292 static void 293 add_desc_item(ea_object_t *o, net_desc_t *nd) 294 { 295 switch (o->eo_catalog & EXT_TYPE_MASK) { 296 case EXT_STRING: 297 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_NAME) { 298 (void) strncpy(nd->net_desc_name, o->eo_item.ei_string, 299 strlen(o->eo_item.ei_string)); 300 } else if ((o->eo_catalog & EXD_DATA_MASK) == 301 EXD_NET_DESC_DEVNAME) { 302 (void) strncpy(nd->net_desc_devname, 303 o->eo_item.ei_string, strlen(o->eo_item.ei_string)); 304 } 305 break; 306 case EXT_UINT8: 307 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_PROTOCOL) { 308 nd->net_desc_protocol = o->eo_item.ei_uint8; 309 } else if ((o->eo_catalog & EXD_DATA_MASK) == 310 EXD_NET_DESC_DSFIELD) { 311 nd->net_desc_dsfield = o->eo_item.ei_uint8; 312 } 313 break; 314 case EXT_UINT16: 315 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_SPORT) { 316 nd->net_desc_sport = o->eo_item.ei_uint16; 317 } else if ((o->eo_catalog & EXD_DATA_MASK) == 318 EXD_NET_DESC_DPORT) { 319 nd->net_desc_dport = o->eo_item.ei_uint16; 320 } else if ((o->eo_catalog & EXD_DATA_MASK) == 321 EXD_NET_DESC_SAP) { 322 nd->net_desc_sap = o->eo_item.ei_uint16; 323 } else if ((o->eo_catalog & EXD_DATA_MASK) == 324 EXD_NET_DESC_VLAN_TPID) { 325 nd->net_desc_vlan_tpid = o->eo_item.ei_uint16; 326 } else if ((o->eo_catalog & EXD_DATA_MASK) == 327 EXD_NET_DESC_VLAN_TCI) { 328 nd->net_desc_vlan_tci = o->eo_item.ei_uint16; 329 } else if ((o->eo_catalog & EXD_DATA_MASK) == 330 EXD_NET_DESC_PRIORITY) { 331 nd->net_desc_priority = o->eo_item.ei_uint16; 332 } 333 break; 334 case EXT_UINT32: 335 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4SADDR || 336 (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4DADDR) { 337 struct in_addr addr; 338 339 addr.s_addr = htonl(o->eo_item.ei_uint32); 340 341 if ((o->eo_catalog & EXD_DATA_MASK) == 342 EXD_NET_DESC_V4SADDR) { 343 IN6_INADDR_TO_V4MAPPED(&addr, 344 &nd->net_desc_saddr); 345 } else { 346 IN6_INADDR_TO_V4MAPPED(&addr, 347 &nd->net_desc_daddr); 348 } 349 } 350 break; 351 case EXT_UINT64: 352 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_BWLIMIT) 353 nd->net_desc_bw_limit = o->eo_item.ei_uint64; 354 break; 355 case EXT_RAW: 356 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6SADDR || 357 (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6DADDR) { 358 in6_addr_t addr; 359 360 addr = *(in6_addr_t *)o->eo_item.ei_raw; 361 if ((o->eo_catalog & EXD_DATA_MASK) == 362 EXD_NET_DESC_V6SADDR) { 363 nd->net_desc_saddr = addr; 364 } else { 365 nd->net_desc_daddr = addr; 366 } 367 } else if ((o->eo_catalog & EXD_DATA_MASK) == 368 EXD_NET_DESC_EHOST) { 369 bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_ehost, 370 ETHERADDRL); 371 } else if ((o->eo_catalog & EXD_DATA_MASK) == 372 EXD_NET_DESC_EDEST) { 373 bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_edest, 374 ETHERADDRL); 375 } 376 break; 377 default: 378 break; 379 } 380 } 381 382 /* Add a description item to the table */ 383 static dladm_status_t 384 add_desc_to_tbl(net_table_t *net_table, net_desc_t *nd) 385 { 386 net_entry_t *ne; 387 388 if ((ne = calloc(1, sizeof (net_entry_t))) == NULL) 389 return (DLADM_STATUS_NOMEM); 390 391 if ((ne->net_entry_tstats = calloc(1, sizeof (net_stat_t))) == NULL) { 392 free(ne); 393 return (DLADM_STATUS_NOMEM); 394 } 395 396 ne->net_entry_desc = nd; 397 ne->net_entry_shead = NULL; 398 ne->net_entry_stail = NULL; 399 ne->net_entry_scount = 0; 400 401 if (net_table->net_table_head == NULL) { 402 net_table->net_table_head = ne; 403 net_table->net_table_tail = ne; 404 } else { 405 net_table->net_table_tail->net_entry_next = ne; 406 net_table->net_table_tail = ne; 407 } 408 net_table->net_entries++; 409 return (DLADM_STATUS_OK); 410 } 411 412 /* Compare dates and return if t1 is equal, greater or lesser than t2 */ 413 static int 414 compare_date(net_time_t *t1, net_time_t *t2) 415 { 416 if (t1->net_time_yr == t2->net_time_yr && 417 t1->net_time_mon == t2->net_time_mon && 418 t1->net_time_day == t2->net_time_day) { 419 return (NET_DATE_EQUAL); 420 } 421 if (t1->net_time_yr > t2->net_time_yr || 422 (t1->net_time_yr == t2->net_time_yr && 423 t1->net_time_mon > t2->net_time_mon) || 424 (t1->net_time_yr == t2->net_time_yr && 425 t1->net_time_mon == t2->net_time_mon && 426 t1->net_time_day > t2->net_time_day)) { 427 return (NET_DATE_GREATER); 428 } 429 return (NET_DATE_LESSER); 430 } 431 432 /* Compare times and return if t1 is equal, greater or lesser than t2 */ 433 static int 434 compare_time(net_time_t *t1, net_time_t *t2) 435 { 436 int cd; 437 438 cd = compare_date(t1, t2); 439 440 if (cd == NET_DATE_GREATER) { 441 return (NET_TIME_GREATER); 442 } else if (cd == NET_DATE_LESSER) { 443 return (NET_TIME_LESSER); 444 } else { 445 if (t1->net_time_hr == t2->net_time_hr && 446 t1->net_time_min == t2->net_time_min && 447 t1->net_time_sec == t2->net_time_sec) { 448 return (NET_TIME_EQUAL); 449 } 450 if (t1->net_time_hr > t2->net_time_hr || 451 (t1->net_time_hr == t2->net_time_hr && 452 t1->net_time_min > t2->net_time_min) || 453 (t1->net_time_hr == t2->net_time_hr && 454 t1->net_time_min == t2->net_time_min && 455 t1->net_time_sec > t2->net_time_sec)) { 456 return (NET_TIME_GREATER); 457 } 458 } 459 return (NET_TIME_LESSER); 460 } 461 462 /* 463 * Given a start and end time and start and end entries check if the 464 * times are within the range, and adjust, if needed. 465 */ 466 static dladm_status_t 467 chk_time_bound(net_time_t *s, net_time_t *e, net_time_t *sns, 468 net_time_t *ens) 469 { 470 if (s != NULL && e != NULL) { 471 if (compare_time(s, e) == NET_TIME_GREATER) 472 return (DLADM_STATUS_BADTIMEVAL); 473 } 474 if (s != NULL) { 475 if (compare_time(s, sns) == NET_TIME_LESSER) { 476 s->net_time_yr = sns->net_time_yr; 477 s->net_time_mon = sns->net_time_mon; 478 s->net_time_day = sns->net_time_day; 479 s->net_time_hr = sns->net_time_hr; 480 s->net_time_min = sns->net_time_min; 481 s->net_time_sec = sns->net_time_sec; 482 } 483 } 484 if (e != NULL) { 485 if (compare_time(e, ens) == NET_TIME_GREATER) { 486 e->net_time_yr = ens->net_time_yr; 487 e->net_time_mon = ens->net_time_mon; 488 e->net_time_day = ens->net_time_day; 489 e->net_time_hr = ens->net_time_hr; 490 e->net_time_min = ens->net_time_min; 491 e->net_time_sec = ens->net_time_sec; 492 } 493 } 494 return (DLADM_STATUS_OK); 495 } 496 497 /* 498 * Given a start and end time (strings), convert them into net_time_t 499 * and also check for the range given the head and tail of the list. 500 * If stime is lower then head or etime is greated than tail, adjust. 501 */ 502 static dladm_status_t 503 get_time_range(net_time_entry_t *head, net_time_entry_t *tail, 504 net_time_t *st, net_time_t *et, char *stime, char *etime) 505 { 506 bzero(st, sizeof (net_time_t)); 507 bzero(et, sizeof (net_time_t)); 508 509 if (stime == NULL && etime == NULL) 510 return (0); 511 512 if (stime != NULL) 513 dissect_time(stime, st); 514 if (etime != NULL) 515 dissect_time(etime, et); 516 517 if (stime != NULL || etime != NULL) { 518 return (chk_time_bound(stime == NULL ? NULL : st, 519 etime == NULL ? NULL : et, 520 &head->my_time_stat->net_stat_time, 521 &tail->my_time_stat->net_stat_time)); 522 } 523 return (0); 524 } 525 526 /* 527 * Walk the list from a given starting point and return when we find 528 * an entry that is greater or equal to st. lasttime will point to the 529 * previous time entry. 530 */ 531 static void 532 get_starting_point(net_time_entry_t *head, net_time_entry_t **start, 533 net_time_t *st, char *stime, uint64_t *lasttime) 534 { 535 net_time_entry_t *next = head; 536 537 if (head == NULL) { 538 *start = NULL; 539 return; 540 } 541 if (stime == NULL) { 542 *start = head; 543 *lasttime = head->my_time_stat->net_stat_ctime; 544 return; 545 } 546 *start = NULL; 547 while (next != NULL) { 548 if (compare_time(st, 549 &next->my_time_stat->net_stat_time) != NET_TIME_LESSER) { 550 *lasttime = next->my_time_stat->net_stat_ctime; 551 next = next->net_time_entry_next; 552 continue; 553 } 554 *start = next; 555 break; 556 } 557 } 558 559 /* 560 * Point entry (pe) functions 561 */ 562 /* Clear all the counters. Done after the contents are written to the file */ 563 static void 564 clear_pe(net_plot_entry_t *pe, int entries, int *pentries) 565 { 566 int count; 567 568 for (count = 0; count < entries; count++) { 569 pe[count].net_pe_totbytes = 0; 570 pe[count].net_pe_totibytes = 0; 571 pe[count].net_pe_totobytes = 0; 572 pe[count].net_pe_tottime = 0; 573 } 574 *pentries = 0; 575 } 576 577 /* Update an entry in the point entry table */ 578 static void 579 update_pe(net_plot_entry_t *pe, net_stat_t *nns, int nentries, 580 int *pentries, uint64_t lasttime) 581 { 582 int count; 583 584 for (count = 0; count < nentries; count++) { 585 if (strcmp(pe[count].net_pe_name, nns->net_stat_name) == 0) 586 break; 587 } 588 if (count == nentries) 589 return; 590 591 if (pe[count].net_pe_totbytes == 0) 592 pe[count].net_pe_lasttime = lasttime; 593 594 pe[count].net_pe_totbytes += nns->net_stat_ibytes + 595 nns->net_stat_obytes; 596 pe[count].net_pe_tottime += nns->net_stat_tdiff; 597 pe[count].net_pe_totibytes += nns->net_stat_ibytes; 598 pe[count].net_pe_totobytes += nns->net_stat_obytes; 599 (*pentries)++; 600 } 601 602 /* Flush the contents of the point entry table to the file. */ 603 static void 604 add_pe_to_file(int (*fn)(dladm_usage_t *, void *), net_plot_entry_t *pe, 605 net_stat_t *ns, int entries, void *arg) 606 { 607 int count; 608 dladm_usage_t usage; 609 uint64_t tottime; 610 611 bcopy(&ns->net_stat_ctime, &usage.du_etime, sizeof (usage.du_etime)); 612 for (count = 0; count < entries; count++) { 613 bcopy(pe[count].net_pe_name, &usage.du_name, 614 sizeof (usage.du_name)); 615 bcopy(&pe[count].net_pe_lasttime, &usage.du_stime, 616 sizeof (usage.du_stime)); 617 usage.du_rbytes = pe[count].net_pe_totibytes; 618 usage.du_obytes = pe[count].net_pe_totobytes; 619 tottime = pe[count].net_pe_tottime; 620 usage.du_bandwidth = (tottime > 0) ? 621 ((pe[count].net_pe_totbytes * 8) / tottime) : 0; 622 usage.du_last = (count == entries-1); 623 fn(&usage, arg); 624 } 625 } 626 627 /* 628 * Net entry functions 629 */ 630 static net_entry_t * 631 get_ne_from_table(net_table_t *net_table, char *name) 632 { 633 int count; 634 net_desc_t *nd; 635 net_entry_t *ne = net_table->net_table_head; 636 637 for (count = 0; count < net_table->net_entries; count++) { 638 nd = ne->net_entry_desc; 639 if (strcmp(name, nd->net_desc_name) == 0) 640 return (ne); 641 ne = ne->net_entry_next; 642 } 643 return (NULL); 644 } 645 646 /* Get the entry for the descriptor, if it exists */ 647 static net_desc_t * 648 get_ndesc(net_table_t *net_table, net_desc_t *nd) 649 { 650 int count; 651 net_desc_t *nd1; 652 net_entry_t *ne = net_table->net_table_head; 653 654 for (count = 0; count < net_table->net_entries; count++) { 655 nd1 = ne->net_entry_desc; 656 if (strcmp(nd1->net_desc_name, nd->net_desc_name) == 0 && 657 strcmp(nd1->net_desc_devname, nd->net_desc_devname) == 0 && 658 bcmp(nd1->net_desc_ehost, nd->net_desc_ehost, 659 ETHERADDRL) == 0 && 660 bcmp(nd1->net_desc_edest, nd->net_desc_edest, 661 ETHERADDRL) == 0 && 662 nd1->net_desc_vlan_tpid == nd->net_desc_vlan_tpid && 663 nd1->net_desc_vlan_tci == nd->net_desc_vlan_tci && 664 nd1->net_desc_sap == nd->net_desc_sap && 665 nd1->net_desc_cpuid == nd->net_desc_cpuid && 666 nd1->net_desc_priority == nd->net_desc_priority && 667 nd1->net_desc_bw_limit == nd->net_desc_bw_limit && 668 nd1->net_desc_sport == nd->net_desc_sport && 669 nd1->net_desc_dport == nd->net_desc_dport && 670 nd1->net_desc_protocol == nd->net_desc_protocol && 671 nd1->net_desc_dsfield == nd->net_desc_dsfield && 672 IN6_ARE_ADDR_EQUAL(&nd1->net_desc_saddr, 673 &nd->net_desc_saddr) && 674 IN6_ARE_ADDR_EQUAL(&nd1->net_desc_daddr, 675 &nd->net_desc_daddr)) { 676 return (nd1); 677 } 678 ne = ne->net_entry_next; 679 } 680 return (NULL); 681 } 682 683 /* 684 * Update the stat entries. The stats in the file are cumulative, so in order 685 * to have increments, we maintain a reference stat entry, which contains 686 * the stats when the record was first written and a total stat entry, which 687 * maintains the running count. When we want to add a stat entry, if it 688 * the reference stat entry, we don't come here. For subsequent entries, 689 * we get the increment by subtracting the current value from the reference 690 * stat and the total stat. 691 */ 692 static void 693 update_stats(net_stat_t *ns1, net_entry_t *ne, net_stat_t *ref) 694 { 695 696 /* get the increment */ 697 ns1->net_stat_ibytes -= (ref->net_stat_ibytes + ref->net_stat_tibytes); 698 ns1->net_stat_obytes -= (ref->net_stat_obytes + ref->net_stat_tobytes); 699 ns1->net_stat_ipackets -= (ref->net_stat_ipackets + 700 ref->net_stat_tipackets); 701 ns1->net_stat_opackets -= (ref->net_stat_opackets + 702 ref->net_stat_topackets); 703 ns1->net_stat_ierrors -= (ref->net_stat_ierrors + 704 ref->net_stat_tierrors); 705 ns1->net_stat_oerrors -= (ref->net_stat_oerrors + 706 ref->net_stat_toerrors); 707 708 /* update total bytes */ 709 ref->net_stat_tibytes += ns1->net_stat_ibytes; 710 ref->net_stat_tobytes += ns1->net_stat_obytes; 711 ref->net_stat_tipackets += ns1->net_stat_ipackets; 712 ref->net_stat_topackets += ns1->net_stat_opackets; 713 ref->net_stat_tierrors += ns1->net_stat_ierrors; 714 ref->net_stat_toerrors += ns1->net_stat_oerrors; 715 716 ne->net_entry_tstats->net_stat_ibytes += ns1->net_stat_ibytes; 717 ne->net_entry_tstats->net_stat_obytes += ns1->net_stat_obytes; 718 ne->net_entry_tstats->net_stat_ipackets += ns1->net_stat_ipackets; 719 ne->net_entry_tstats->net_stat_opackets += ns1->net_stat_opackets; 720 ne->net_entry_tstats->net_stat_ierrors += ns1->net_stat_ierrors; 721 ne->net_entry_tstats->net_stat_oerrors += ns1->net_stat_oerrors; 722 } 723 724 /* Add the stat entry into the table */ 725 static dladm_status_t 726 add_stat_to_tbl(net_table_t *net_table, net_stat_t *ns) 727 { 728 net_entry_t *ne; 729 730 ne = get_ne_from_table(net_table, ns->net_stat_name); 731 if (ne == NULL) 732 return (DLADM_STATUS_NOMEM); 733 734 /* Ptr to flow desc */ 735 ns->net_stat_desc = ne->net_entry_desc; 736 if (ns->net_stat_desc->net_desc_newrec) { 737 ns->net_stat_desc->net_desc_newrec = B_FALSE; 738 ns->net_stat_isref = B_TRUE; 739 ne->net_entry_sref = ns; 740 } else if (ns->net_stat_ibytes < ne->net_entry_sref->net_stat_tibytes || 741 (ns->net_stat_obytes < ne->net_entry_sref->net_stat_tobytes)) { 742 ns->net_stat_isref = B_TRUE; 743 ne->net_entry_sref = ns; 744 } else { 745 ns->net_stat_isref = B_FALSE; 746 update_stats(ns, ne, ne->net_entry_sref); 747 } 748 if (ne->net_entry_shead == NULL) { 749 ne->net_entry_shead = ns; 750 ne->net_entry_stail = ns; 751 } else { 752 if (!ns->net_stat_isref) { 753 ne->net_entry_ttime += (ns->net_stat_ctime - 754 ne->net_entry_stail->net_stat_ctime); 755 ns->net_stat_tdiff = ns->net_stat_ctime - 756 ne->net_entry_stail->net_stat_ctime; 757 } 758 ne->net_entry_stail->net_stat_next = ns; 759 ne->net_entry_stail = ns; 760 } 761 762 ne->net_entry_scount++; 763 return (DLADM_STATUS_OK); 764 } 765 766 /* Add a flow/link descriptor record to the table */ 767 static dladm_status_t 768 add_desc(net_table_t *net_table, ea_file_t *ef, int nobjs) 769 { 770 net_desc_t *nd; 771 net_desc_t *dnd; 772 int count; 773 ea_object_t scratch; 774 775 if ((nd = calloc(1, sizeof (net_desc_t))) == NULL) 776 return (DLADM_STATUS_NOMEM); 777 nd->net_desc_newrec = B_TRUE; 778 779 for (count = 0; count < nobjs; count++) { 780 if (ea_get_object(ef, &scratch) == -1) { 781 free(nd); 782 return (DLADM_STATUS_NOMEM); 783 } 784 add_desc_item(&scratch, nd); 785 } 786 if ((dnd = get_ndesc(net_table, nd)) != NULL) { 787 dnd->net_desc_newrec = B_TRUE; 788 free(nd); 789 return (DLADM_STATUS_OK); 790 } 791 if (add_desc_to_tbl(net_table, nd) != 0) { 792 free(nd); 793 return (DLADM_STATUS_NOMEM); 794 } 795 return (DLADM_STATUS_OK); 796 } 797 798 /* Make an entry into the time sorted list */ 799 static void 800 addto_time_list(net_table_t *net_table, net_time_entry_t *nt, 801 net_time_entry_t *ntc) 802 { 803 net_stat_t *ns = nt->my_time_stat; 804 net_stat_t *ns1; 805 net_time_entry_t *end; 806 net_time_t *t1; 807 int count; 808 809 t1 = &ns->net_stat_time; 810 811 net_table->net_time_entries++; 812 813 if (net_table->net_time_head == NULL) { 814 net_table->net_time_head = nt; 815 net_table->net_time_tail = nt; 816 } else { 817 net_table->net_time_tail->net_time_entry_next = nt; 818 nt->net_time_entry_prev = net_table->net_time_tail; 819 net_table->net_time_tail = nt; 820 } 821 822 if (net_table->net_ctime_head == NULL) { 823 net_table->net_ctime_head = ntc; 824 net_table->net_ctime_tail = ntc; 825 } else { 826 end = net_table->net_ctime_tail; 827 count = 0; 828 while (count < net_table->net_time_entries - 1) { 829 ns1 = end->my_time_stat; 830 /* Just add it to the tail */ 831 if (compare_date(t1, &ns1->net_stat_time) == 832 NET_DATE_GREATER) { 833 break; 834 } 835 if (strcmp(ns1->net_stat_name, ns->net_stat_name) == 836 0) { 837 ntc->net_time_entry_next = 838 end->net_time_entry_next; 839 if (end->net_time_entry_next != NULL) { 840 end->net_time_entry_next-> 841 net_time_entry_prev = ntc; 842 } else { 843 net_table->net_ctime_tail = ntc; 844 } 845 end->net_time_entry_next = ntc; 846 ntc->net_time_entry_prev = end; 847 return; 848 } 849 count++; 850 end = end->net_time_entry_prev; 851 } 852 net_table->net_ctime_tail->net_time_entry_next = ntc; 853 ntc->net_time_entry_prev = net_table->net_ctime_tail; 854 net_table->net_ctime_tail = ntc; 855 } 856 } 857 858 /* Add stat entry into the lists */ 859 static dladm_status_t 860 add_stats(net_table_t *net_table, ea_file_t *ef, int nobjs) 861 { 862 net_stat_t *ns; 863 int count; 864 ea_object_t scratch; 865 net_time_entry_t *nt; 866 net_time_entry_t *ntc; 867 868 if ((ns = calloc(1, sizeof (net_stat_t))) == NULL) 869 return (DLADM_STATUS_NOMEM); 870 871 if ((nt = calloc(1, sizeof (net_time_entry_t))) == NULL) { 872 free(ns); 873 return (DLADM_STATUS_NOMEM); 874 } 875 if ((ntc = calloc(1, sizeof (net_time_entry_t))) == NULL) { 876 free(ns); 877 free(nt); 878 return (DLADM_STATUS_NOMEM); 879 } 880 881 nt->my_time_stat = ns; 882 ntc->my_time_stat = ns; 883 884 for (count = 0; count < nobjs; count++) { 885 if (ea_get_object(ef, &scratch) == -1) { 886 free(ns); 887 free(nt); 888 free(ntc); 889 return (DLADM_STATUS_NOMEM); 890 } 891 add_stat_item(&scratch, ns); 892 } 893 if (add_stat_to_tbl(net_table, ns) != 0) { 894 free(ns); 895 free(nt); 896 free(ntc); 897 return (DLADM_STATUS_NOMEM); 898 } 899 addto_time_list(net_table, nt, ntc); 900 return (DLADM_STATUS_OK); 901 } 902 903 /* Free the entire table */ 904 static void 905 free_logtable(net_table_t *net_table) 906 { 907 net_entry_t *head; 908 net_entry_t *next; 909 net_stat_t *ns; 910 net_stat_t *ns1; 911 net_time_entry_t *thead; 912 net_time_entry_t *tnext; 913 914 thead = net_table->net_time_head; 915 while (thead != NULL) { 916 thead->my_time_stat = NULL; 917 tnext = thead->net_time_entry_next; 918 thead->net_time_entry_next = NULL; 919 thead->net_time_entry_prev = NULL; 920 free(thead); 921 thead = tnext; 922 } 923 net_table->net_time_head = NULL; 924 net_table->net_time_tail = NULL; 925 926 thead = net_table->net_ctime_head; 927 while (thead != NULL) { 928 thead->my_time_stat = NULL; 929 tnext = thead->net_time_entry_next; 930 thead->net_time_entry_next = NULL; 931 thead->net_time_entry_prev = NULL; 932 free(thead); 933 thead = tnext; 934 } 935 net_table->net_ctime_head = NULL; 936 net_table->net_ctime_tail = NULL; 937 938 net_table->net_time_entries = 0; 939 940 head = net_table->net_table_head; 941 while (head != NULL) { 942 next = head->net_entry_next; 943 head->net_entry_next = NULL; 944 ns = head->net_entry_shead; 945 while (ns != NULL) { 946 ns1 = ns->net_stat_next; 947 free(ns); 948 ns = ns1; 949 } 950 head->net_entry_scount = 0; 951 head->net_entry_sref = NULL; 952 free(head->net_entry_desc); 953 free(head->net_entry_tstats); 954 free(head); 955 head = next; 956 } 957 net_table->net_table_head = NULL; 958 net_table->net_table_tail = NULL; 959 net_table->net_time_entries = 0; 960 free(net_table); 961 } 962 963 /* Parse the exacct file, and return the parsed table. */ 964 static void * 965 parse_logfile(char *file, int logtype, dladm_status_t *status) 966 { 967 ea_file_t ef; 968 ea_object_t scratch; 969 net_table_t *net_table; 970 971 *status = DLADM_STATUS_OK; 972 if ((net_table = calloc(1, sizeof (net_table_t))) == NULL) { 973 *status = DLADM_STATUS_NOMEM; 974 return (NULL); 975 } 976 if (ea_open(&ef, file, NULL, 0, O_RDONLY, 0) == -1) { 977 *status = DLADM_STATUS_BADARG; 978 free(net_table); 979 return (NULL); 980 } 981 bzero(&scratch, sizeof (ea_object_t)); 982 while (ea_get_object(&ef, &scratch) != -1) { 983 if (scratch.eo_type != EO_GROUP) { 984 (void) ea_free_item(&scratch, EUP_ALLOC); 985 (void) bzero(&scratch, sizeof (ea_object_t)); 986 continue; 987 } 988 /* Read Link Desc/Stat records */ 989 if (logtype == DLADM_LOGTYPE_FLOW) { 990 /* Flow Descriptor */ 991 if ((scratch.eo_catalog & 992 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC) { 993 (void) add_desc(net_table, &ef, 994 scratch.eo_group.eg_nobjs - 1); 995 /* Flow Stats */ 996 } else if ((scratch.eo_catalog & 997 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS) { 998 (void) add_stats(net_table, &ef, 999 scratch.eo_group.eg_nobjs - 1); 1000 } 1001 } else if (logtype == DLADM_LOGTYPE_LINK) { 1002 /* Link Descriptor */ 1003 if ((scratch.eo_catalog & 1004 EXD_DATA_MASK) == EXD_GROUP_NET_LINK_DESC) { 1005 (void) add_desc(net_table, &ef, 1006 scratch.eo_group.eg_nobjs - 1); 1007 /* Link Stats */ 1008 } else if ((scratch.eo_catalog & 1009 EXD_DATA_MASK) == EXD_GROUP_NET_LINK_STATS) { 1010 (void) add_stats(net_table, &ef, 1011 scratch.eo_group.eg_nobjs - 1); 1012 } 1013 } else { 1014 if (((scratch.eo_catalog & EXD_DATA_MASK) == 1015 EXD_GROUP_NET_LINK_DESC) || ((scratch.eo_catalog & 1016 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC)) { 1017 (void) add_desc(net_table, &ef, 1018 scratch.eo_group.eg_nobjs - 1); 1019 } else if (((scratch.eo_catalog & EXD_DATA_MASK) == 1020 EXD_GROUP_NET_LINK_STATS) || ((scratch.eo_catalog & 1021 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS)) { 1022 (void) add_stats(net_table, &ef, 1023 scratch.eo_group.eg_nobjs - 1); 1024 } 1025 } 1026 (void) ea_free_item(&scratch, EUP_ALLOC); 1027 (void) bzero(&scratch, sizeof (ea_object_t)); 1028 } 1029 1030 (void) ea_close(&ef); 1031 return ((void *)net_table); 1032 } 1033 1034 /* 1035 * Walk the ctime list. This is used when looking for usage records 1036 * based on a "resource" name. 1037 */ 1038 dladm_status_t 1039 dladm_walk_usage_res(int (*fn)(dladm_usage_t *, void *), int logtype, 1040 char *logfile, char *resource, char *stime, char *etime, void *arg) 1041 { 1042 net_table_t *net_table; 1043 net_time_t st, et; 1044 net_time_entry_t *start; 1045 net_stat_t *ns = NULL; 1046 net_stat_t *nns; 1047 uint64_t tot_time = 0; 1048 uint64_t last_time; 1049 uint64_t tot_bytes = 0; 1050 uint64_t tot_ibytes = 0; 1051 uint64_t tot_obytes = 0; 1052 boolean_t gotstart = B_FALSE; 1053 dladm_status_t status; 1054 dladm_usage_t usage; 1055 int step = 1; 1056 1057 /* Parse the log file */ 1058 net_table = parse_logfile(logfile, logtype, &status); 1059 if (net_table == NULL) 1060 return (status); 1061 1062 if (net_table->net_entries == 0) 1063 return (DLADM_STATUS_OK); 1064 start = net_table->net_ctime_head; 1065 1066 /* Time range */ 1067 status = get_time_range(net_table->net_ctime_head, 1068 net_table->net_ctime_tail, &st, &et, stime, etime); 1069 if (status != DLADM_STATUS_OK) 1070 return (status); 1071 1072 while (start != NULL) { 1073 nns = start->my_time_stat; 1074 1075 /* Get to the resource we are interested in */ 1076 if (strcmp(resource, nns->net_stat_name) != 0) { 1077 start = start->net_time_entry_next; 1078 continue; 1079 } 1080 1081 /* Find the first record */ 1082 if (!gotstart) { 1083 get_starting_point(start, &start, &st, stime, 1084 &last_time); 1085 if (start == NULL) 1086 break; 1087 nns = start->my_time_stat; 1088 gotstart = B_TRUE; 1089 } 1090 1091 /* Write one entry and return if we are out of the range */ 1092 if (etime != NULL && compare_time(&nns->net_stat_time, &et) 1093 == NET_TIME_GREATER) { 1094 if (tot_bytes != 0) { 1095 bcopy(ns->net_stat_name, &usage.du_name, 1096 sizeof (usage.du_name)); 1097 bcopy(&last_time, &usage.du_stime, 1098 sizeof (usage.du_stime)); 1099 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1100 sizeof (usage.du_etime)); 1101 usage.du_rbytes = tot_ibytes; 1102 usage.du_obytes = tot_obytes; 1103 usage.du_bandwidth = tot_bytes*8/tot_time; 1104 usage.du_last = B_TRUE; 1105 fn(&usage, arg); 1106 } 1107 return (DLADM_STATUS_OK); 1108 } 1109 1110 /* 1111 * If this is a reference entry, just print what we have 1112 * and proceed. 1113 */ 1114 if (nns->net_stat_isref) { 1115 if (tot_bytes != 0) { 1116 bcopy(&nns->net_stat_name, &usage.du_name, 1117 sizeof (usage.du_name)); 1118 bcopy(&nns->net_stat_ctime, &usage.du_stime, 1119 sizeof (usage.du_stime)); 1120 usage.du_rbytes = tot_ibytes; 1121 usage.du_obytes = tot_obytes; 1122 usage.du_bandwidth = tot_bytes*8/tot_time; 1123 usage.du_last = B_TRUE; 1124 fn(&usage, arg); 1125 NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes, 1126 tot_obytes, step); 1127 } 1128 last_time = nns->net_stat_ctime; 1129 start = start->net_time_entry_next; 1130 continue; 1131 } 1132 1133 ns = nns; 1134 if (--step == 0) { 1135 tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes; 1136 tot_ibytes += ns->net_stat_ibytes; 1137 tot_obytes += ns->net_stat_obytes; 1138 tot_time += ns->net_stat_tdiff; 1139 bcopy(&ns->net_stat_name, &usage.du_name, 1140 sizeof (usage.du_name)); 1141 bcopy(&last_time, &usage.du_stime, 1142 sizeof (usage.du_stime)); 1143 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1144 sizeof (usage.du_etime)); 1145 usage.du_rbytes = tot_ibytes; 1146 usage.du_obytes = tot_obytes; 1147 usage.du_bandwidth = tot_bytes*8/tot_time; 1148 usage.du_last = B_TRUE; 1149 fn(&usage, arg); 1150 1151 NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes, 1152 tot_obytes, step); 1153 last_time = ns->net_stat_ctime; 1154 } else { 1155 tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes; 1156 tot_ibytes += ns->net_stat_ibytes; 1157 tot_obytes += ns->net_stat_obytes; 1158 tot_time += ns->net_stat_tdiff; 1159 } 1160 start = start->net_time_entry_next; 1161 } 1162 1163 if (tot_bytes != 0) { 1164 bcopy(&ns->net_stat_name, &usage.du_name, 1165 sizeof (usage.du_name)); 1166 bcopy(&last_time, &usage.du_stime, 1167 sizeof (usage.du_stime)); 1168 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1169 sizeof (usage.du_etime)); 1170 usage.du_rbytes = tot_ibytes; 1171 usage.du_obytes = tot_obytes; 1172 usage.du_bandwidth = tot_bytes*8/tot_time; 1173 usage.du_last = B_TRUE; 1174 fn(&usage, arg); 1175 } 1176 1177 free_logtable(net_table); 1178 return (status); 1179 } 1180 1181 /* 1182 * Walk the time sorted list if a resource is not specified. 1183 */ 1184 dladm_status_t 1185 dladm_walk_usage_time(int (*fn)(dladm_usage_t *, void *), int logtype, 1186 char *logfile, char *stime, char *etime, void *arg) 1187 { 1188 net_table_t *net_table; 1189 net_time_entry_t *start; 1190 net_stat_t *ns = NULL, *nns; 1191 net_time_t st, et, *t1; 1192 net_desc_t *nd; 1193 net_entry_t *ne; 1194 net_plot_entry_t *pe; 1195 int count; 1196 int step = 1; 1197 int nentries = 0, pentries = 0; 1198 uint64_t last_time; 1199 dladm_status_t status; 1200 1201 /* Parse the log file */ 1202 net_table = parse_logfile(logfile, logtype, &status); 1203 if (net_table == NULL) 1204 return (status); 1205 1206 if (net_table->net_entries == 0) 1207 return (DLADM_STATUS_OK); 1208 start = net_table->net_time_head; 1209 1210 /* Find the first and last records and starting point */ 1211 status = get_time_range(net_table->net_time_head, 1212 net_table->net_time_tail, &st, &et, stime, etime); 1213 if (status != DLADM_STATUS_OK) 1214 return (status); 1215 get_starting_point(start, &start, &st, stime, &last_time); 1216 /* 1217 * Could assert to be non-null, since get_time_range() 1218 * would have adjusted. 1219 */ 1220 if (start == NULL) 1221 return (DLADM_STATUS_BADTIMEVAL); 1222 1223 /* 1224 * Collect entries for all resources in a time slot before 1225 * writing to the file. 1226 */ 1227 nentries = net_table->net_entries; 1228 1229 pe = malloc(sizeof (net_plot_entry_t) * net_table->net_entries + 1); 1230 if (pe == NULL) 1231 return (DLADM_STATUS_NOMEM); 1232 1233 ne = net_table->net_table_head; 1234 for (count = 0; count < nentries; count++) { 1235 nd = ne->net_entry_desc; 1236 pe[count].net_pe_name = nd->net_desc_name; 1237 ne = ne->net_entry_next; 1238 } 1239 1240 clear_pe(pe, nentries, &pentries); 1241 1242 /* Write header to file */ 1243 /* add_pe_to_file(fn, pe, ns, nentries, arg); */ 1244 1245 t1 = &start->my_time_stat->net_stat_time; 1246 1247 while (start != NULL) { 1248 1249 nns = start->my_time_stat; 1250 /* 1251 * We have crossed the time boundary, check if we need to 1252 * print out now. 1253 */ 1254 if (compare_time(&nns->net_stat_time, t1) == 1255 NET_TIME_GREATER) { 1256 /* return if we are out of the range */ 1257 if (etime != NULL && 1258 compare_time(&nns->net_stat_time, &et) == 1259 NET_TIME_GREATER) { 1260 if (pentries > 0) { 1261 add_pe_to_file(fn, pe, ns, nentries, 1262 arg); 1263 clear_pe(pe, nentries, &pentries); 1264 } 1265 free(pe); 1266 return (DLADM_STATUS_OK); 1267 } 1268 /* update the stats from the ns. */ 1269 t1 = &nns->net_stat_time; 1270 last_time = ns->net_stat_ctime; 1271 if (--step == 0) { 1272 if (pentries > 0) { 1273 add_pe_to_file(fn, pe, ns, nentries, 1274 arg); 1275 clear_pe(pe, nentries, &pentries); 1276 } 1277 step = 1; 1278 } 1279 } 1280 1281 /* 1282 * if this is a reference entry, just print what we have 1283 * for this resource and proceed. We will end up writing 1284 * the stats for all the entries when we hit a ref element, 1285 * which means 'steps' for some might not be accurate, but 1286 * that is fine, the alternative is to write only the 1287 * resource for which we hit a reference entry. 1288 */ 1289 if (nns->net_stat_isref) { 1290 if (pentries > 0) { 1291 add_pe_to_file(fn, pe, ns, nentries, arg); 1292 clear_pe(pe, nentries, &pentries); 1293 } 1294 step = 1; 1295 } else { 1296 update_pe(pe, nns, nentries, &pentries, last_time); 1297 } 1298 ns = nns; 1299 start = start->net_time_entry_next; 1300 } 1301 1302 if (pentries > 0) 1303 add_pe_to_file(fn, pe, ns, nentries, arg); 1304 1305 free(pe); 1306 free_logtable(net_table); 1307 1308 return (DLADM_STATUS_OK); 1309 } 1310 1311 dladm_status_t 1312 dladm_usage_summary(int (*fn)(dladm_usage_t *, void *), int logtype, 1313 char *logfile, void *arg) 1314 { 1315 net_table_t *net_table; 1316 net_entry_t *ne; 1317 net_desc_t *nd; 1318 net_stat_t *ns; 1319 int count; 1320 dladm_usage_t usage; 1321 dladm_status_t status; 1322 1323 /* Parse the log file */ 1324 net_table = parse_logfile(logfile, logtype, &status); 1325 if (net_table == NULL) 1326 return (status); 1327 1328 if (net_table->net_entries == 0) 1329 return (DLADM_STATUS_OK); 1330 1331 ne = net_table->net_table_head; 1332 for (count = 0; count < net_table->net_entries; count++) { 1333 ns = ne->net_entry_tstats; 1334 nd = ne->net_entry_desc; 1335 1336 if (ns->net_stat_ibytes + ns->net_stat_obytes == 0) { 1337 ne = ne->net_entry_next; 1338 continue; 1339 } 1340 bcopy(&nd->net_desc_name, &usage.du_name, 1341 sizeof (usage.du_name)); 1342 usage.du_duration = ne->net_entry_ttime; 1343 usage.du_ipackets = ns->net_stat_ipackets; 1344 usage.du_rbytes = ns->net_stat_ibytes; 1345 usage.du_opackets = ns->net_stat_opackets; 1346 usage.du_obytes = ns->net_stat_obytes; 1347 usage.du_bandwidth = 1348 (ns->net_stat_ibytes + ns->net_stat_obytes) * 8 / 1349 usage.du_duration; 1350 usage.du_last = (count == net_table->net_entries-1); 1351 fn(&usage, arg); 1352 1353 ne = ne->net_entry_next; 1354 } 1355 1356 free_logtable(net_table); 1357 return (DLADM_STATUS_OK); 1358 } 1359 1360 /* 1361 * Walk the ctime list and display the dates of the records. 1362 */ 1363 dladm_status_t 1364 dladm_usage_dates(int (*fn)(dladm_usage_t *, void *), int logtype, 1365 char *logfile, char *resource, void *arg) 1366 { 1367 net_table_t *net_table; 1368 net_time_entry_t *start; 1369 net_stat_t *nns; 1370 net_time_t st; 1371 net_time_t *lasttime = NULL; 1372 uint64_t last_time; 1373 boolean_t gotstart = B_FALSE; 1374 dladm_status_t status; 1375 dladm_usage_t usage; 1376 1377 /* Parse the log file */ 1378 net_table = parse_logfile(logfile, logtype, &status); 1379 if (net_table == NULL) 1380 return (status); 1381 1382 if (net_table->net_entries == 0) 1383 return (DLADM_STATUS_OK); 1384 1385 start = net_table->net_ctime_head; 1386 1387 while (start != NULL) { 1388 nns = start->my_time_stat; 1389 1390 /* get to the resource we are interested in */ 1391 if (resource != NULL) { 1392 if (strcmp(resource, nns->net_stat_name) != 0) { 1393 start = start->net_time_entry_next; 1394 continue; 1395 } 1396 } 1397 1398 /* get the starting point in the logfile */ 1399 if (!gotstart) { 1400 get_starting_point(start, &start, &st, NULL, 1401 &last_time); 1402 if (start == NULL) 1403 break; 1404 nns = start->my_time_stat; 1405 gotstart = B_TRUE; 1406 } 1407 1408 if (lasttime == NULL || 1409 compare_date(&nns->net_stat_time, lasttime) == 1410 NET_DATE_GREATER) { 1411 bzero(&usage, sizeof (dladm_usage_t)); 1412 (void) strlcpy(usage.du_name, nns->net_stat_name, 1413 sizeof (usage.du_name)); 1414 bcopy(&nns->net_stat_ctime, &usage.du_stime, 1415 sizeof (usage.du_stime)); 1416 fn(&usage, arg); 1417 lasttime = &nns->net_stat_time; 1418 } 1419 1420 start = start->net_time_entry_next; 1421 continue; 1422 } 1423 1424 free_logtable(net_table); 1425 return (status); 1426 } 1427